openSUSE:打包 init 脚本

跳转到:导航搜索
Icon-warning.png
警告:SysV 样式的 Init 脚本在当前维护的 openSUSE 版本中要么已被弃用,要么不受支持。本文档保留作为参考文档,供旧版本使用。

本文档描述了 SysV 样式的 Init 脚本的指南,用于在 openSUSE 包中进行使用和包含。它们符合 LSB 标准。对于 systemd 单元文件,请参阅 openSUSE:Systemd_packaging_guidelines

名称

Init 脚本的名称必须符合 LSB 标准,因此必须在 http://www.lanana.org/lsbreg/init/init.txt 上列出。可以按照 http://www.lanana.org/lsbreg/instructions.html 中描述的方式注册新名称。

结构

Init 脚本的结构在 /etc/init.d/skeleton 中描述得很好。此文件也可以用作新 init 脚本的有效模板。

LSB 头部

Init 脚本是 shell 脚本,因此文件以通常的头部开始

#!/bin/sh

#!/bin/bash

然后通常会有注释。这些应该提及作者、版权或许可信息。该文件必须包含一个特殊的注释头部,提供有关 init 脚本的一些元信息。LSB 头部由注释限定,具体来说,头部开始标记为

### BEGIN INIT INFO

LSB 头部结束标记为

### END INIT INFO

所有 LSB 头部条目必须具有这些边界注释。

此示例取自 /etc/init.d/esound

 # 1995-2002, 2008 SUSE Linux Products GmbH, Nuernberg, Germany.
 # All rights reserved.
 #
 # Author: Stanislav Brabec, feedback to http://www.suse.de/feedback
 #
 ### BEGIN INIT INFO
 # Provides:          esound
 # Required-Start:    alsasound $remote_fs
 # Should-Start:      $network $portmap
 # Required-Stop:     alsasound $remote_fs
 # Should-Stop:       $network $portmap
 # Default-Start:     5
 # Default-Stop:
 # Short-Description: Sound daemon with network support
 # Description:       Starts esound server to allow remote access to sound
 #       card. To use esound locally, you do not need to start this
 #       server on boot. You should edit server settings before
 #       starting it via sysconfig editor: Network/Sound/Esound
 ### END INIT INFO

LSB 头部由以下部分组成

# Provides: 行

LSB 头部中的 # Provides: 行列出了此服务提供的任何启动设施。其他服务可以在其 # Required-Start:# Required-Stop: 行中引用这些启动设施。通常是守护进程的名称。如果多个包可以提供相同的设施(例如,sendmailpostfixdhcpcddhclient),则两个 init 脚本都应提供相同的设施名称。

# Provides: boot_facility_1 [boot_facility_2...]

当使用 start 参数运行 init 脚本时,由 Provides 关键字指定的启动设施应被视为存在,因此需要这些启动设施的 init 脚本应稍后启动。当使用 stop 参数运行 init 脚本时,由 Provides 关键字指定的启动设施应被视为不再存在。

# Required-Start: 行

LSB 头部中的 # Required-Start: 行列出了此服务启动期间必须可用的任何启动设施。

# Required-Start: boot_facility_1 [boot_facility_2...]

如果 init 脚本不需要在启动之前需要其他启动设施,则此行是必需的,即使为空也必须使用 Required-Start!

# Required-Stop: 行

LSB 头部中的 # Required-Stop: 行列出了在关闭此服务之前不应停止的任何启动设施。

# Required-Stop: boot_facility_1 [boot_facility_2...]

如果 init 脚本不需要在启动之前需要其他启动设施,则此行是必需的,即使为空也必须使用 Required-Start!

# Should-Start: 行

LSB 头部中的 # Should-Start: 行列出了任何设施,如果存在,则应在启动此服务期间可用。目的是允许“可选”依赖项,如果未提供设施,则不会导致服务失败。

# Should-Start: boot_facility_1 [boot_facility_2...]

