Runlevels What is a runlevel?

When you boot your system, a number of tasks need to be performed before you are able to log on. This "normal" boot operation is fully defined -- it will be the same every time you restart your system. A runlevel is a state in which your system is running and contains a collection of scripts (runlevel scripts or initscripts) that must be executed when you enter or leave a runlevel.

Booting your System

The process that takes care of the runlevels is called init and is also the first process started by the Linux kernel. init's configuration file is called /etc/inittab and gets read immediately after init is started. In this configuration file, the commands used to enter a certain runlevel are listed. For instance, after initialising the system (si), the boot runlevel is started:

si::sysinit:/sbin/rc sysinit
rc::bootwait:/sbin/rc boot

As you can see, init relies on the rc script. When started with the "boot" argument, rc starts all scripts in the /etc/runlevels/boot directory. Don't think about the sequence used to start the scripts -- we will explain later how Gentoo uses dependencies for the init scripts.

When the boot runlevel is completed (the boot runlevel is an intermediate one), init checks what runlevel it should start. If you have not defined one as kernel parameter, it will use the one defined in /etc/inittab:

id:3:initdefault:

In this case, the runlevel id is "3", and gets mapped to:

l3:3:wait:/sbin/rc default

In other words, the rc script is asked to activate the default runlevel. Again, this results in executing all /etc/runlevels/default scripts.

Numbers and Names

When you take a look at /etc/inittab, you will see the following section:

l0:0:wait:/sbin/rc shutdown
l1:S1:wait:/sbin/rc single
l2:2:wait:/sbin/rc nonetwork
l3:3:wait:/sbin/rc default
l4:4:wait:/sbin/rc default
l5:5:wait:/sbin/rc default
l6:6:wait:/sbin/rc reboot

As you can see, there is a mapping of numbers to names (not vice versa). For instance, 0 maps to "shutdown", 1 to "single" etc. Vice versa is not true, as "default" is used by 3, 4 and 5. These numbers are the runlevel numbers. Most distributions work with the numbers; Gentoo however decided to make it a bit more userfriendly and continue with the naming.

As you can see from the listings, Gentoo defines 7 runlevels. Three of them are internal runlevels: sysinit, shutdown and reboot. The sysinit runlevel mounts all necessary filesystems as defined in /etc/fstab. The shutdown runlevel shuts down all running services and powers down the system. The reboot runlevel acts like the shutdown runlevel, but reboots the system instead of powering down.

The other four runlevels are boot, default, nonetwork and single. Each of them has a subdirectory in /etc/runlevels containing scripts that need to be started when the runlevel is activated.

Working with the initscripts

If you take a closer look to /etc/runlevels/default, you will notice that it contains symbolic links to identically named scripts located in /etc/init.d and not just scripts as we mentioned previously. For instance, /etc/runlevels/default/postfix is a symbolic link to /etc/init.d/postfix. In general we can say that such a script provides a service...

Each script in /etc/init.d can be executed with the arguments start, stop, restart, pause, zap, status, ineed, iuse, needsme, usesme or broken.

To start, stop or restart a service (and all depending services), start, stop and restart should be used:

# /etc/init.d/postfix start

If you want to stop a service, but not the services that depend on it, you can use the pause argument:

# /etc/init.d/postfix pause

If you want to see what status a service has (started, stopped, paused, ...) you can use the status argument:

# /etc/init.d/postfix status

If the status information tells you that the service is running, but it doesn't, then you can reset the status information to "stopped" with the zap argument:

# /etc/init.d/postfix zap

To also ask what dependencies the service has, you can use iuse or ineed. With ineed you can see the services that are really necessary for the correct functioning of the service. iuse on the other hand shows the services that can be used by the service, but are not necessary for the correct functioning.

# /etc/init.d/postfix ineed

Similarly, you can ask what services require the service (needsme) or can use it (usesme):

# /etc/init.d/postfix needsme

Finally, you can ask what dependencies the service requires but that are missing:

# /etc/init.d/postfix broken
Working with rc-update What is rc-update?

Gentoo's init system uses a dependency-tree to decide what service needs to be started first. As this is a tedious task that we wouldn't want our users to do manually, we have created tools that ease the administration of the runlevels and init scripts.

With rc-update you can add and remove init scripts to a runlevel. The rc-update tool will then automatically ask the depscan.sh script to rebuild the dependency tree.

Adding and Removing Services

You have already added init scripts to the "default" runlevel during the installation of Gentoo. At that time you might not had a clue what the "default" is for, but now you should. The rc-update script requires a second argument that defines the action: add, del or show.

