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

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

#!/bin/sh

. /etc/rc.subr

name="mumbled"
rcvar=`set_rcvar`

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

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

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

sig_reload="USR1"(4)

start_precmd="${name}_prestart"(5)
stop_postcmd="echo Bye-bye"(6)

extra_commands="reload plugh xyzzy"(7)

plugh_cmd="mumbled_plugh"(8)
xyzzy_cmd="echo 'Nothing happens.'"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

下面是 checkyesno 的合理使用范围:

if checkyesno mumbled_enable; then
        foo
fi

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

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

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

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

如果对于FreeBSD有问题,请先阅读文档,如不能解决再联系<[email protected]>.
关于本文档的问题请发信联系 <[email protected]>.