System V init

This init program was inspired by the one from UNIX System V, and so dates back to the mid 1980s. The version most often found in Linux distributions was written initially by Miquel van Smoorenburg. Until recently, it was considered the way to boot Linux, obviously including embedded systems, and BusyBox init is a trimmed down version of System V init.

Compared to BusyBox init, System V init has two advantages. Firstly, the boot scripts are written in a well-known, modular format, making it easy to add new packages at build time or runtime. Secondly, it has the concept of runlevels, which allow a collection of programs to be started or stopped in one go, by switching from one runlevel to another.

There are 8 runlevels numbered from 0 to 6, plus S:

  • S: Single user mode
  • 0: Halt the system
  • 1 to 5: General use
  • 6: Reboot the system

Levels 1 to 5 can be used as you please. On desktop Linux distributions, they are conventionally assigned as follows:

  • 1: Single user
  • 2: Multi-user with no network configuration
  • 3: Multi-user with network configuration
  • 4: Not used
  • 5: Multi-user with graphical login

The init program starts the default runlevel given by the initdefault line in /etc/inittab. You can change the runlevel at runtime using the command telinit [runlevel] which sends a message to init. You can find the current runlevel, and the previous one, by using the runlevel command. Here is an example:

# runlevel
N 5
# telinit 3
INIT: Switching to runlevel: 3
# runlevel
5 3

On the first line, the output from runlevel is N 5, meaning that there is no previous runlevel because the runlevel has not changed since booting, and the current runlevel is 5. After changing the runlevel, the output is 5 3 showing that there has been a transition from 5 to 3. The halt and reboot commands switch to runlevels of 0 and 6 respectively. You can override the default runlevel by giving a different one on the kernel command line as a single digit from 0 to 6, or S for single user mode. For example, to force the runlevel to be for a single user, you would append S to the kernel command line and it would look something like this:

console=ttyAMA0 root=/dev/mmcblk1p2 S

Each runlevel has a number of scripts that stop things, called kill scripts, and another group that starts things, the start scripts. When entering a new runlevel, init first runs the kill scripts and then the start scripts. Running daemons which have neither a start script nor a kill script in the new runlevel are sent a SIGTERM signal. In other words, the default action on switching runlevel is to terminate the daemons unless told to do otherwise.

In truth, runlevels are not used that much in embedded Linux: most devices simply boot to the default runlevel and stay there. I have a feeling that it is partly because most people are not aware of them.

Tip

Runlevels are a simple and convenient way to switch between modes, for example, from production to maintenance mode.

System V init is an option in Buildroot and the Yocto Project. In both cases, the init scripts have been stripped of any bash specifics, so they work with the BusyBox ash shell. However, Buildroot cheats by replacing the BusyBox init program with SystemV init and adding inittab that mimics the behavior of BusyBox. Buildroot does not implement runlevels except that switching to levels 0 or 6 halts or reboots the system.

Next, let's look at some of the details. The following examples are taken from the fido version of the Yocto Project. Other distributions may implement the init scripts a little differently.

inittab

The init program begins by reading /etc/inttab, which contains entries that define what happens at each runlevel. The format is an extended version of the BusyBox inittab that I described in the preceding section, which is not surprising because BusyBox borrowed it from System V in the first place!

The format of each line in inittab is as follows:

id:runlevels:action:process

The fields are shown here:

  • id: A unique identifier of up to four characters.
  • runlevels: The runlevels for which this entry should be executed. (This was left blank in the BusyBox inittab)
  • action: One of the keywords given as follows.
  • process: The command to run.

The actions are the same as for BusyBox init: sysinit, respawn, once, wait, restart, ctrlaltdel, and shutdown. However, System V init does not have askfirst, which is specific to BusyBox.

As an example, this is the complete inttab supplied by the Yocto Project target core-image-minimal:

# /etc/inittab: init(8) configuration.
# $Id: inittab,v 1.91 2002/01/25 13:35:21 miquels Exp $

# The default runlevel.
id:5:initdefault:

# Boot-time system configuration/initialization script.
# This is run first except when booting in emergency (-b) mode.
si::sysinit:/etc/init.d/rcS

# 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/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
# Normally not reached, but fallthrough in case of emergency.
z6:6:respawn:/sbin/sulogin
AMA0:12345:respawn:/sbin/getty 115200 ttyAMA0
# /sbin/getty invocations for the runlevels.
#
# The "id" field MUST be the same as the last
# characters of the device (after "tty").
#
# Format:
#  <id>:<runlevels>:<action>:<process>
#

1:2345:respawn:/sbin/getty 38400 tty1

The fist entry, id:5:initdefault, sets the default runlevel to 5. The next entry, si::sysinit:/etc/init.d/rcS, runs the script rcS at boot up. There will be more about this later. A little further on, there is a group of six entries beginning with l0:0:wait:/etc/init.d/rc 0. They run the script /etc/init.d/rc each time there is a change in the runlevel: this script is responsible for processing the start and kill scripts. There is an entry for runlevel S which runs the single-user login program.

