Home Wiki > openSUSE:Packaging init scripts
Sign up | Login

openSUSE:Packaging init scripts

tagline: From openSUSE

This document describes the guidelines for SysV-style Initscripts, for use and inclusion in openSUSE packages. They are LSB compilant. For systemd unit files, refer to openSUSE:Systemd_packaging_guidelines.

Name

The name of init scripts must be LSB compliant, so it must be listed at http://www.lanana.org/lsbreg/init/init.txt. A new name can be registered as described at http://www.lanana.org/lsbreg/instructions.html.

Structure

The structure of init scripts is well described in /etc/init.d/skeleton. This file can also be used as an efficient template for new init scripts.

LSB Header

Init scripts are shell scripts, so the file begins with the usual header:

#!/bin/sh

or

#!/bin/bash

Then there are usually comments. These should mention the information about author, copyright, or license. The file must include a special comment header providing some meta information about the init script. The LSB Header is bounded by comments, specifically, the beginning of the header is marked with:

### BEGIN INIT INFO

The end of the LSB Header is marked with:

### END INIT INFO

All LSB Header entries must have these boundary comments.

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

The LSB Header is composed of the following sections:

# Provides: line

The # Provides: line in the LSB Header lists any boot facilities that this service provides. Other services can reference these boot facilities in their # Required-Start: and # Required-Stop: lines. It is usually the name of the daemon. If more than one package can provide the same facility (e.g., sendmail vs. postfix, dhcpcd vs. dhclient), the init scripts of both should provide the same facility name.

# Provides: boot_facility_1 [boot_facility_2...]

When an initscript is run with a start argument, the boot facility or facilities specified by the Provides keyword shall be deemed present and hence init scripts which require those boot facilities should be started later. When an initscript is run with a stop argument, the boot facilities specified by the Provides keyword are deemed no longer present.

# Required-Start: line

The # Required-Start: line in the LSB Header lists any boot facilities which must be available during startup of this service.

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

This line is mandatory, if an initscript has no need for requiring other boot facilities before starting, you must use Required-Start, even if empty !

# Required-Stop: line

The # Required-Stop: line in the LSB Header lists any boot facilities which should NOT be stopped before shutting down this service.

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

This line is mandatory, if an initscript has no need for requiring other boot facilities before starting, you must use Required-Start, even if empty !

# Should-Start: line

The # Should-Start: line in the LSB Header lists any facilities, which, if present, should be available during startup of this service. The intent is to allow for "optional" dependencies which do not cause the service to fail if a facility is not available.

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

This line is optional, if an initscript has no use for starting other optional dependencies before hand, it should be omitted.

# Should-Stop: line

The # Should-Stop: line in the LSB Header lists any facilities, which, if present, should only be stopped after shutting down this service. The intent is to allow for "optional" dependencies which do not cause the service to fail if a facility is not available.

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

This line is optional, if an initscript has no use for preventing other optional dependencies from stopping until after it has shutdown, the line should be omitted.


LSB allows defining distribution-specific extensions to this header. Such keywords are prefixed by X-VendorTag-. The following keywords are available on SUSE Linux:

# X-Start-Before: line

The # X-Start-Before: line in the LSB Header implies that the script using this keyword should be started before the specified facilities.

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

This line is optional and currently vendor specific (openSUSE).

# X-Stop-After: line

The # X-Stop-After: line in the LSB Header implies that the script using this keyword should be stopped after the specified facilities.

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

This line is optional and currently vendor specific (openSUSE).

Both # X-Start-Before: and # X-Stop-After: allows script writers to use dependencies in the new script without modifying other e.g. system init scripts.

# Default-Start: line

The # Default-Start: line in the LSB Header lists the runlevels for which the service will be enabled by default. These runlevels are space-separated.

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

Each SystemV-style initscript which needs to start by default in any runlevel must include this line in the LSB Header. Only services which are really required for a vital system should define runlevels here. If the service does not start by default in any runlevel, this line should be omitted. For example, if a service starts by default in runlevels 3, 4, and 5 only, the LSB Header in the initscript would specify:

# Default-Start: 3 4 5

# Default-Stop: line

The # Default-Stop: line in the LSB Header lists the runlevels for which the service will not be started by default. These runlevels are space-separated, and must contain all of the numeric runlevels not used in the # Default-Start: line.

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