To add or remove an init script, just give rc-update the add or del argument, followed by the init script and the runlevel. For instance:

# rc-update del postfix default

The rc-update show command will show all the available init scripts and list at which runlevels they will execute:

# rc-update show
Configuring Services Why the Need for Extra Configuration?

Init scripts can be quite complex. It is therefor not really interesting to have the users directly edit the init script, as it would make it more error-prone. It is however important to be able to configure such a service. For instance, you might want to give more options to the service itself.

A second reason to have this configuration outside the init script is to be able to update the init scripts without being afraid that your configuration changes are undone.

The /etc/conf.d Directory

Gentoo provides an easy way to configure such a service: every init script that can be configured has a file in /etc/conf.d. For instance, the apache2 initscript (called /etc/init.d/apache2) has a configuration file called /etc/conf.d/apache2, which can contain the options you want to give to the Apache 2 server when it is started:

APACHE2_OPTS="-D PHP4"

Such a configuration file contains variables and variables alone (just like /etc/make.conf), making it very easy to configure services. It also allows us to provide more information about the variables (as comments).

Writing Init Scripts Do I Have To?

No. Writing an init script is usually not necessary as Gentoo provides ready-to-use init scripts for all provided services. However, you might have installed a service without using Portage, in which case you will most likely have to create an init script.

Do not use the init script provided by the service if it isn't explicitly written for Gentoo: Gentoo's init scripts are not compatible with the init scripts used by other distributions!

Layout

The basic layout of an init script is shown below.

#!/sbin/runscript

depend() {
  (Dependency information)
}

start() {
  (Commands necessary to start the service)
}

stop() {
  (Commands necessary to stop the service)
}

restart() {
  (Commands necessary to restart the service)
}

Any init script requires the start() function to be defined. All other sections are optional.

Dependencies

There are two dependencies you can define: use and need. As we have mentioned before, the need dependency is more strict than the use dependency. Following this dependency type you enter the service you depend on, or the virtual dependency.

A virtual dependency is a dependency that a service provides, but that is not provided solely by that service. Your init script can depend on a system logger, but there are many system loggers available (metalogd, syslog-ng, sysklogd, ...). As you cannot need every single one of them (no sensible system has all these system loggers installed and running) we made sure that all these services provide a virtual dependency.

Let us take a look at the dependency information for the postfix service.

depend() {
  need net
  use logger dns
  provide mta
}

As you can see, the postfix service:

  • requires the (virtual) net dependency (which is provided by, for instance, /etc/init.d/net.eth0)
  • uses the (virtual) logger dependency (which is provided by, for instance, /etc/init.d/syslog-ng)
  • uses the (virtual) dns dependency (which is provided by, for instance, /etc/init.d/named)
  • provides the (virtual) mta dependency (which is common for all mail servers)
Controlling the Order

In some cases you might not require a service, but want your service to be started before (or after) another service if it is available on the system (note the conditional - this is no dependency anymore) and ran in the same runlevel (note the conditional - only services in the same runlevel are involved). You can provide this information using the before or after settings.

As an example we view the settings of the Portmap service:

depend() {
  need net
  before inetd
  before xinetd
}

You can also use the "*" glob to catch all services in the same runlevel, although this isn't adviseable.

depend() {
  before *
}
Standard Functions

Next to the depend() functionality, you also need to define the start() function. This one contains all the commands necessary to initialise your service. It is adviseable to use the ebegin and eend functions to inform the user about what is happening:

start() {
  ebegin "Starting my_service"
  start-stop-daemon --start --quiet --exec /path/to/my_service
  eend $?
}

If you need more examples of the start() function, please read the source code of the available init scripts in your /etc/init.d directory. As for start-stop-daemon, there is an excellent man page available if you need more information:

# man start-stop-daemon

Other functions you can define are: stop() and restart(). You are not obliged to define these functions! Our init system is intelligent enough to fill these functions in herself if you use start-stop-daemon.

Adding Custom Options

If you want your init script to support more options than the ones we have already encountered, you should add the option to the opts variable, and create a function with the same name as the option. For instance, to support an option called restartdelay:

opts="${opts} restartdelay"

restartdelay() {
  stop()
  sleep 3    # Wait 3 seconds before starting again
  start()
}
Service Configuration Variables

You don't have to do anything to support a configuration file in /etc/conf.d: if your init script is executed, the following files are automatically sourced (i.e. the variables are available to use):

  • /etc/conf.d/<your init script>
  • /etc/conf.d/basic
  • /etc/rc.conf

Also, if your init script provides a virtual dependency (such as net), the file associated with that dependency (such as /etc/conf.d/net) will be sourced too.