Starting Linux services with init scripts

A Linux system provides many services to the users, like secure shell access or the print spooler service. These services need to be started and stopped at appropriate times. The start and stop of these services is controlled by init scripts. An init script for a service specifies when that service should be started and when it should be stopped.

THE init PROCESS

After the kernel image has been decompressed and kernel has been initialized, the first user space process init is created by the kernel. init has the process-id 1. It is the job of init to start further processes for running the system. So init is the parent of all these processes. Also, init adopts all processes whose natural parents have died.

RUN LEVELS

A Linux system works at a certain run levels, based on the configuration of processes running in the system and the functionality provided. The levels are 0-6 and S, or s. Three levels are reserved and are,

  • 0, for system halt
  • 1, for bringing the system to single user mode and,
  • 6, for rebooting the system.

Of the remaining levels, S, or s also bring the system to the single user model. The interpretation of remaining levels varies between implementations. For example, an implementation might provide level 2 for multi-user mode but without some networking protocols like NFS, level 3 for all network services and level 5 for availability of graphical user interface. Levels 2-5 are said to be user-defined and one implementation of level 2 might well be equivalent to level 3 of another and level 5 of yet another implementation. However, in all cases, there is a default run level, where all services are available and the system is fully usable.

There is a Linux command runlevel, which prints the previous and current run levels on the basis of records in the file, /var/run/utmp.

System V init PROCESS

In Unix System V, there is the /etc/inittab file, which provides the script for the init process. /etc/inittab serves two purposes. First, it identifies a default runlevel for the init process, that is, the runlevel to which it should bring up the system to, unless it has been executed with a specific runlevel parameter. When the system is booted, init brings the system to the default runlevel. Second, /etc/inittab gave init a list of programs to be executed for a given runlevel. /etc/inittab is not found on present day Ubuntu Linux systems as now System V init process is no longer used and the Upstart init process management daemon (described later on, in this tutorial) is used instead.

Looking at a part of a typical /etc/inittab file,

# The default runlevel
id:2:initdefault:

# Boot-time system configuration/initialization script.
si::sysinit:/etc/rc.sysinit

# What to do in single-user mode.
~:S:wait:/sbin/sulogin

# /etc/init.d executes the S and K scripts upon change
# of runlevel.
#
# Runlevel 0 is halt.
# Runlevel 1 is single-user.
# Runlevels 2-5 are multi-user.
# Runlevel 6 is reboot.

l0:0:wait:/etc/rc 0
l1:1:wait:/etc/rc 1
l2:2:wait:/etc/rc 2
l3:3:wait:/etc/rc 3
l4:4:wait:/etc/rc 4
l5:5:wait:/etc/rc 5
l6:6:wait:/etc/rc 6

# What to do at the CTRL-ALT-DEL
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

# Runlevel 2,3: getty on virtual consoles
1:23:respawn:/sbin/mingetty tty1
2:23:respawn:/sbin/mingetty tty2
3:23:respawn:/sbin/mingetty tty3
4:23:respawn:/sbin/mingetty tty4

The entries are of the form id:runlevels:action:process, where id is the identifier of the relevant inittab entry, runlevels are the runlevels at which the entry is to be processed and process refers to the command to be executed. The third field, action tells how init should execute the command in the process field. action can have values like, wait, respawn, initdefault, etc. wait means that init should start this process when it enters this runlevel and wait for its completion. If the system was already at this runlevel and the init command for this runlevel was given, init would not execute this command. respawn means that the process would be restarted whenever it terminates.

Looking at the lines like l2:2:wait:/etc/rc 2 in the inittab file, it means that after entering the given runlevel (2, here), init executes /etc/rc with the runlevel (2, here) parameter. rc uses the runlevel parameter to decide which set of scripts are to be run. For example, in this case, rc, using the runlevel parameter 2, would run the scripts in /etc/rc2.d directory.

The scripts are kept in the /etc/init.d directory. A runlevel scripts directory, like /etc/rc1.d or /etc/rc2.d has symbolic links to these scripts. The links start with a K or an S character, followed by a number between 01 and 99. The /etc/rc script executes first the K scripts and then the S scripts, both in the ascending order of the two digit number, following the K or S character in the script link name.

init and Upstart

init has been in operation for a long time, but there are difficulties. Most of the problems stem from the fact that init is essentially a synchronous kind of process system. It executes certain scripts when it enters a runlevel sequentially, one after another. It is not able to discover and mount devices, when there is no change in runlevel. The most common example of this is a USB flash drive inserted in one of the USB ports of a system. Similarly, when a network interface become suddenly available, init does not know about it and is not able to do the necessary initialization steps and make the interface usable.

