7 链接脚本到 rc.d 框架

  当编写好了一个脚本,它需要被整合到 rc.d 中去。 一个重要的步骤就是安装脚本到 /etc/rc.d (对基本系统而言)或 /usr/local/etc/rc.d (对ports而言)中去。在 <bsd.prog.mk> 和 <bsd.port.mk> 中都为此提供了方便的接口, 通常你不必担心适当的所有权限和模式。系统脚本应当是通过可以在 src/etc/rc.d 找到的 Makefile 安装的。Port 脚本可以像 Porter's Handbook 中描述那样通过使用 USE_RC_SUBR 来被安装。

  不过,我们应该预先考虑到我们脚本在系统启动顺序中的位置。 我们的脚本所处理的服务可能依赖于其它的服务。举个例子, 没有网络接口和路由选择的启用运行的话,一个网络守护进程是不起作用的。 即使一个服务看似什么都不需要,在基本文件系统检查挂载完毕之前也很难启动。

  之前我们曾提到过 rcorder(8)。现在是时候来密切地关注下它了。 笼统地说,rcorder(8) 处理一组文件,检验它们的内容, 并从文件集合打印一个文件列表的依赖顺序到 stdout 标准输出。这点是用于保持文件内部的依赖信息, 而每个文件只能说明自己的依赖。一个文件可以指定如下信息:

  并不奇怪的是,rcorder(8) 只能处理接近 sh(1) 语法的文本文件。rcorder(8) 所解读的特殊行看起来类似 sh(1) 的注释。这种特殊文本行的语法相当严格地简化了其处理。 请查阅 rcorder(8) 以获取更详细的信息。

  除使用 rcorder(8) 的特殊行以外, 脚本可以坚持将其依赖的其它服务强制性启动。当其它服务是可选的, 并因系统管理员错误地在 rc.conf(5) 中禁用掉该服务而使其不能自行启动时,会需要这一点。

  将这些谨记在心,我们来考虑下简单结合了依赖信息增强的守护进程脚本:

#!/bin/sh

# PROVIDE: mumbled oldmumble (1)
# REQUIRE: DAEMON cleanvar frotz(2)
# BEFORE:  LOGIN(3)
# KEYWORD: nojail shutdown(4)

. /etc/rc.subr

name=mumbled
rcvar=mumbled_enable

command="/usr/sbin/${name}"
start_precmd="${name}_prestart"

mumbled_prestart()
{
	if ! checkyesno frotz_enable && \
	    ! /etc/rc.d/frotz forcestatus 1>/dev/null 2>&1; then
		force_depend frotz || return 1(5)
	fi
	return 0
}

load_rc_config $name
run_rc_command "$1"

  跟前面一样,做如下详细分析:

(1)
该行声明了我们脚本所提供的 “条件” 的名字。 现在其它脚本可以用那些名字来标明我们脚本的依赖。

注意: 通常脚本指定一个单独的已提供的条件。然而, 并没有什么妨碍我们从列出的那些条件中指定,例如, 为了兼容性的目的。

在其它情况,主要的名称,或者说唯一的, PROVIDE: 条件应该与 ${name} 相同。

(2)(3)
因此我们的脚本指示了其依赖于别的脚本所提供的 “条件”。根据这些行的信息,脚本请示 rcorder(8) 以将其放在一个或多个提供 DAEMONcleanvar 的脚本后面,但在提供 LOGIN 的脚本前面。

注意: BEFORE: 这一行不可以在其它脚本不完整的依赖关系列表中滥用。 适合使用 BEFORE: 的情况是当其它脚本不关心我们的脚本, 但是我们的脚本如果在另一个之前运行的话能够更好地执行任务。 一个典型的实例是网络接口和防火墙: 虽然接口不依赖防火墙来完成自己的工作, 但是系统安全将因一切网络流量之前启动的防火墙而受益。

