BSD rc.d脚本编程实战
上一页 |  | 下一页

# 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) 可通过选项而被指定来包括或省去由特殊关键字所列出的文件。）

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

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

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

    #!/bin/sh

    # PROVIDE: mumbled oldmumble
    # REQUIRE: DAEMON cleanvar frotz
    # BEFORE:  LOGIN
    # KEYWORD: nojail shutdown

    . /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
    	fi
    	return 0
    }

    load_rc_config $name
    run_rc_command "$1"

跟前面一样，做如下详细分析：

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

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

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

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

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

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

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

  如我们从上述文字所记起的，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) 中所描述的其本身关闭顺序的最终阶段之前停止该脚本， 可能是个不错的主意。特别是， 你显然是应该关闭一个需要相当长时间， 或需要特定的动作才能干净地关闭的服务。 数据库引擎就是这样一个典型的例子。

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

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

上一页 | 起始页 | 下一页
启动并停止高级守护进程 |  | 给予 rc.d 脚本更多的灵活性

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

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