Towards the end of inittab, there are two entries that run a getty daemon to generate a login prompt on the devices /dev/ttyAMA0 and /dev/tty1 when entering runlevels 1 through to 5, thereby allowing you to log on and get an interactive shell:

AMA0:12345:respawn:/sbin/getty 115200 ttyAMA0
1:2345:respawn:/sbin/getty 38400 tty1

The device ttyAMA0 is the serial console on the ARM Versatile board we are emulating with QEMU, it will be different for other development boards. Tty1 is a virtual console which is often mapped to a graphical screen if you have built your kernel with CONFIG_FRAMEBUFFER_CONSOLE or VGA_CONSOLE. Desktop Linux usually spawns six getty processes on virtual terminals 1 to 6, which you can select with the key combination Ctrl + Alt + F1 through Ctrl + Alt + F6, with virtual terminal 7 reserved for the graphical screen. Virtual terminals are seldom used on embedded devices.

The script /etc/init.d/rcS that is run by the sysinit entry does little more than enter runlevel S:

#!/bin/sh

[...]
exec /etc/init.d/rc S

Hence, the first run level entered is S, followed by the default runlevel of 5. Note that runlevel S is not recorded and is never displayed as a prior runlevel by the runlevel command.

The init.d scripts

Each component that needs to respond to a runlevel change has a script in /etc/init.d to perform that change. The script should expect two parameters: start and stop. I will give an example of this later.

The runlevel handling script, /etc/init.d/rc, takes the runlevel it is switching to as a parameter. For each runlevel, there is a directory named rc<runlevel>.d:

# ls -d /etc/rc*
/etc/rc0.d  /etc/rc2.d  /etc/rc4.d  /etc/rc6.d
/etc/rc1.d  /etc/rc3.d  /etc/rc5.d  /etc/rcS.d

There you will find a set of scripts beginning with a capital S followed by two digits and you may also find scripts beginning with a capital K. These are start and kill scripts: Buildroot uses the same idea, borrowed from here:

# ls /etc/rc5.d
S01networking   S20hwclock.sh   S99rmnologin.sh S99stop-bootlogd
S15mountnfs.sh  S20syslog

These are in fact symbolic links back to the appropriate script in init.d. The rc script runs all the scripts beginning with a K first, adding the stop parameter , and then runs those beginning with an S adding the start parameter . Once again, the two digit code is there to impart the order in which the scripts should run.

Adding a new daemon

Imagine that you have a program named simpleserver which is written as a traditional Unix daemon, in other words, it forks and runs in the background. You will need an init.d script like this:

#! /bin/sh

case "$1" in
  start)
    echo "Starting simpelserver"
    start-stop-daemon -S -n simpleserver -a /usr/bin/simpleserver
    ;;
  stop)
    echo "Stopping simpleserver"
    start-stop-daemon -K -n simpleserver
    ;;
  *)
    echo "Usage: $0 {start|stop}"
  exit 1
esac

exit 0

Start-stop-daemon is a helper function that makes it easier to manipulate background processes such as this. It originally came from the Debian installer package, dpkg, but most embedded systems use the one from BusyBox. It starts the daemon with the -S parameter, making sure that there is never more than one instance running at any one time and it finds the daemon by name with -K and sends a signal, SIGTERM, by default. Place this script in /etc/init.d/simpleserver and make it executable.

Then, add symlinks from each of the run levels that you want to run this program from, in this case, only the default runlevel, 5:

# cd /etc/init.d/rc5.d
# ln -s ../init.d/simpleserver S99simpleserver

The number 99 means that this will be one of the last programs to be started. Bear in mind that there may be other links beginning S99, in which case the rc script will just run them in lexical order.

It is rare in embedded devices to have to worry too much about shutdown operations, but if there is something that needs to be done, add kill symlinks to levels 0 and 6:

# cd /etc/init.d/rc0.d
# ln -s ../init.d/simpleserver K01simpleserver
# cd /etc/init.d/rc6.d
# ln -s ../init.d/simpleserver K01simpleserver

Starting and stopping services

You can interact with the scripts in /etc/init.d by calling them directly with, for example, the syslog script which controls the syslogd and klogd daemons:

# /etc/init.d/syslog --help
Usage: syslog { start | stop | restart }

# /etc/init.d/syslog stop
Stopping syslogd/klogd: stopped syslogd (pid 198)
stopped klogd (pid 201)
done

# /etc/init.d/syslog start
Starting syslogd/klogd: done

All scripts implement start and stop and should implement help. Some implement status as well, which will tell you whether the service is running or not. Mainstream distributions that still use System V init have a command named service to start and stop services and hide the details of calling the scripts directly.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.152.159