Each SystemV-style initscript which needs to start by default in any runlevel must include this line in the LSB Header (if the # Default-Start: line is present, then there should also be a # Default-Stop: line.)

For example, if a service starts by default in runlevels 3, 4, and 5 only, then the # Default-Stop: line in the LSB Header has to specify runlevels 0, 1, 2, and 6:

# Default-Stop: 0 1 2 6

Please note, hat the # Default-Stop: are ignored in openSUSE, because the boot script concept uses a differential link scheme as described in the manual page init.d(7).

# Short-Description: line

The # Short-Description: line in the LSB Header provides a brief summary of the actions of the init script. This must be no longer than a single, 80 character line of text.

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

All openSUSE SysV-style initscripts must contain the # Short-Description: line in the LSB Header. It can be considered roughly equivalent to the Summary: field in an RPM spec file. It is displayed, for example, in the YaST Runlevel Editor.

# Description: line

The # Description: line in the LSB Header provides a more complete description of the actions of the initscript. It may span mulitple lines, where each continuation line must begin with a '#' followed by tab character or a '#' followed by at least two space characters. The multiline description is terminated by the first line that does not match this criteria. This information is also displayed in the YaST runlevel editor.

Example:

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

All openSUSE SysV-style initscripts must contain the # Description: line in the LSB Header. It can be considered roughly equivalent to the %description section in an RPM spec file.

Facilities

As required by LSB, SUSE Linux init scripts already provide some default facility names. These are defined in /etc/insserv.conf. At the moment, the following system facility names are provided:

  • $local_fs — All local file systems are mounted. Most services should need this.
  • $remote_fs — All remote file systems are mounted. Because /usr may be remote, many services should need this as well.
  • $syslog — The system logging facility is up.
  • $network — Low level networking (eth card, etc.) is up.
  • $named — Hostname resolution is available.
  • $netdaemons — All network daemons are running. This was removed in LSB 1.2. For now, it still available for backward compatibility.
  • $time — The system time has been set correctly.
  • $portmap — SunRPC portmapping service is available.
  • $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.</dd>
  • $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.


Actions

See /etc/init.d/skeleton, which contains a sample code and includes some valuable comments. The following example is taken from /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

As defined by the LSB, all init scripts should know how to handle the following arguments:

  • start — Starts the service.
  • stop — Stops the service.
  • restart — Stops and restarts the service if it is already running. Otherwise, starts the service.
  • reload — Causes the configuration of the service to be reloaded without actually stopping and restarting the service.
  • force-reload — Causes the configuration to be reloaded if the service supports this. Otherwise, the service is restarted.
  • status — Prints the current status of the service.
  • usage - by default, if the initscript is run without any action, it should list a "usage message" that has all actions (intended for use)

The start, stop, restart, force-reload, and status actions must be supported by all init scripts. The reload action is optional.

SUSE defines the following additional actions:

  • try-restart — Restarts the service only if it was active before. This action is now part of LSB (as of 1.9). Red Hat has a similar action named condrestart.
  • probe — Probes for the necessity of a reload. Depending on the service, prints "reload" or "restart" if a reload is required. Nothing is printed if a reload is not required. This action is optional and is not part of LSB (as of 1.9).

SystemV-style initscripts should support both the try-restart and condrestart action. These two actions are intended to serve an identical purpose, and must not differ in behavior. In fact, it is highly recommended that packagers implement try-restart and condrestart and as equivalent options in the case statement:

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

You are not prohibited from adding other commands, but you should list all commands which you intend to be used interactively to the usage message.

Exit Status Codes

Reference: See LSB Specification. It defines the following exit status codes for init scripts (except for the status action, see below):


Exist Status Code Description
0 success
1 generic or unspecified error
2 invalid or excess arguments
3 unimplemented feature (e.g., "reload")
4 user had insufficient privileges
5 program is not installed
6 program is not configured
7 program is not running
8-99 reserved for future LSB use
100-149 reserved for distribution use
150-199 reserved for application use
200-254 reserved

For all other initscript actions, the init script must return an exit status of zero if the action was successful. In addition to straightforward success, the following situations are also to be considered successful:

  • restarting a service (instead of reloading it) with the force-reload argument
  • running start on a service already running
  • running stop on a service already stopped or not running
  • running restart on a service already stopped or not running
  • running condrestart or try-restart on a service already stopped or not running

Status Functions

The LSB defines the following exit codes for the status action:

Exist Status Code Description
0 program is running or service is OK
1 program is dead and /var/run pid file exists
2 program is dead and /var/lock lock file exists
3 program is not running
4 program or service status is unknown
5-99 reserved for future LSB use
100-149 reserved for distribution use
150-199 reserved for application use
200-254 reserved

The functions defined in /etc/rc.status help record, display, and return the actual rc status information in init scripts. They can be sourced into the init scripts this way:

. /etc/rc.status

The following functions are available:

rc_active

This function checks whether a service is enabled (by symlinks). It returns “0” if the service is enabled in a runlevel and returns “1” otherwise.

rc_exit

This function terminates an init script with the exit status appropriate to the overall rc status.

rc_failed [num]

This function sets the local and the overall rc status to a selected value defined by the parameter num. The value “1” is used by default.

rc_check

This function checks the exit status of the last command ($?) and sets the local rc status to this value if the value is different from “0”. Then it sets the overall rc status to the value of the local rc status if it differs from “0”. This function is used internally by other rc status functions.

rc_reset

This function sets both the local and the overall rc status to “0”.

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

This function checks, sets, and displays the rc status. By default, it is quiet: it only calls rc_check. So, it must be called with an option to display the status. The options have the following meaning:

  • -r calls rc_reset. This option is usable together with -v. The command rc_status -v -r checks, sets, and displays the current rc status. Then rc_reset is called.
  • -s displays “skipped” and sets the status to “3”. It means an unimplemented feature.
  • -u displays “unused” and sets the status to “3”. It means an unimplemented feature.
  • -v[num] displays the actual status and resets local status to “0”. By default, the status is displayed on the actual line. The parameter num defines that it should display num lines above the actual cursor position.


Installation

The init script is usually included in the package sources and an extra source file. It is installed in the %install section. Normally initscripts may not be marked as %config files.

Although init files live in /etc, they are scripts to be executed, not configured. Any configuration should be made available through /etc/sysconfig/<service> rather than in the init script itself. A valid exception to this rule would be existing packages where configuration is still done via the init file. In this case, the init file could be marked as %config following the rules from the Configuration files section to preserve a users configuration upon upgrade, hopefully so that the user can migrate said configuration to a new /etc/sysconfig/<service> config file.

Init scripts require to have 0755 or 0700 permissions.

There could also be a symlink named rcname pointing to /etc/init.d/name. The symlink is located either in /sbin or in /usr/sbin depending on the prefix where the service is installed. It is useful for init scripts that can be started, stopped, restarted by hand.

Finally, the init script can be enabled after the package is installed and must be disabled after the package is removed. The service should be restarted after an update and should be stopped before the package is removed. The macros %fillup_and_insserv, %insserv_force_if_yast, %restart_on_update, %insserv_cleanup, and %stop_on_removal are intended for this purpose.

Note that SUSE Linux provides the utility insserv to enable or disable init scripts. Refer to the manual page of insserv(8) for more detailed information. This utility is also used by the macros %fillup_and_insserv, %insserv_force_if_yast, and %insserv_cleanup.

The init scripts are disabled by default on SUSE Linux, except those that are necessary for minimal system functionality. Therefore the %fillup_and_insserv and %insserv_force_if_yast macros are used only in packages providing such base services.

Spec file example:

...

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

...

The names of init scripts must be listed as parameters of the related macros if they must be enabled, restarted, or stopped. The only difference is the macro %insserv_cleanup. It does not need any parameters because it removes all dangling symlinks anyway.

Packages with SysV-style initscripts must put any them into /etc/init.d. A rpm macro exists for this directory, %_initrddir.

How LSB Provides actually work on openSUSE

LSB based system uses /usr/lib/lsb/install_initd for script activation and /usr/lib/lsb/remove_initd for deactivation. When these tasks occur, the LSB dependencies are read, and the start and stop priorities of the scripts are then adjusted to satisfy those dependencies.

What this means that the LSB header dependencies are honored (albeit in a static mechanism). For override dependencies found in the LSB header the system tool insserv(8) can be used, for an further explanation how to do this the manual page insserv(8) should be read.


Initialization of Environment Variables

Since initscripts may be run manually by a system administrator with non-standard environment variable values for PATH, USER, LOGNAME, etc., init scripts should not depend on the values of these environment variables. They should be set to known/default values if they are needed.


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.


System tools for SystemV initscripts

start_daemon(8) and startproc(8) tools

  • start_daemon Starts a daemon, if it is not already running.
  • startproc Starts a process as a daemon, if it is not already running.
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) tool

Sends a signal to the program; by default it sends a SIGTERM, and if the process doesn't die, it sends a SIGKILL a few seconds later. It also tries to remove the pidfile, if it finds one.

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) and pidofproc(8) tool

Try to find the status and pid of a program; checking likely pid files. Mainly the pidofproc is the verbose version of 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 functions

the above systems tools will be alos provided by the shell functions found in /lib/lsb/init-functions:

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

together with the synopsis specified by the LSB 3.1 and higher:

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