此行是可选的,如果 init 脚本不需要在启动之前启动其他可选依赖项,则应省略它。

# Should-Stop: 行

LSB 头部中的 # Should-Stop: 行列出了任何设施,如果存在,则应仅在关闭此服务后停止。目的是允许“可选”依赖项,如果未提供设施,则不会导致服务失败。

# Should-Stop: boot_facility_1 [boot_facility_2...]

如果 init 脚本不需要在关闭后阻止其他可选依赖项停止,则应省略此行。


LSB 允许定义特定于发行版的此头部的扩展。此类关键字以 X-VendorTag- 为前缀。以下关键字在 SUSE Linux 上可用

# X-Start-Before: 行

LSB 头部中的 # X-Start-Before: 行表示使用此关键字的脚本应在指定的设施之前启动。

# X-Start-Before: boot_facility_1 [boot_facility_2...]

此行是可选的,并且当前特定于供应商(openSUSE)。

# X-Stop-After: 行

LSB 头部中的 # X-Stop-After: 行表示使用此关键字的脚本应在指定的设施之后停止。

# X-Stop-After: boot_facility_1 [boot_facility_2...]

此行是可选的,并且当前特定于供应商(openSUSE)。

# X-Start-Before:# X-Stop-After: 都允许脚本编写者在不修改其他例如系统 init 脚本的情况下使用依赖项。

# Default-Start: 行

LSB 头部中的 # Default-Start: 行列出服务将默认启用的运行级别。这些运行级别用空格分隔。

# Default-Start: run_level_1 [run_level_2...]

每个需要默认在任何运行级别启动的 SystemV 样式的 init 脚本都必须在 LSB 头部包含此行。只有真正需要用于关键系统的服务才应在此处定义运行级别。如果服务不在任何运行级别默认启动,则应省略此行。例如,如果服务仅在运行级别 3、4 和 5 默认启动,则 init 脚本中的 LSB 头部将指定

# Default-Start: 3 4 5

# Default-Stop: 行

LSB 头部中的 # Default-Stop: 行列出服务将不会默认启动的运行级别。这些运行级别用空格分隔,并且必须包含未在 # Default-Start: 行中使用的所有数字运行级别。

# Default-Stop: run_level_1 [run_level_2...]

每个需要默认在任何运行级别启动的 SystemV 样式的 init 脚本都必须在 LSB 头部包含此行(如果存在 # Default-Start: 行,则也应存在 # Default-Stop: 行)。

例如,如果服务仅在运行级别 3、4 和 5 默认启动,则 LSB 头部中的 # Default-Stop: 行必须指定运行级别 0、1、2 和 6

# Default-Stop: 0 1 2 6
请注意,openSUSE 中的 # Default-Stop: 被忽略,因为引导脚本概念使用 init.d(7) 手册页中描述的差异链接方案。

# Short-Description: 行

LSB 头部中的 # Short-Description: 行提供 init 脚本操作的简要摘要。这不能超过单行 80 个字符的文本。

# Short-Description: This service is a mail server.

所有 openSUSE SysV 样式的 init 脚本必须在 LSB 头部包含 # Short-Description: 行。可以将其大致等同于 RPM 规范文件中的 Summary: 字段。它显示在 YaST 运行级别编辑器中,例如。

# Description: 行

LSB 头部中的 # Description: 行提供 init 脚本操作的更完整描述。它可以跨越多行,其中每行延续必须以 '#' 后跟制表符字符或 '#' 后跟至少两个空格字符开头。多行描述在不符合此标准的第一个行处终止。此信息也显示在 YaST 运行级别编辑器中。

示例

# Description: Bluetooth services for service discovery, authentication,
#              Human Interface Devices, etc.

所有 openSUSE SysV 样式的 init 脚本必须在 LSB 头部包含 # Description: 行。可以将其大致等同于 RPM 规范文件中的 %description 部分。

设施

如 LSB 所要求,SUSE Linux init 脚本已经提供了一些默认的设施名称。这些在 /etc/insserv.conf 中定义。目前,提供了以下系统设施名称

  • $local_fs — 所有本地文件系统都已挂载。大多数服务应该需要此功能。
  • $remote_fs — 所有远程文件系统都已挂载。因为 /usr 可能是远程的,所以许多服务也应该需要此功能。
  • $syslog — 系统日志设施已启动。
  • $network — 低级网络(eth 卡等)已启动。
  • $named — 可用主机名解析。
  • $netdaemons — 所有网络守护进程正在运行。这在 LSB 1.2 中已被删除。目前为了向后兼容仍然可用。
  • $time — 系统时间已正确设置。
  • $portmap — SunRPC 端口映射服务可用。
  • $null — 在 Required-StopShould-Stop 的情况下强制执行空依赖项,否则 insserv 假定与 Start 情况相同的依赖项。


除了在 /etc/insserv.conf 上 openSUSE 的 LSB 兼容系统设施之外,还已知以下系统设施

  • $all此设施表示服务应插入所有服务的末尾。显然,使用此设施的所有服务将被分组到一种启动顺序中。
  • $null用于在 # Should-Stop:# Required-Stop: 的情况下强制执行空依赖项,否则 insserv(8) 假定与 # Should-Start: 处指定的依赖项相同

分别在 # Required-Start: 处。

其他(非系统)设施可以在 LSB 头部中的 # Provides: 行中定义。


操作

请参阅 /etc/init.d/skeleton,其中包含示例代码并包含一些有价值的注释。以下示例取自 /etc/init.d/cron

case "$1" in
    start)
        echo -n "Starting CRON daemon"
        startproc $CRON_BIN
        rc_status -v
        ;;
    stop)
        echo -n "Shutting down CRON daemon"
        killproc -TERM $CRON_BIN
        rc_status -v
        ;;
    try-restart)
        $0 status >/dev/null &&  $0 restart
        rc_status
        ;;
    restart)
        $0 stop
        $0 start
        rc_status
        ;;
    force-reload)
        echo -n "Reload service Cron"
        checkproc $CRON_BIN
        rc_status -v
        ;;
    reload)
        rc_status -v
        ;;
    status)
        echo -n "Checking for Cron: "
        checkproc $CRON_BIN
        rc_status -v
        ;;
    probe)
        ;;
    *)
        echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}"
        exit 1
        ;;
esac

如 LSB 所定义,所有 init 脚本都应知道如何处理以下参数

  • start — 启动服务。
  • stop — 停止服务。
  • restart — 如果服务正在运行,则停止并重新启动该服务。否则,启动服务。
  • reload — 在不实际停止和重新启动服务的情况下,导致重新加载服务的配置。
  • force-reload — 如果服务支持,则导致重新加载配置。否则,将重新启动该服务。
  • status — 打印服务的当前状态。
  • usage - 默认情况下,如果 init 脚本没有任何操作运行,则应列出“用法消息”,其中包含所有操作(用于使用)

start、stop、restart、force-reload 和状态操作必须被所有 init 脚本支持。reload 操作是可选的。

SUSE 定义了以下附加操作

  • try-restart — 仅当服务之前处于活动状态时才重新启动该服务。此操作现在是 LSB 的一部分(截至 1.9)。Red Hat 有一个类似的名为 condrestart 的操作。
  • probe — 探测是否需要重新加载。根据服务,如果需要重新加载,则打印 "reload" 或 "restart"。如果不需要重新加载,则不打印任何内容。此操作是可选的,并且不是 LSB 的一部分(截至 1.9)。

SystemV 样式的 init 脚本应支持 try-restart 和 condrestart 操作。这两个操作旨在服务于相同的目的,并且行为不得不同。事实上,强烈建议打包者将 try-restart 和 condrestart 作为 case 语句中的等效选项来实现

