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

# 6 启动并停止高级守护进程

我们来给之前的 “骨架” 脚本加点 “血肉”，并让它更复杂更富有特性吧。 默认的方法已能够为我们做很好的工作了， 但是我们可能会需要它们一些方面的调整。 现在我们将学习如何调整默认方法来符合我们的需要。

    #!/bin/sh

    . /etc/rc.subr

    name="mumbled"
    rcvar=`set_rcvar`

    command="/usr/sbin/${name}"
    command_args="mock arguments > /dev/null 2>&1"

    pidfile="/var/run/${name}.pid"

    required_files="/etc/${name}.conf /usr/share/misc/${name}.rules"

    sig_reload="USR1"

    start_precmd="${name}_prestart"
    stop_postcmd="echo Bye-bye"

    extra_commands="reload plugh xyzzy"

    plugh_cmd="mumbled_plugh"
    xyzzy_cmd="echo 'Nothing happens.'"

    mumbled_prestart()
    {
        if checkyesno mumbled_smart; then
            rc_flags="-o smart ${rc_flags}"
        fi
        case "$mumbled_mode" in
        foo)
            rc_flags="-frotz ${rc_flags}"
            ;;
        bar)
            rc_flags="-baz ${rc_flags}"
            ;;
        *)
            warn "Invalid value for mumbled_mode"
            return 1
            ;;
        esac
        run_rc_command xyzzy
        return 0
    }

    mumbled_plugh()
    {
        echo 'A hollow voice says "plugh".'
    }

    load_rc_config $name
    run_rc_command "$1"

  附加给 `$command` 的参数在 `command_args` 中进行传递。它们在 `$mumbled_flags` 之后将被添加到命令行。 其实际的执行便是此后最终的命令行传递给 `eval` 运算，输入和输出以及重定向都可以在 `command_args` 中指定。

  > 注意: 永远不要 在 `command_args` 包含破折号选项， 类似 `-X` 或 `--foo` 这样的。`command_args` 的内容将出现在最终命令行的末尾，因此它们可能是紧接在 `${name}_flags` 中所列出的参数后面； 但大多的命令将不能识别出普通参数后的破折号选项。 更好的传递附加给 `$command` 的选项的方式是添加它们到 `${name}_flags` 的起始处。另一种方法是像后文所示的那样来修改 `rc_flags`。

  一个得体的守护进程会创建一个 pidfile 进程文件， 以使其进程能够更容易更可靠地被找到。如果设置了 `pidfile` 变量，告诉 rc.subr(8) 哪里能找到供其默认方法所使用的 `pidfile` 进程文件。

  > 注意: 事实上，rc.subr(8) 在启动一个守护进程前还会使用 pidfile 进程文件来查看它是否已经在运行。使用了 `faststart` 参数可以跳过这个检查步骤。

  如果守护进程只有在确定的文件存在的情况下才可以运行， 那就将它们列到 `required_files` 中，而 rc.subr(8) 将在启动守护进程之前检查那些文件是否存在。 还有相关的分别用来检查目录和环境变量的 `required_dirs` 和 `required_vars` 可供使用。它们都在 rc.subr(8) 中有详细的说明。

  > 注意: 来自 rc.subr(8) 的默认方法，通过使用 `forcestart` 作为脚本的参数， 可以强制性地跳过预先需要的检查。

  我们可以在守护进程有异常的时候，自定义发送给守护进程的信号。 特别是，`sig_reload` 指定了使守护进程重新装载其配置的信号；默认情况也就是 `SIGHUP` 信号。 另一个信号是发送给守护进程以停止该进程；默认情况下是 `SIGTERM` 信号，但这是可以通过设置 `sig_stop` 来进行适当更改的。

  > 注意: 信号名称应当以不包含 `SIG` 前缀的形式指定给 rc.subr(8)，就如范例中所示的那样。 FreeBSD 版本的 kill(1) 程序能够识别出 `SIG` 前缀，不过其它系统版本的就不一定了。

  在默认的方法前面或后面执行附加任务是很容易的。 对于我们脚本所支持的每条命令参数而言，我们可以定义 ``argument`_precmd` 和 ``argument`_postcmd` 来完成。这些 sh(1) 命令分别在它们各自的方法前后被调用， 显然，从它们各自的名字就能看出来。

  > 注意: 如果我们需要的话，用自定义的 ``argument`_cmd` 改写默认的方法，并不妨碍我们仍然使用 ``argument`_precmd` 和 ``argument`_postcmd`。 特别是，前者便于检查自定义的方法， 以及执行自身命令之前所遇到更严密的条件。于是，将 ``argument`_precmd` 和 ``argument`_cmd` 一起使用，使我们合理地将检查从动作中独立了出来。

  > 别忘了你可以将任意的有效的 sh(1) 表达式插入到方法和你定义的 pre- 与 post-commands 命令中。 在大部分情况下，调用函数使实际任务有好的风格， 但千万不要让风格限制了你对其幕后到底是怎么回事的思考。

  如果我们愿意实现一些自定义参数， 这些参数也可被认作为我们脚本的 命令，我们需要在 `extra_commands` 中将它们列出并提供方法以处理它们。

  > 注意: `reload` 是个特别的命令。一方面， 它有一个在 rc.subr(8) 中预置的方法。另一方面， `reload` 命令默认是不被提供的。 理由是并非所有的守护进程都使用同样的重载方法， 并且有些守护进程根本没有任何东西可重载的。所以显而易见， 我们需要去询问都提供了哪些的内建功能。我们可以通过 `extra_commands` 来这样做。

  > 我们从 `reload` 的默认方法得到了什么呢？ 守护进程常常在收到一个信号后重新载入它们的配置 ── 一般来说，也就是 `SIGHUP` 信号。因此 rc.subr(8) 尝试发送一个信号给守护进程来重载它。 该信号一般预设为 `SIGHUP`， 但是如果必要的话可以通过 `sig_reload` 变量来自定义它。

  我们的脚本提供了两个非标准的命令， `plugh` 和 `xyzzy`。 我们看到它们在 `extra_commands` 中被列出来了， 并且现在是时候给它们提供方法了。`xyzzy` 的方法是内联的而 `plugh` 的是以 `mumbled_plugh` 形式完成的函数。

  非标准命令在启动或停止的时候不被调用。 通常它们是为了系统管理员的方便。它们还能被其它的子系统所使用， 例如，devd(8)，前提是 devd.conf(5) 中已经指定了。

  全部可用命令的列表，当脚本不加参数地调用时，在 rc.subr(8) 打印出的使用方法中能够找到。例如， 这就是供学习的脚本用法的内容：

      # /etc/rc.d/mumbled
      Usage: /etc/rc.d/mumbled [fast|force|one](start|stop|restart|rcvar|reload|plugh|xyzzy|status|poll)

  如果脚本需要的话，它可以调用自己的标准或非标准的命令。 这可能看起来有点像函数的调用，但我们知道，命令和 shell 函数并非一直都是同样的东西。举个例子，`xyzzy` 在这里不是以函数来实现的。另外，还有应该被有序调用的 pre-command 预置命令和 post-command 后置命令。 所以脚本运行自己命令的合适方式就是利用 rc.subr(8)， 就像范例中展示的那样。
  rc.subr(8) 提供了一个方便的函数叫做 `checkyesno`。 它以一个变量名作为参数并返回一个为零的退出值， 当且仅当该变量设置为 `YES`，或 `TRUE`，或 `ON`，或 `1`，区分大小写；否则返回一个非零的退出值。 在第二种情况中，函数测试变量的设置为 `NO`， `FALSE`，`OFF`，或 `0`，区分大小写； 如果变量包含别的内容的话它打印一条警告信息，例如，垃圾。

  切记对 sh(1) 而言零值意味着真而非零值意味着假。

  > 重要: `checkyesno` 函数使用一个 变量名。不要扩大含义将变量的 值 传递给它； 否则它不会如你预期那样的工作。

  > 下面是 `checkyesno` 的合理使用范围：

  >     if checkyesno mumbled_enable; then
  >             foo
  >     fi

  > 相反地，以下面的方式调用 `checkyesno` 是不会工作的 -- 至少是不会如你预期的那样：

  >     if checkyesno "${mumbled_enable}"; then
  >             foo
  >     fi

  我们可以通过修改 `$start_precmd` 中的 `rc_flags` 来影响传递到 `$command` 的标帜。
  某种情况下我们可能需要发出一条重要的信息，那样的话 syslog 可以很好地记录日志。 这可以使用下列 rc.subr(8) 函数来轻松完成： `debug`，`info`， `warn`，以及 `err`。 后者以指定的代码值退出脚本。
  方法的退出值和它们的 pre-commands 预命令不只是默认被忽略掉。如果 ``argument`_precmd` 返回了一个非零退出值，主方法将不会被执行。依次地是， ``argument`_postcmd` 将不会被调用，除非主方法返回的是一个为零的退出值。

  > 注意: 然而，当给一个参数使用 `force` 前缀的时候，如 `forcestart`，rc.subr(8) 会听从命令行指示而忽略那些退出值最后仍然调用所有的命令。

上一页 | 起始页 | 下一页
启动并停止简单守护进程 |  | 链接脚本到 rc.d 框架

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

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