6.26.启动和停止服务 (rc 脚本)

rc.d 脚本被用于在系统启动时启动服务,同时给予管理员一种标准的方式来停止,启动和重启服务。 port 集成到系统 rc.d 框架中。其使用细节可以在 Handbook 中 rc.d 章节中找到。可用命令的详细解释在 rc(8)rc.subr(8) 中提供。最后,这里有一篇关于 rc.d 脚本实际方面的文章。

假设有一个虚拟 port 名叫 doorman ,需要启动一个 doorman 守护进程。在 Makefile 文件中添加:

USE_RC_SUBR= doormand

可以列出多个脚本并进行安装。这些脚本必须位于 files 子目录中并且必须在文件名后添加 .in 后缀。标准 SUB_LIST 文件扩展名会与这文件发生冲突。强烈鼓励使用 %%PREFIX%%%%LOCALBASE%% 文件扩展名。更多关于 SUB_LIST 的信息可以在相关章节找到。

一个简单的 rc.d 脚本来启动 doormand 守护进程的例子:

#!/bin/sh

# PROVIDE: doormand
# REQUIRE: LOGIN
# KEYWORD: shutdown
#
# Add these lines to /etc/rc.conf.local or /etc/rc.conf
# to enable this service:
#
# doormand_enable (bool):    Set to NO by default.
#                Set it to YES to enable doormand.
# doormand_config (path):    Set to %%PREFIX%%/etc/doormand/doormand.cf
#                by default.

. /etc/rc.subr

name=doormand
rcvar=doormand_enable

load_rc_config $name

: ${doormand_enable:="NO"}
: ${doormand_config="%%PREFIX%%/etc/doormand/doormand.cf"}

command=%%PREFIX%%/sbin/${name}
pidfile=/var/run/${name}.pid

command_args="-p $pidfile -f $doormand_config"

run_rc_command "$1"

除非有非常好的理由需要更早地启动服务,或者以特定用户(不是 root )运行,否则所有 port 脚本都必须使用以下语句:

REQUIRE: LOGIN

如果启动脚本启动了必须关闭的守护进程,则以下语句将在系统关闭时触发服务的停止:

KEYWORD: shutdown

如果脚本不启动持久服务,则不需要这样做。

对于可选配置元素,这里更推荐使用“=”风格的默认变量赋值,而不是:=风格,因为前者仅在变量未设置时设置默认值,而后者则在变量未设置或为空时设置默认值。用户可能会添加类似以下内容的自定义变量:

doormand_flags=""

在他们的 rc.conf.local ,使用":="风格的默认变量赋值会不恰当地覆盖用户的意图。 _enable 变量是必须的而且必须使用":"来设置默认值。

重要

port 在安装和卸载时不能启动和停止其服务。请勿滥用 plist 关键字(在@preexec 命令,@postexec 命令,@preunexec 命令,@postunexec 命令部分中描述的) ,通过运行修改当前正在运行的系统的命令,包括启动或停止服务。

6.26.1 提交前的清单

在贡献一个带有 rc.d 脚本的 port 前,更重要的是,在提交之前,确认以下的清单是否准备完毕。

devel/rclint port 程序可以检查其中大部分内容,但它并不是合格检查的替代品。

  1. 如果这是一个新文件,它是否以 .sh 为文件扩展名?如果是,必须更改为 .in ,因为 rc.d 文件不能以那个文件扩展名结尾。

  2. 文件的名称(去掉 .in 后缀), PROVIDE 行和$name 是否全部匹配?文件名和 PROVIDE 一致可以更容易地进行调试,特别是针对 rcorder(8) 问题。文件名和$name 匹配可以更容易确定 rc.conf[.local] 中哪些变量是相关的。这也是所有新脚本的政策,包括基本系统中的脚本。

  3. REQUIRE 行是否设为 LOGIN?对于以非 root 用户身份运行的脚本,这是必需的。如果它作为 root 运行,是否有充分的理由使其在 LOGIN 之前运行?如果没有,则必须在 LOGIN 之后运行,以便于基本系统大部分东西已经运行之后本地脚本可以松散地分组到 rcorder(8) 中。

  4. 脚本是否开启一个持久性服务?如果是的话,那它必须有 KEYWORD: shutdown

  5. 确保没有出现 KEYWORD: FreeBSD 。这已经多年来不是必需的,也不是期望的。这也表明新脚本是从旧脚本中复制/粘贴而来,因此必须更加谨慎地进行审核。

  6. 如果脚本使用了解释性语言例如 perlpythonruby ,确保 command_interpreter 设置正确,例如对于 Perl 来说,在 SUB_LIST 中添加 PERL=${PERL} 并且使用 %%PERL%% 。否则的话,service name stop 命令可能不起作用,查看 service(8) 获取更多信息。

  7. 所有出现 /usr/local 的地方是否已被替换为 %%PREFIX%%

  8. 默认变量赋值是否在 load_rc_config 之后进行?

  9. 是否有对空字符串进行的默认赋值?它们应该被移除,但请仔细检查选项是否在文件顶部的注释中有文档说明。

  10. 在脚本中是否实际使用了设置在变量中的内容?

  11. 在默认名称 _flags 中列出的选项是否实际上是必须的?如果是,则它们必须在 command_args 中。这里要注意 -d,因为它通常是将进程“daemonize”的参数,因此实际上是必需的。

  12. _name__flags 必须永远不会包括在 command_args 中(反之亦然,虽然这种错误很少见)。

  13. 脚本是否无条件地执行任何代码?这是不被推荐的做法。通常必须通过 start_precmd 来处理这些事情。

  14. 所有布尔测试都必须使用 checkyesno 函数。不允许手动编写测试 [Yy][Ee][Ss] 等。

  15. 如果有一个循环(例如,等待某些东西启动),它是否有一个计数器来终止循环?如果有错误,我们不希望启动进程一直被卡住。

  16. 脚本是否创建需要特定权限的文件或目录(例如,需要由运行进程的用户拥有的 pid )?考虑使用具有适当命令行参数的 install(1) 一步完成整个过程,而不是传统的touch(1)/chown(8)/chmod(1) 例程。

最后更新于

FreeBSD 中文社区