The solution has come from the Ubuntu team in the form of Upstart init process management daemon, which is the replacement for the System V init daemon. Upstart process management daemon was introduced in Ubuntu version 6.04, Edgy Eft, released in October 2006. The Upstart init daemon is event-based. It executes jobs in /etc/init directory based on events generated.

Upstart is backward compatible with System V init, and is able to run System V init scripts unmodified. So one can either use an existing (or write new) System V init script or write an Upstart job. We will look at both cases, but first some basic Upstart concepts.

Upstart is event based; services are started and stopped on the basis of events. The concept of runlevels exists in a limited way, just to maintain backward compatibility with System V init. It is not that the Upstart init daemon runs certain scripts whenever there is a change of runlevel. The Upstart services are started or stopped whenever a corresponding event is emitted by some entity in the system. However, for backward compatibility with System V init scripts, the current runlevel information is maintained and a change in runlevel is emitted as an event, so that corresponding System V scripts can be executed. inittab file has has more or less disappeared from the scene. The only unlikely use of inittab is when you wish to override the default runlevel 2 with some other runlevel (3, 4 or 5). Then you can have an initdefault entry to that effect in the /etc/inittab file. A change in runlevel is also signaled as an event.

Developing an init script

We will develop an init script, first using the System V init script concepts and check whether it works fine with the Upstart init infrastructure. Then, we will develop an equivalent Upstart job.

Writing a System V init script

The starting point is the file /etc/init.d/skeleton, which is the template for making init scripts as per the Linux Standard Base (LSB) guidelines. Copy this file into a file with the name of the desired init script, say my_server in the /etc/init.d directory . Next we need to modify the my_server script with the daemon name, required start and stop boot facilities, start and stop runlevels, etc . After that, the init script can be installed with the command,

$ sudo sysv-rc-conf --level 2345 my_server on
$ sudo sysv-rc-conf --level 016 my_server off

sysv-rc-conf is not installed by default. It can be installed with the command,

$ sudo apt-get install sysv-rc-conf

Once, the script is installed, the corresponding daemon can be started with the command,

$ sudo invoke-rc.d my_server start

Or, you can use the popular service command,

$ sudo service my_server start

Upstart Events

Events are generated by the init daemon at certain points like startup and shutdown and are also received from other parts of the system like udev. Any part of the system can generate event using the initctl command.The startup event is signalled by Upstart init daemon signalling that it has completed its own initialization and that the rest of the system may be started. Other useful events are starting, started, stopping and stopped, emitted as jobs progress from start to completion.

Upstart Jobs

Upstart jobs definitions are kept in text files in the directory /etc/init. The file name of job files must end in .conf. The name of the job is the name of the file sans .conf. The job files must not have the executable permission for anyone. A job may be a task, in which case init's job is just to start it and forget about it. Else, a job may be a service, in which case it is expected to run for ever. If, for any reason, a service terminates, init restarts it. By default, all jobs are deemed to be services, unless there is a task stanza in the job file specifying it to be a task.

Let's look at a sample job file shown below. It is for starting a hypothetical server named myserver. The file is named myserver.conf.


# myserver - starting myserver after system startup
#

description "myserver - example"
author "Yours truly <yt@example.com>"

start on net-device-up IFACE=eth0
stop on runlevel [01S6]

console output

chdir /home/abc/prog/bin

exec /home/abc/prog/bin/myserver

In the job file above, the lines starting with # are comments. A job file has multiple configuration stanzas. As the names suggest, the description stanza gives a description of the job and the author stanza has a string giving details about the author of the job configuration file. Now, we have the most important start on stanza. myserver depends upon availability of the network interface eth0 for communication with clients. network-interface job emits net-device-up event when the network interface eth0 comes up. The stop on stanza here indicates that the job should stop at runlevels, 0,1, S and 6. By default, the outputs go to /dev/null, so the console stanza says that the outputs should come to console instead of /dev/null. We want myserver to execute in directory /home/abc/prog/bin and chdir stanza ensures that.

Each job file must have a exec or script stanza. exec specifies the binary executable file to be executed for the job whereas script gives the shell script to be executed by the /bin/sh -e command.

The above server can be started with the command,

$ initctl start myserver

RELATED COMMANDS

init(8), update-rc.d(8), initctl(8), inotify(7), runlevel(7), starting(7), started(7), stopping(7) and stopped(7).

Software: