debian6启动脚本详解

我们都知道,在Debian 5下面设置系统启动项使用的是 update-rc.d 命令,如:

/usr/sbin/update-rc.d xxx defaults 98

可以参考:http://wangyan.org/blog/ubuntu-update-rc-d.html


但是在 Debian 6 下面,已经没有 update-rc.d 命令了,取而代之的是:insserv 。

下面简单介绍一下它的用法:(下班了,赶紧拷贝一份过来吧)

转自:https://zh.opensuse.org/index.php?title=openSUSE:Packaging_init_scripts&variant=zh

命名

启动脚本的命名必须与 LSB 兼容,它必须被列示在 http://www.lanana.org/lsbreg/init/init.txt。http://www.lanana.org/lsbreg/instructions.html 描述了如何注册新名称。

SuSE 除非是基础核心包,用户软件没必要去注册这种名称,一般命名就是包名,守护程序是包名 + 字母d。

结构

/etc/init.d/skeleton 文件是一个空白的启动脚本,体现了启动脚本的结构。该文件也可以作为启动脚本的模板。

强烈推荐打开这份文件再继续阅读下面内容,否则就会像听天书一样。

LSB 头部

启动脚本本身也是终端脚本,因为文件的开头和其他终端脚本的常规开头一样:

#!/bin/sh

或者

#!/bin/bash

接着通常会是注释。注释应该说明作者、版权或授权方式的信息。启动脚本还必须包含一部分特殊的注释头部来提供关于启动脚本本身的元数据信息。

LSB 头部是由注释作为上下文区别开的边界,该头部的上界是:

### BEGIN INIT INFO

下界是:

### 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: 行中引入我们提供的这些引导设施。 引导设施通常是守护程序的名字。如果多个软件包提供了相同的引导设施(例如 sendmail vs. postfixdhcpcd vs. dhclient),两者的启动脚本应该提供相同的引导设施名。

# Provides: boot_facility_1 [boot_facility_2...]

当启动脚本带着一个 start 开始参数运行时,即 /etc/init.d/NetworkManager start 这样,Provides 关键词指定的引导设施(们)就被视为可用的,因此其他要求这些引导设施的启动脚本就可以随后启动。当启动脚本带着一个 stop 停止参数运行时,即 /etc/init.d/NetworkManager stop 这样,Provides 关键词指定的引导设施就被视为不再可用。

# Required-Start: 行

LSB 头部的 # Required-Start: 行列出了任何启动此脚本服务必须的引导设施。

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

本行是必须的,即使你的启动脚本不需要任何其他启动脚本提供的引导设施,你也必须有这行,哪怕它是空的!

# Required-Stop: 行

LSB 头部的 # Required-Stop: 行列出了所有在停止本脚本服务之前必须不能先行停止的引导设施。

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

本行是可选的,如果一个启动脚本不需要任何在它停止前不能被停止的引导设施,那么就不需要该行。

# Should-Start: 行

LSB 头部的 # Should-Start: 行列出了任何一旦可用就应该在启动本脚本服务前被启动的引导设施。目的是允许额外的即使不可用也不会造成本脚本服务失败的依赖。

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

本行为可选,如果一个启动脚本不需要启动任何可选依赖,那么可以没有本行。

# Should-Stop: 行

LSB 头部的 # Should-Stop: 行列出了所有一旦可用就不能在停止本脚本服务前停止的引导设施。目的是允许额外的即使不可用也不会造成本脚本服务失败的依赖。

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

本行为可选,如果一个启动脚本不需要任何在它停止前不能停止的额外依赖,本行可以没有。

LSB 允许为本头部定义发行版专有的插件。这样的插件应该以 X-出品商标签- 开头,比如 X-SuSE-。以下是 openSUSE 发行版专有的插件:

# X-Start-Before: 行

LSB 头部的 # X-Start-Before: 行的意思是说本行中所提到的脚本应该在本脚本启动前被启动。

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

本行是可选的,目前是 openSUSE 发行版专有的。

# X-Stop-After: line

