Author: Mark Sobell
Because the traditional System V init daemon (SysVinit) does not deal well with modern hardware, including hotplug devices, USB hard and flash drives, and network-mounted filesystems, Ubuntu replaced it with the Upstart init daemon.
This article is excerpted from the recently published book A Practical Guide to Ubuntu Linux.
Several other replacements for SysVinit are also available. One of the most prominent, initng, is available for Debian and runs on Ubuntu. Solaris uses SMF (Service Management Facility) and Mac OS uses launchd. Over time, Ubuntu will likely come to incorporate features of each of these systems into Upstart.
The runlevel-based SysVinit daemon (sysvinit package) uses runlevels (single-user, multiuser, and more) and links from the /etc/rc?.d directories to the init scripts in /etc/init.d to start and stop system services. The event-based Upstart init daemon (upstart package) uses events to start and stop system services. With the Feisty release, Ubuntu switched to the Upstart init daemon and began the transition from the SysVinit setup to the Upstart setup. This article discusses Upstart and the parts of SysVinit that remain: the /etc/rc?.d and /etc/init.d directories and the concept of runlevels.
The Upstart init daemon is event-based and runs specified programs when something on the system changes. These programs, which are frequently scripts, start and stop services. This setup is similar in concept to the links to init scripts that SysVinit calls as a system enters runlevels, except Upstart is more flexible. Instead of starting and stopping services only when the runlevel changes, Upstart can start and stop services upon receiving information that something on the system has changed. Such a change is called an event. For example, Upstart can take action when it learns from udev that a filesystem, printer, or other device has been added or removed from the running system. It can also start and stop services when the system is brought up, when the system is shut down, or when a job changes state.
The Upstart system comprises five packages, all of which are installed by default:
-
upstart provides the Upstart init daemon and initctl utility.
-
upstart-logd provides the logd daemon and the job definition file for the logd service.
-
upstart-compat-sysv provides job definition files for the rc tasks as well as the reboot, runlevel, shutdown, and telinit utilities that provide compatibility with SysVinit.
-
startup-tasks provides job definition files for system startup tasks.
-
system-services provides job definition files for tty services.
Definitions
There are several terms it helps to understand when talking about init. An event is a change in state that init can be informed of. Almost any change in state — either internal or external to the system — can trigger an event. For example, the boot loader triggers the startup event, the system entering runlevel 2 triggers the runlevel 2 event, and a filesystem being mounted triggers the path-mounted event. Removing and installing a hotplug or USB device (such as a printer) can trigger an event. You can also trigger an event manually by using the initctl emit
command.
A job is a series of instructions that init reads. The instructions typically include a program (binary file or shell script) and the name of an event. The Upstart init daemon runs the program when the event is triggered. You can run and stop a job manually using the initctl start
and stop
commands, respectively. Jobs are divided into tasks and services.
A task is a job that performs its work and returns to a waiting state when it is done.
A service is a job that does not normally terminate by itself. For example, the logd daemon and the gettys are implemented as services. The init daemon monitors each service, restarting the service if it fails and killing the service when it is stopped manually or by an event.
The /etc/event.d directory holds job definition files (files defining the jobs that the Upstart init daemon runs). Initially this directory is populated by Upstart software packages. With Ubuntu releases following Feisty, installing some services will add a file to this directory to control the service, replacing the files that installing a service had placed in the /etc/rc?.d and /etc/init.d directories.
At its core, the Upstart init daemon is a state machine. It keeps track of the state of jobs and, as events are triggered, tracks jobs as they change states. When init tracks a job from one state to another, it may execute the job’s commands or terminate the job.
The System V init daemon used changes in runlevels to determine when to start and stop processes. Ubuntu systems, which use the Upstart init daemon, have no concept of runlevels. To ease migration from a runlevel-based system to an event-based system, and to provide compatibility with software intended for other distributions, Ubuntu emulates runlevels using Upstart.
The rc? jobs, which are defined by the /etc/event.d/rc? files, run the /etc/init.d/rc script. This script runs the init scripts in /etc/init.d from the links in the /etc/rc?.d directories, emulating the functionality of these links under SysVinit. The rc? jobs run these scripts as the system enters a runlevel; they take no action when the system leaves a runlevel. Upstart implements the runlevel and telinit utilities to provide compatibility with SysVinit systems.
The initctl (init control) utility allows a system administrator working with root privileges to communicate with the Upstart init daemon. This utility can start, stop, and report on jobs. For example, the initctl list
command lists jobs and their state:
$ sudo initctl list logd (stop) waiting rc-default (stop) waiting rc0 (stop) waiting ... tty5 (start) running, process 4720 tty6 (start) running, process 4727
See the initctl man page and the examples in this section for more information. You can give the command initctl help
(no hyphens before help
) to display a list of initctl commands. Alternatively, you can give the command initctl list --help
to display more information about the list
command, or replace list with the initctl command you want more information about. The start, stop, and status utilities are links to initctl that run the initctl commands they are named for.
Jobs
Each file in the /etc/event.d directory defines a job and usually has at least an event and a command. When the event is triggered, init executes the command. This section describes examples of both administrator-defined jobs and jobs installed with the Upstart packages.
The following administrator-defined job uses the exec
keyword to execute a shell command. You can also use this keyword to execute a shell script stored in a file or a binary executable file.
$ cat /etc/event.d/mudat start on runlevel 2 exec echo "Entering multiuser mode on " $(date) > /tmp/mudat.out
This file defines a task: It runs the echo shell command when the system enters multiuser mode (runlevel 2). This command writes a message that includes the time and date to /tmp/mudat.out. The shell uses command substitution to execute the date utility. After this job runs to completion, the mudat task stops and enters a wait state.
In the next example, the cat utility shows the contents of the /tmp/mudat.out file and the initctl list
command reports on this task (the status utility displays the same information):
$ cat /tmp/mudat.out Entering multiuser mode on Tue Jul 10 17:34:39 PDT 2007 $ sudo initctl list mudat mudat (stop) waiting
If the exec
command line contains shell special characters, init executes /bin/sh (a link to dash) and passes the command line to the shell. Otherwise, exec
executes the command line directly. To run multiple shell commands, either use exec
to run a shell script stored in a file or use script...end script
(discussed next).
The Upstart init daemon can monitor only jobs (services) whose programs are executed using exec
. It cannot monitor jobs run using script...end script
. Put another way, services require the use of exec
while tasks can use either method to run a program.
myjob example
You can also define an event and set up a job that is triggered by that event. The myjob job definition file below defines a job that is triggered by the hithere event:
$ cat /etc/event.d/myjob start on hithere script echo "Hi there, here I am!" > /tmp/myjob.out date >> /tmp/myjob.out end script
The myjob file shows another way of executing commands: It includes two command lines between the script
and end script
keywords. These keywords always cause init to execute /bin/sh. These commands write a message and the date to the /tmp/myjob.out file. You can use the emit
initctl command to trigger the job. Following, init displays the stages myjob goes through when you trigger it:
$ sudo initctl emit hithere hithere myjob (start) waiting myjob (start) starting myjob (start) pre-start myjob (start) spawned, process 6064 myjob (start) post-start, (main) process 6064 myjob (start) running, process 6064 myjob (stop) running myjob (stop) stopping myjob (stop) killed myjob (stop) post-stop myjob (stop) waiting $ cat /tmp/myjob.out Hi there, here I am! Sat Jul 7 20:19:13 PDT 2007 $ sudo initctl list myjob myjob (stop) waiting
In the preceding example, cat shows the output that myjob generates and initctl displays the status of the job. You can run the same job with the command initctl start myjob
(or just start myjob
). The initctl start
command is useful when you want to run a job without triggering an event. For example, you can use the command initctl start mudat
to run the mudat job from the previous example without triggering the runlevel 2 event.
Specifying events with arguments
The telinit and shutdown utilities emit runlevel events that include arguments. For example, shutdown emits runlevel 0, and telinit 2 emits runlevel 2. You can match these events within a job definition using the following syntax:
start | stop on event [arg]
where event is an event such as runlevel and arg is an optional argument. To stop a job when the system enters runlevel 2, specify stop on runlevel 2
. You can also specify runlevel [235]
to match runlevels 2, 3, and 5 or runlevel [!2]
to match any runlevel except 2.
Although Upstart ignores additional arguments in an event, additional arguments in an event name within a job definition file must exist in the event. For example, runlevel
(no argument) in a job definition file matches all runlevel events (regardless of arguments) whereas runlevel S arg2
does not match any runlevel event because the runlevel event takes only one argument.
Job definition files in /etc/event.d
As Ubuntu transitions from SysVinit to Upstart init, more jobs will be defined in the /etc/event.d directory. This section describes some of the jobs that the Upstart packages put in this directory.
The /etc/event.d/rc2 job definition file defines the rc2 task, which is similar to the other rc? tasks. The rc2 task is started when the system enters multiuser mode (the event is named runlevel 2); it is stopped when the system enters any runlevel other than runlevel 2 (runlevel [!2]). The first part of the script calls the runlevel utility, which makes the system appear to be in runlevel 2 (there are no real runlevels) and assigns values to two variables. The real work is done by the exec
command, which runs the /etc/init.d/rc script with an argument of 2
. This script calls the links in the /etc/rc?.d directory that correspond to its argument. Thus the rc2 task runs the init scripts that the links in the /etc/rc2.d directory point to.
$ cat /etc/event.d/rc2 # rc2 - runlevel 2 compatibility # # This task runs the old sysv-rc runlevel 2 ("multi-user") scripts. It # is usually started by the telinit compatibility wrapper. start on runlevel 2 stop on runlevel [!2] console output script set $(runlevel --set 2 || true) if [ "$1" != "unknown" ]; then PREVLEVEL=$1 RUNLEVEL=$2 export PREVLEVEL RUNLEVEL fi exec /etc/init.d/rc 2 end script
tty services
Following is the job definition file for the service that starts and monitors the getty process on tty1:
$ cat /etc/event.d/tty1 # tty1 – getty # # This service maintains a getty on tty1 from the point when # the system is started until it is shut down again. start on runlevel 2 start on runlevel 3 start on runlevel 4 start on runlevel 5 stop on runlevel 0 stop on runlevel 1 stop on runlevel 6 r espawn exec /sbin/getty 38400 tty1
This service starts the getty process when any of the events runlevel 2 through runlevel 5 are triggered (i.e., when the system enters multiuser mode) and stops when any of the events runlevel 0, runlevel 1, or runlevel 6 is triggered (i.e., when the system is shut down, enters single-user mode, or is rebooted). The respawn
keyword tells init to restart the job if it terminates, and exec
runs a getty process on tty1 at 38,400 baud. The initctl utility reports that the tty1 service has started and is running as process 4747; ps reports on the process:
$ sudo initctl list tty1 tty1 (start) running, process 4747 $ ps -ef | grep 4747 root 4747 1 0 Jul02 tty1 00:00:00 /sbin/getty 38400 tty1
rc-default task and inittab
Under SysVinit, the initdefault
entry in the /etc/inittab file tells init which runlevel to bring the system to when it comes up. Ubuntu does not include an inittab file and, by default, the Upstart init daemon (using the rc-default task) boots the system to multiuser mode (runlevel 2, the default runlevel). If you want the system to boot to a different runlevel, create an inittab file. The following file causes the system to boot to single-user mode (runlevel S):
$ cat /etc/inittab :id:S:initdefault:
When the system comes up in single-user (recovery) mode, if the root account on the system is unlocked, init requests the root password before displaying the root prompt. Otherwise it displays the root prompt without requesting a password.
Never set the system to boot to runlevel 0 or 6, as it will not come up properly. To boot to multiuser mode (runlevel 2), remove the inittab file if it exists or create the inittab file shown in the example and replace the S
with a 2
.
Copyright 2008 Mark G. Sobell.
Category:
- System Administration