try-restart|condrestart)
    $0 status
    if test $? = 0; then
        $0 restart
    else
        rc_reset
    fi
    rc_status
    ;;

您不受禁止添加其他命令,但应将所有您打算交互使用过的命令列出到 usage 消息中。

退出状态码

参考:请参阅 LSB 规范。它定义了 init 脚本的以下退出状态码(状态操作除外,见下文)


退出状态码 描述
0 成功
1 通用或未指定的错误
2 无效或过多的参数
3 未实现的功能(例如,“reload”)
4 用户权限不足
5 程序未安装
6 程序未配置
7 程序未运行
8-99 保留供未来 LSB 使用
100-149 保留供发行版使用
150-199 保留供应用程序使用
200-254 保留

对于所有其他 init 脚本操作,如果操作成功,init 脚本必须返回零的退出状态。除了直接成功之外,还应考虑以下情况为成功

  • 使用 force-reload 参数重新启动服务
  • 对已经运行的服务运行 start
  • 对已经停止或未运行的服务运行 stop
  • 对已经停止或未运行的服务运行 restart
  • 对已经停止或未运行的服务运行 condrestart 或 try-restart

状态函数

LSB 定义了状态操作的以下退出代码

退出状态码 描述
0 程序正在运行或服务正常
1 程序已死并且 /var/run pid 文件存在
2 程序已死并且 /var/lock lock 文件存在
3 程序未运行
4 程序或服务状态未知
5-99 保留供未来 LSB 使用
100-149 保留供发行版使用
150-199 保留供应用程序使用
200-254 保留

/etc/rc.status 中定义的函数有助于记录、显示和返回 init 脚本中的实际 rc 状态信息。它们可以通过以下方式被 init 脚本引用

. /etc/rc.status

以下函数可用

rc_active

此函数检查服务是否已启用(通过符号链接)。如果服务在某个运行级别中已启用,则返回“0”,否则返回“1”。

rc_exit

此函数使用适当的 rc 状态终止 init 脚本。

rc_failed [num]

此函数将本地和全局 rc 状态设置为参数 num 定义的值。默认情况下,使用“1”作为值。

rc_check

此函数检查最后一个命令 ($?) 的退出状态,如果该值不同于“0”,则将本地 rc 状态设置为此值。然后,如果它与“0”不同,则将全局 rc 状态设置为本地 rc 状态的值。此函数由其他 rc 状态函数内部使用。

rc_reset

此函数将本地和全局 rc 状态都设置为“0”。

rc_status [-r] [-s] [-u] [-v[num]]

此函数检查、设置和显示 rc 状态。默认情况下,它是静默的:它仅调用 rc_check。因此,必须使用一个选项来显示状态来调用它。选项的含义如下

  • -r 调用 rc_reset。此选项可以与 -v 一起使用。命令 rc_status -v -r 检查、设置和显示当前的 rc 状态。然后调用 rc_reset
  • -s 显示“skipped”并将状态设置为“3”。这意味着未实现的功能。
  • -u 显示“unused”并将状态设置为“3”。这意味着未实现的功能。
  • -v[num] 显示实际状态并将本地状态重置为“0”。默认情况下,状态显示在实际行上。参数 num 定义了它应该在实际光标位置上方显示 num 行。


安装

init 脚本通常包含在软件包源代码中,以及一个额外的源文件。它安装在 %install 部分中。通常,init 脚本不应标记为 %config 文件。

虽然 init 文件位于 /etc 中,但它们是需要执行的脚本,而不是需要配置的脚本。任何配置都应该通过 /etc/sysconfig/ 提供,而不是在 init 脚本本身中。对这条规则的一个有效例外是现有的软件包,其中配置仍然通过 init 文件完成。在这种情况下,可以根据 Configuration files 部分的规则,将 init 文件标记为 %config 文件,以保留用户在升级时的配置,希望用户可以将配置迁移到新的 /etc/sysconfig/ 配置文件。