LSB 头部的 # X-Stop-After: 行的意思是说这里列出的脚本应该在本脚本服务停止后也被停止。

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

本行是可选的,目前也是 openSUSE 发行版专有的。

# X-Start-Before: 和  # X-Stop-After: 都允许脚本作者在新启动脚本中直接使用依赖而不去改动其他的比如系统启动脚本的内容。


# Default-Start: line

LSB 头部的 # Default-Start: 行列出了一些运行级别,在这些运行级别下面,本脚本服务应该默认启用。运行级别间以空格分隔。

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

每个在任何运行级别需要默认启动的 SystemV 风格的启动脚本都必须在 LSB 头部中有本行。只有 系统真正需要的脚本服务才可以定义运行级别。如果脚本服务不在任何运行级别下默认启用,那么就不需要提供本行。 例如,如果一个服务在只在运行级别 3,4,和 5 级时默认启动,启动脚本的 LSB 头部就应该指定:

# Default-Start: 3 4 5

# Default-Stop: 行

LSB 头部的 # Default-Stop: 列出了脚本服务没有默认启动的运行级别。运行级别间以空格分隔,且必须包含所有 # Default-Start: 行没有使用过的运行级别。

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

每个需要在任何运行级别下默认启动的 SystemV 风格启动脚本必须在 LSB 头部包含本行 (如果有 # Default-Start: 行就必须有 # Default-Stop: 行,它们总是成对出现。)

例如,一个脚本服务只在运行级别 3,4, 和 5 默认启用,那么 LSB 头部的 # Default-Stop: 行就必须指定运行级别 0, 1, 2, 和 6:

# Default-Stop: 0 1 2 6
请注意,openSUSE 会忽略掉  # Default-Stop: 行,因为 openSUSE 的启动脚本设置使用了在 init.d(7) 的 man 帮助手册里说明的差分链接方案。但该行依然不能省略。


# Short-Description: 行

LSB 头部的 # Short-Description: 行提供了关于启动脚本行为的简要介绍。本行只有一行且不能超过 80 个字符。

# Short-Description: 这是一个邮件服务器服务。(当然要用英文写)

所有的 openSUSE 下 SysV 风格的启动脚本都必须在 LSB 头部包含 # Short-Description: 行。它有点类似于 RPM spec 文件的 Summary: 标签。这是用于在 YaST 运行级别编辑器中显示的。

# Description: 行

LSB 头部的 # Description: 行提供了启动脚本行为更加完整的描述。它可以是多行,后续的行必须以 '#' 加一个 tab (也就是至少两个空格不能多于四个空格)开头。一旦某行首不满足该条件就以为着多行描述的终止。这也是用来在 YaST 运行级别编辑器里显示的。

Example:

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

所有 openSUSE 下的 SysV 风格的启动脚本必须在 LSB 头部包含 # Description: 行。它可以被视为 RPM spec 文件中的 %description 章节。

设备

根据 LSB 的要求, openSUSE 系的发行版启动脚本已经提供了一些默认的设备名。它们在 /etc/insserv.conf 里。目前,有如下系统设备名可供使用:

  • $local_fs — 所有的本地文件系统的挂载点。大多数脚本服务都需要这个。All local file systems are mounted.
  • $remote_fs — All remote file systems are mounted.所有的远程文件系统的挂载点。因为 /usr 目录可以是远程目录,许多脚本服务也应该需要这个。
  • $syslog — 系统日志设备可用。
  • $network — 底层网络设备(比如有线网卡)可用。
  • $named — 主机名解析可用。
  • $netdaemons — 所有的网络守护程序都在运行中。这在 LSB 1.2 中被删除了。但目前我们还提供它以便于向后兼容。
  • $time — 系统时间正确。
  • $portmap — SunRPC portmapping 服务可用。
  • $null — Enforce an empty dependency in case of Required-Stop and Should-Stop otherwise insserv assumes the same dependencies as for Start case.


Beside the LSB compliant System Facilities as defined in /etc/insserv.conf on openSUSE the following System Facilities are known:

  • $allThis facility indicates that a service should be inserted at the end of all services. Clearly all services using this facility will be grouped into one starting order.
  • $nullUsed to enforce an empty dependency in case of # Should-Stop: and # Required-Stop: otherwise insserv(8) assumes the same dependencies as specified at # Should-Start:

respectivly at # Required-Start:.

Other (non-system) facilities may be defined in the # Provides: line in the LSB Header.

行为

/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 的定义,所有的启动脚本都必须知道如何去处理如下行为:

  • start — 启动脚本服务。
  • stop — 停止脚本服务。
  • restart — 如果脚本服务已在运行,重启它。如果没在运行,启动它。
  • reload — 不重启脚本服务直接重新加载它的配置。
  • force-reload — 如果脚本服务支持的话,就重新加载配置,不然就重启。
  • status — 输出脚本服务目前的状态。
  • usage - 默认如果该启动脚本没有附加任何行为选项,它应该列出所有可用的行为以供用户选择。

所有启动脚本都必须支持 startstoprestartforce-reload, 和 status 行为。reload 行为是可选支持的。

openSUSE 定义了如下额外行为:

  • try-restart — 只重启启用着的脚本服务。这个行为现在已被 LSB 1.9+ 收录。红帽也有一个类似的行为叫做 condrestart
  • probe — 探测是否有必要重加载配置文件。如果有必要,根据脚本服务提供的功能来重新加载或重启。如果没必要的话,什么也不做。这个行为目前是可选的。

SystemV 风格的启动脚本应该支持 try-restart 和 condrestart 行为。这两个行为的目的是相同的,因此在行为上不能有所不同。实际操作上,强烈建议打包者将 try-restart和 condrestart 在行为声明中作为同等选项:

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

你也可以添加其他行为,但你必须在 usage 行为中交互输出它以让用户知道并能够使用。

Exit 退出状态代码

参考 LSB 定义。它为启动脚本定义了如下的退出状态代码(status 行为没有定义退出状态代码,详情见下)。

Exit 退出状态代码 描述
0 成功
1 一般或未定义错误
2 无效或多余的参数
3 没有这个行为 (例如 "reload")
4 用户权限不足
5 程序未安装
6 程序未配置
7 程序未运行
8-99 保留,以供 LSB 未来使用
100-149 保留,以供发行版使用
150-199 保留,以供应用程序使用
200-254 保留

对于所有其他的启动脚本行为,启动脚本必须在行为成功时返回退出状态 0。除了直接成功,下列情况也应该被认为是成功的:

  • 使用 force-reload 参数重启服务(不是重加载配置文件)。
  • 服务已在运行时 start
  • 服务不在运行时或已经终止时 stop
  • 服务不在运行时或已经终止时 restart
  • 服务不在运行时或已经终止时 condrestart 或 try-restart

Status 函数

LSB 为 status 行为定义了如下退出代码:

现有 Status 代码 描述
0 程序运行中或脚本服务没问题。
1 程序僵死,/var/run pid 进程文件仍存在。
2 程序僵死,/var/lock lock 文件仍存在。
3 程序没有运行。
4 程序或脚本服务的状态未知。
5-99 保留,以供 LSB 未来使用
100-149 保留给发行版使用
150-199 保留给应用程序使用
200-254 保留

定义在 /etc/rc.status 里的函数可以帮助记录,显示,和返回启动脚本中真实的 rc status 状态信息。它们可以这样引入到启动脚本中:

. /etc/rc.status

有如下函数可用:

rc_active

该函数通过检测链接来检查脚本服务是否启动。如果脚本服务在某运行级别下启动就返回 “0”,否则返回 “1”

rc_exit

该函数结束启动脚本,返回合适的退出状态代码。

rc_failed [num]

该函数将本地和全部的 rc status 设定为由 num 参数定义的值。默认使用 “1”

rc_check

该函数检测最后一条命令的退出状态值 ($?),如果退出状态的值不是 “0” 就把本地 rc_status 的状态设置为现在的值。然后本地 rc 状态的值不是 “0” 又会导致全局的 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 显示 “跳过” 然后设定状态为 “3”。它意味着是一个还没写出来的功能。
  • -u 显示 “无用” 然后设定状态为 “3”。它意味着是一个还没写出来的功能。
  • -v[num] 显示真实状态并且重置本地状态为 “0”。默认地,状态显示在目前的行上。num 参数定义了它应该显示在目前鼠标位置之上的第 num 行。

安装

启动脚本通常被视为软件包源代码,作为一个额外的 %source 文件。它在 %install 章节安装。通常启动脚本不应该被标记为 %config 文件。

虽然启动脚本的位置在 /etc,但是它们是需要被执行而不是被配置的脚本。任何配置都应该通过 /etc/sysconfig/ 服务来做,而不是启动脚本自身。一个有力的豁免是已有的通过启动脚本进行配置的软件包。在这种情况下,遵照 配置文件 章节的规定,启动脚本应该被标记为 %config,以便于升级时能够保留用户配置。当然,用户最好迁移这些配置到一个新的 /etc/sysconfig/ 配置文件。

启动脚本必须有 0755 或者 0700 权限。

也应该有一个名为 rcname 的系统链接指向 /etc/init.d/name。根据服务安装的路径前缀,系统链接既可以放在 /sbin 也可以放在 /usr/sbin。这可以方便启动脚本的手动启动、停止和重启。

最后,启动脚本可以在软件包安装后启用,但必须在软件包卸载后禁用。升级之后也应该重启服务,但卸载前必须停用。宏 %fillup_and_insserv, %insserv_force_if_yast,%restart_on_update, %insserv_cleanup,和 %stop_on_removal 是用来执行这些操作的。

注意 SuSE Linux 提供了 insserv 工具来启用或禁用启动脚本。参考 insserv(8) 的用户手册以获取更详细的信息。该工具也被%fillup_and_insserv%insserv_force_if_yast,和 %insserv_cleanup 宏使用。

在 SuSE Linux 上启动脚本默认是禁用的,除非是那些提供最小化系统功能所需的启动脚本。因此 %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

...

如果启动脚本必须被启用、重启或停止,它们的名字必须作为相关宏的参数列出。唯一有区别的是 %insserv_cleanup 宏。它不需要任何参数,因为无论如何它都会删除全部的跨硬盘分区系统链接。

软件包必须把 SysV 风格的启动脚本放到 /etc/init.d。该目录有一个替代的 RPM 宏,%_initrddir

LSB Provides 实际是怎样在 openSUSE 工作的

基于 LSB 的系统使用 /usr/lib/lsb/install_initd 来启动脚本,/usr/lib/lsb/remove_initd 来禁用脚本。当需要进行这些操作时,先读取 LSB 依赖关系,然后调整脚本们的 start 和 stop 属性以满足依赖关系。

这意思就是说 LSB 头部写明的依赖关系是被遵守的(尽管是一个静态的机制)。 可以使用 insserv(8) 工具来覆盖 LSB 头部的依赖关系,更多解释请阅读 insserv(8) 的用户手册。

初始化环境变量

由于启动脚本可能需要被系统管理员手动运行以支持非标准的环境变量值如 PATH、USER、LOGNAME 等等,启动脚本不应该依赖这些环境变量。需要它们时,它们应该被设定为众所周知的/默认的值。

Initscripts must be on their best behavior

SystemV-style initscripts must behave sensibly if they are started when the service is already running, or stopped when the service is not running. They must not kill unrelated (but perhaps, similarly-named) user processes as a result of their normal actions. The best way to achieve this is to use the init-script functions provided by/etc/rc.status:

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

and the systems tools /sbin/start_daemon or /sbin/startproc for start a process, /sbin/killproc to stop a process, and /sbin/checkproc to check for a running process. For an furhter explanation the manual pages of start_daemon(8) or startproc(8), killproc(8), and checkproc(8) should be consulted.

The startproc command starts processes identified by path name of the executable. The exist status is LSB see “Exit Status Codes” for more details. It can be replaced by the LSB command start_daemon which does not fork before starting the executable. That requires that the new process its self forks and disconnect from the terminal (see manual pages startproc(8)daemon(3)fork(2), and setsid(2)). The killproc LSB command sends signals to processes identified by full path name of the executable. (see manual page killproc(8)). The pidofproc LSB command uses the base name of an the to seek for a process and the checkproc command uses the full path of an executable to do the same (see manual page pidofproc(8)).

Then there are usually checks of whether the service is correctly installed and the related sysconfig files are read. The LSB compliant error values must be returned in case of problems. See “Exit Status Codes” for more details.

This example is taken from /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


If a service reloads its configuration automatically (as in the case of cron, for example), the reload action of the initscript must behave as if the configuration was reloaded successfully. The restart, condrestart, try-restart, reload and force-reload actions may be atomic; that is if a service is known not to be operational after a restart or reload, the script may return an error without any further action.


用于 SystemV 启动脚本中的系统工具

start_daemon(8) 和 startproc(8) 工具

  • start_daemon 启动一个守护程序,如果该程序当前没有运行的话。
  • startproc 将一个进程作为守护程序启动,如果该进程当前没有运行的话。
start_daemon [-fLve] [[-n 数字]+/-<增减优先级>] [-u 用户] [-g 组] [-l 日志文件|-q|-d] [-p 进程文件]
          [-i 要忽略的文件][-c 以根用户执行] 到可执行文件的完整路径 [可执行文件的参数]


startproc [-fLves] [[-n 数字]+/-<增减优先级>] [-(t|T) <秒数>] [-u 用户] [-g 组] [-l 日志文件|-q|-d] [-p 进程文件]
          [-i 要忽略的文件] [-c 以根用户执行] 到可执行文件的完整路径 [可执行文件的参数]

killproc(8) 工具

向程序发送一个信号; 默认发送 SIGTERM 结束信号, 如果进程没死的话,会在数秒后发送一个 SIGKILL 杀死信号。它也会试着移除能找到的进程文件。

killproc [-vqLN] [-g|-G] [-p 进程文件] [-i 要忽略的文件] [-c 以根用户运行] [-t <秒数>] [-] 到可执行文件的完整路径

killproc -n [-vq] [-g|-G] [-t <秒数>] [-] 内核 thread 会话的名字

killproc    [-vq] [-g|-G] [-t <秒数>] [-] 可执行文件的 basename 基础名称

checkproc(8) 和 pidofproc(8) 工具

试着找到一个程序的状态和进程编号; 检查进程文件。pidofproc 主要是 checkproc 的调试版本。

checkproc [-vLkNz] [-p 进程文件] [-i 要忽略的文件] [-c 以根用户运行] 可执行文件的完整路径

checkproc -n [-vk] 内核 thread 会话的名称

checkproc    [-vk] 可执行文件的 basename 基础名称

pidofproc [-LkNz] [-p 进程文件] [-i 要忽略的文件] [-c 以根用户运行] 可执行文件的完整路径

pidofproc -n [-k] 内核 thread 会话的名称

pidofproc    [-k] 可执行文件的 basename 基础名称

LSB 命令行函数

如上系统工具也可以由 /lib/lsb/init-functions 内的命令行函数提供:

# 加载 LSB 命令行函数
. /lib/lsb/init-functions

加上 LSB 3.1 或更高版本指定要的提要:

start_daemon [-f] [-n +/-<优先级>] 到可执行文件的完整路径 [可执行文件的参数]
killproc [-p 进程文件] 到可执行文件的完整路径 [-]
pidofproc [-p 进程文件] 到可执行文件的完整路径

参考:

http://blog.163.com/bull_linux/blog/static/2138811422012102695650440/

http://wiki.debian.org/LSBInitScripts

http://wiki.debian.org/LSBInitScripts/DependencyBasedBoot




博主所有文章已转自私人博客  Joe 的个人博客 ,谢谢关注!

你可能感兴趣的:(linux)