除了条件相对应的每个单独服务,脚本使用元条件和它们的 “占位符” 来保证某个操作组在其它之前被执行。 这些是由 UPPERCASE 大写名字所表示的。它们的列表和用法可以在 rc(8) 中找到。

切记将一个服务名称放进 REQUIRE: 行不能保证实际的服务会在我们的脚本启动的时候运行。 所需求的服务可能会启动失败或在 rc.conf(5) 中被禁掉了。 显然,rcorder(8) 是无法追踪这些细节的,并且 rc(8) 也不会去追踪。所以, 脚本启动的应用程序应当能够应付任何所需求的服务的不可用情况。 某些情况下,我们可以用 下面 所讨论的方式来协助脚本。

(4)
如我们从上述文字所记起的,rcorder(8) 关键字可以用来选择或省略某些脚本。即任何 rcorder(8) 用户可以通过指定 -k-s 选项来分别指定 “保留清单(keep list)” 和 “跳过清单(skip list)”。 从全部文件到按依赖关系排列的清单,rcorder(8) 将只是挑出保留清单(除非是空的) 中那些带关键字的以及从跳过清单中挑出不带关键字的文件。

在 FreeBSD 中,rcorder(8)/etc/rc/etc/rc.shutdown 所使用。 这两个脚本定义了 FreeBSD 中 rc.d 关键字以及它们的意义的标准列表如下:

nojail

该服务不适用于 jail(8) 环境。 如果是在 jail 的内部的话,自动启动和关闭程序将忽略该脚本。

nostart

该服务只能手动启动否则将不会启动。 自动启动程序将忽略此脚本。结合 shutdown 关键字的话,这可以用来编写只在系统关闭时执行一些任务的脚本。

shutdown

这个关键字 明确 地列出了需要在系统关闭前停止的服务。

注意: 当系统即将关闭的时候, /etc/rc.shutdown 在运行。 它假定认为大部分的 rc.d 脚本在那刻什么都不做。因此, /etc/rc.shutdown 选择性地调用带有 shutdown 关键字的 rc.d 脚本, 有效地忽略其余的脚本。为了更快的关闭, /etc/rc.shutdown 传递 faststop 命令给其运行的脚本, 以跳过预置的检查,例如,进程文件 pidfile 的检查。 正如依赖性服务应该在其所依赖的服务之前停止, /etc/rc.shutdown 以相反的依赖次序来运行这些脚本。

如果写一个真正的 rc.d 脚本的话, 你应当考虑到其是否与系统关闭时有关系。例如, 如果你的脚本只通过响应 start 命令来运行任务,那么你不需要包含这个关键字。然而, 如果你的脚本管理着一个服务,那么,在系统进入 halt(8) 中所描述的其本身关闭顺序的最终阶段之前停止该脚本, 可能是个不错的主意。特别是, 你显然是应该关闭一个需要相当长时间, 或需要特定的动作才能干净地关闭的服务。 数据库引擎就是这样一个典型的例子。

(5)
force_depend 起始的行应被用于更谨慎的情况。通常,用于修正相互关联的 rc.d 脚本分层结构的配置文件时会更加稳妥。

如果你仍不能完成不含 force_depend 的脚本, 范例提供了一个如何有条件地调用它的习惯用法。在范例中,我们的 mumbled 守护进程需求另一个以高级方式启动的进程, frotz。但 frotz 也是可选的; 而且 rcorder(8) 对这些信息是一无所知的。幸运的是, 我们的脚本已访问到全部的 rc.conf(5) 变量。如果 frotz_enable 为真,我们希望的最好结果是依靠 rc.d 已经启动了 frotz。 否则我们强制检查 frotz 的状态。最终, 如果 frotz 依赖的服务没有找到或运行的话, 我们将强制其运行。这时 force_depend 将发出一条警告信息,因为它只应该在检查到配置信息丢失的情况下被调用。

本文档和其它文档可从这里下载:ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读文档,如不能解决再联系<questions@FreeBSD.org>.
关于本文档的问题请发信联系 <doc@FreeBSD.org>.