Init 脚本需要具有 0755 或 0700 权限。

也可能存在一个名为 rcname 的符号链接,指向 /etc/init.d/name。该符号链接位于 /sbin 或 /usr/sbin 中,具体取决于安装服务的目录前缀。它对于可以通过手动启动、停止、重新启动的 init 脚本很有用。

最后,在软件包安装后可以启用 init 脚本,并在软件包删除后禁用 init 脚本。在更新后应重新启动该服务,并在删除软件包之前应停止该服务。宏 %fillup_and_insserv、%insserv_force_if_yast、%restart_on_update、%insserv_cleanup 和 %stop_on_removal 旨在用于此目的。

请注意,SUSE Linux 提供了实用程序 insserv 来启用或禁用 init 脚本。有关更多详细信息,请参阅 insserv(8) 的手册页。

SUSE Linux 上的 init 脚本默认情况下是禁用的,除非那些对于最小系统功能是必要的。因此,%fillup_and_insserv 和 %insserv_force_if_yast 宏仅在提供此类基本服务的软件包中使用。

Spec 文件示例

...

Requires(pre): %insserv_prereq %fillup_prereq

...

%install

...

install -D -m 755 service.init %{buildroot}%{_initrddir}/service

...

mkdir -p %{buildroot}%{_sbindir}
ln -sf %{_initrddir}/service %{buildroot}%{_sbindir}/rcservice

...

%post
%fillup_and_insserv service

%preun
%stop_on_removal service

%postun
%restart_on_update service
%insserv_cleanup

...

%files
%defattr(-,root,root)
...
%{_initrddir}/service
%{_sbindir}/rcservice

...

如果 init 脚本需要启用、重新启动或停止,则必须将 init 脚本的名称列为相关宏的参数。唯一的区别是宏 %insserv_cleanup。它不需要任何参数,因为它无论如何都会删除所有悬挂的符号链接。

具有 SystemV 样式 init 脚本的软件包必须将它们放入 /etc/init.d 中。有一个 rpm 宏用于此目录,%_initrddir。

LSB 实际如何在 openSUSE 上工作

基于 LSB 的系统使用 /usr/lib/lsb/install_initd 进行脚本激活,并使用 /usr/lib/lsb/remove_initd 进行停用。当这些任务发生时,将读取 LSB 依赖项,然后调整脚本的启动和停止优先级以满足这些依赖项。

这意味着 LSB 标头依赖项受到尊重(尽管采用静态机制)。对于 LSB 标头中发现的覆盖依赖项,可以使用系统工具 insserv(8),有关如何执行此操作的更多说明,应阅读 insserv(8) 的手册页。


初始化环境变量

由于 init 脚本可能由系统管理员使用非标准环境变量值(例如 PATH、USER、LOGNAME 等)手动运行,因此 init 脚本不应依赖这些环境变量的值。如果需要它们,则应将它们设置为已知/默认值。


Init 脚本必须表现良好

SystemV 样式的 init 脚本必须在服务已经运行时启动或服务未运行时停止时表现良好。它们不得因为正常操作而杀死无关的(但可能名称相似的)用户进程。实现此目的的最佳方法是使用 /etc/rc.status 提供的 init 脚本函数

# Source function library.
. /etc/rc.status
rc_reset

以及系统工具 /sbin/start_daemon 或 /sbin/startproc 用于启动进程,/sbin/killproc 用于停止进程,以及 /sbin/checkproc 用于检查正在运行的进程。有关更多说明,应查阅 start_daemon(8) 或 startproc(8)、killproc(8) 和 checkproc(8) 的手册页。

startproc 命令启动由可执行文件路径名称标识的进程。退出状态是 LSB,请参阅 “Exit Status Codes” 以获取更多详细信息。它可以被 LSB 命令 start_daemon 替换,后者在启动可执行文件之前不会 fork。这要求新进程本身 fork 并断开与终端的连接(请参阅手册页 startproc(8)、daemon(3)、fork(2) 和 setsid(2))。LSB 命令 killproc 将信号发送到由可执行文件的完整路径名称标识的进程。(请参阅手册页 killproc(8))。LSB 命令 pidofproc 使用基本名称来查找进程,而 checkproc 命令使用可执行文件的完整路径来执行相同的操作(请参阅手册页 pidofproc(8))。

然后通常会检查服务是否正确安装以及相关的 sysconfig 文件是否被读取。如果出现问题,必须返回符合 LSB 的错误值。有关更多详细信息,请参阅 “Exit Status Codes”

此示例取自 /etc/init.d/ypbind

YPBIND_BIN=/usr/sbin/ypbind
 test -x $YPBIND_BIN || { echo "$YPBIND_BIN not installed";
         if [ "$1" = "stop" ]; then exit 0; else exit 5; fi; }
 
 YPBIND_CONFIG=/etc/sysconfig/ypbind
 test -r $YPBIND_CONFIG || { echo "$YPBIND_CONFIG not existing";
         if [ "$1" = "stop" ]; then exit 0; else exit 6; fi; }
 
 # Read config
 . $YPBIND_CONFIG


如果服务自动重新加载其配置(例如,cron),则 init 脚本的 reload 操作必须表现为配置已成功重新加载。restart、condrestart、try-restart、reload 和 force-reload 操作可能是原子的;也就是说,如果已知服务在重新启动或重新加载后未运行,则脚本可以在没有任何进一步操作的情况下返回错误。


SystemV init 脚本的系统工具

start_daemon(8) 和 startproc(8) 工具

  • start_daemon 如果尚未运行,则启动守护程序。
  • startproc 如果尚未运行,则将进程作为守护程序启动。
start_daemon [-fLve] [-n +/-<prio>] [-u user] [-g group] [-l log_file|-q|-d] [-p pid_file]
          [-i ignore_file][-c root]/path/to/executable [arguments for executable]


startproc [-fLves] [[-n ]+/-<prio>] [-(t|T) <sec>] [-u user] [-g group] [-l log_file|-q|-d] [-p pid_file]
          [-i ignore_file] [-c root] /path/to/executable [arguments for executable]

killproc(8) 工具

向程序发送信号;默认情况下,它发送 SIGTERM,如果进程未死,则几秒钟后发送 SIGKILL。它还会尝试删除找到的 pid 文件。

killproc [-vqLN] [-g|-G] [-p pid_file] [-i ingnore_file] [-c root] [-t <sec>] [-<SIG>] /full/path/to/executable

killproc -n [-vq] [-g|-G] [-t <sec>] [-<SIG>] name_of_kernel_thread

killproc    [-vq] [-g|-G] [-t <sec>] [-<SIG>] basename_of_executable

checkproc(8) 和 pidofproc(8) 工具

尝试查找程序的状态和 pid;检查可能的 pid 文件。主要是 pidofproc 是 checkproc 的详细版本。

checkproc [-vLkNz] [-p pid_file] [-i ingnore_file] [-c root] /full/path/to/executable

checkproc -n [-vk] name_of_kernel_thread

checkproc    [-vk] basename_of_executable

pidofproc [-LkNz] [-p pid_file] [-i ingnore_file] [-c root] /full/path/to/executable

pidofproc -n [-k] name_of_kernel_thread

pidofproc    [-k] basename_of_executable

LSB shell 函数

上述系统工具也将由位于 /lib/lsb/init-functions 中的 shell 函数提供

# Load the LSB shell functions
. /lib/lsb/init-functions

以及 LSB 3.1 及更高版本指定的语法

start_daemon [-f] [-n +/-<prio>] /path/to/executable [arguments for executable]
killproc [-p pid_file] /full/path/to/executable [-<SIG>]
pidofproc [-p pid_file] /full/path/to/executable