Image of the glider from the Game of Life by John Conway
Skip to content

Managing Services in Ubuntu, Part II: Managing Runlevels

In my last post, I gave you a glimpse to runlevels, their purpose, and how they affect the overall operation of your box. Today, in this post, I am going to dig deeper into runlevels, as well as manipulating services in specific runlevels. I'm going to show you why you want to do this, as well as how. We'll learn about the 'update-rc.d' command mainly, spending a great deal of time with examples and demos.

Before I begin, however, I want to make something painfully clear. Linux gives you the awesome power to completely break your box. I'm talking to the point where not even a backup would work, and you're left with the task of reinstalling the operating system. However, on the flip side, Linux gives you the awesome power to manage and administer your box. Intense flexibility and unbelievable portability. Today, if you're following along with the examples given in this tutorial, it is possible to leave your box in an unbootable state, or in a state of undesired operation. Of course, this is not what we want. So, if you are going to follow along, I would recommend following along in an Ubuntu virtual machine. Otherwise, take extra care to follow the tutorial exactly, and all should be well.

Ok, in my last post, I talked about what a runlevel is and what it does to your box. However, I never really mentioned why you should care, or how to to manage a specific runlevel. I plan on discussing those items here. First, is why you should care about runlevels. I've seen the argument that runlevels add nothing to your box, and managing services is trivial, so runlevels just add an extra layer of complexity. I disagree. For me, runlevels add a layer of dependability. The whole idea of runlevels is to maintain a level of predictability with your box. I should be able, at any given time during the day, predict what is running on my box. If not, then I've given up a level of security. By not knowing how my box is performing, or what it is executing, I've opened a door for potential vulnerability. However, if I can predict the state of my box, regardless of time of day, stress it's under or number of users logged in, then I've maintained a level of security and dependability. As such, I've done my job well as a system administrator. Runlevels give me that predictability, as well as flexibility in the state I want my box operating under.

As a review, let's look at the runlevels in Ubuntu:

0: Halt / Shut down.  All processes are stopped, all filesystems are unmounted, and all users taken offline.  Power can now be safely removed.
1: Single user mode.  Only root allowed login (requires password).  All filesystems listed in /etc/fstab are mounted.  Limited set of services started.
2-5: Multi-user mode.  All filesystems listed in /etc/fstab mounted, graphical environment (if installed) brought up.  Full standard operational mode.
6: Reboot.  As with runlevel 0, all processes stopped, all filesystems unmounted, all users taken offline, the box rebooted, and taken to the default runlevel.

If you did your homework in the last post, you probably changed your default runlevel in your /etc/event.d/rc-default file. If you changed this to runlevel 6, you probably noticed a problem: your box infinitely rebooted. Unfortunately, I did not give you a way out in the last post. I'll do that now.

When booting the box, you can bypass the defined default runlevel by passing 's' or 'S' as a kernel argument in GRUB. When your box is booting, press the [Esc] key before the timeout ends, and you'll be taken to the GRUB menu. You'll have the ability to use your arrow keys to highlight an entry. From there, there is a prompt at the bottom of the menu telling you what you can do to that entry that is highlighted. For example, you can press the 'e' key to edit that line. Go ahead, and do that to one of the highlighted entries. You'll notice that it takes you to a new menu, showing an initrd (initial ramdisk, used by the kernel to load some necessary drivers for the system), the kernel itself, and a memory test for your RAM. Select the kernel entry, and hit the 'e' key again to edit the kernel. At the end of the line, enter 's' or 'S', and press enter. Press the 'b' key to boot the kernel now, and you will boot into an sulogin prompt. If you enabled the root account, which is disabled by default on an Ubuntu box, you will be prompted for the root password, WHICH IS IN PLAIN TEXT ECHOED TO THE SCREEN (it's a bug, we're working on it). If the root account is not enabled, then you'll be thrown to a root prompt, at which you can edit your /etc/event.d/rc-default file to change the default runlevel to the appropriate entry.

Woah. Wait a minute. You mean, all I have to do is enter an 's' at the kernel line in GRUB, and I get root access to the machine?! Yes. Any box that is physically available can be easily compromised, even with harddrive disk encryption. So, considering the physical security of your box is something that you'll obviously want to take into account as a system administrator. However, we're off topic a bit, so let's get back on target.

Runlevels are nothing more than your box running in a specific state. Fortunately, we can change the desired state that we want our box to be running in by adjusting the runlevel. This is done with the update-rc.d command.

At this point, let's play with a package for this tutorial. I've chosen sendmail as our culprit, so if you don't already have it installed, run the following command below. If you do have sendmail installed, and would rather use another service package, go for it. We'll use sendmail in this post:

aaron@kratos:~ 1354 % sudo aptitude -R install sendmail

In my last post, sendmail was one of the services on my box that was starting when I enter runlevel 2. Maybe I don't want sendmail to start, or rather, if it is already started, when I enter runlevel 2, I want to kill it. In other words, I don't want it running for runlevel 2. How can I make this change?

Well, first, I could just delete the soft link from the runlevel directory /etc/rc2.d/:

aaron@kratos:~ 1355 % sudo rm /etc/rc2.d/S21sendmail

That would definitely keep it from starting when I enter runlevel 2, but what if I wanted to kill it if it was already started from a previous runlevel? Just deleting the soft link won't do it. I need to turn it into a K-script. Further, deleting and creating soft links in my /etc/rc[0-6].d/ directories by hand is a bit of a pain. This is where the update-rc.d command comes in:

aaron@kratos:~ 1356 % sudo update-rc.d -f sendmail remove
 Removing any system startup links for /etc/init.d/sendmail ...
aaron@kratos:~ 1357 % sudo update-rc.d sendmail stop 20 0 1 2 3 4 5 6 . 
 Adding system startup for /etc/init.d/sendmail ...
   /etc/rc0.d/K20sendmail -> ../init.d/sendmail
   /etc/rc1.d/K20sendmail -> ../init.d/sendmail
   /etc/rc2.d/K20sendmail -> ../init.d/sendmail
   /etc/rc3.d/K20sendmail -> ../init.d/sendmail
   /etc/rc4.d/K20sendmail -> ../init.d/sendmail
   /etc/rc5.d/K20sendmail -> ../init.d/sendmail
   /etc/rc6.d/K20sendmail -> ../init.d/sendmail

We'll get into the syntax a bit later, but these commands, as observed, removed any existing soft links that previously existed, and created new K-scripts for all of my runlevels.

First off, why two separate commands? Well, by Debian policy, no package upgrade will ever overwrite a previous configuration. This includes updating soft links in the runlevel directories. This also ensures persistent changes and allows the system administrator to prevent daemons from launching. So, if soft links already exist, they first need to be removed, then new links created.

Second, you'll notice that I need to specifically state when I want the service to start and when I want it to stop during the runlevel process. This is intentional, and offers a great deal of flexibility to the system administrator. Remember, that all the soft links either start with an S for starting the service, or a K for stopping (killing) the service. Each soft link is processed in alphanumeric order, so there is a number following the K or S telling the order in which I want the scripts to execute.

There is something to be said about logic behind the order of when you are starting and stopping services. For example, you may want to restore your firewall before enabling your network device. Further, you may want your network brought up before you begin starting services. Ultimately, the choice is up to you.

Let's continue with managing our runlevels, and learn the syntax of the update-rc.d command. There are four possible ways in which I can use the update-rc.d command. Let's start with the first- defaults. As our symlinks already exist, we'll need to remove them:

aaron@kratos:~ 1358 % sudo update-rc.d -f sendmail remove
[... snip ...]
aaron@kratos:~ 1359 % sudo update-rc.d sendmail defaults
[... snip ...]

(I'll be snipping some output from here on out). As we can see, the defaults for sendmail were to stop it at position 20 for runlevels 0, 1 and 6, and to start it at position 20 for runlevels 2 through 5. Actually, position 20 is the default for any package, regardless. This means apache, postfix, squid, ssh, etc. This may not be what we want. Maybe I want to affect sendmail at a different position.

The 2nd possible syntax of the update-rc.d command to to change that default position to another number for all runlevels. Remember to remove our soft links first, then create new ones:

aaron@kratos:~ 1360 % sudo update-rc.d -f sendmail remove
[... snip ...]
aaron@kratos:~ 1361 % sudo update-rc.d sendmail defaults 10
[... snip ...]

As we can see, I was able to change the position of stopping and starting sendmail from 20 to 10. This affected all runlevels 0 through 6. However, I may not want to affect all runlevels the same. Maybe I want to treat runlevels 0, 1 and 6 differently than 2 through 5.

Which brings us to our 3rd possible syntax for the update-rc.d command- using the default runlevels, but changing when it's stopped and started. Again, let's remove our symlinks, and create new ones:

aaron@kratos:~ 1362 % sudo update-rc.d -f sendmail remove
[... snip ...]
aaron@kratos:~ 1363 % sudo update-rc.d sendmail defaults 10 21
[... snip ...]

As we can see, the default runlevels 0, 1 and 6 were changed to stop sendmail at position 21, and the default runlevels 2 through 5 were changed to start sendmail at position 10. So, I can change the starting and stopping position using the default runlevels. Cool.

Finally, let's look at our last usage of the update-rc.d command: full flexibility of when I start and stop the service in each individual runlevel. I bet you haven't forgotten to remove your soft links before creating new ones, have you:

aaron@kratos:~ 1364 % sudo update-rc.d -f sendmail remove
[... snip ...]
aaron@kratos:~ 1365 % sudo update-rc.d sendmail start 10 2 3 . start 20 4 5 . stop 19 0 1 6 .
[... snip ...]

Here, as we can see, I affected the starting position for sendmail in runlevels 2 and 3 to be 10. For runlevels 4 and 5, I wanted sendmail to be at position 20. Finally, for runlevels 0, 1 and 6, I wanted to kill sendmail at position 19. You'll also notice that the syntax is fairly straight-forward:

update-rc.d [service] start|stop [position] [runlevel] [runlevel] [runlevel] ... [.]

The first argument, as has been consistent up to this point, is the service. Then, I mention whether or not I want to create K-scripts or S-scripts by calling either the start or stop option. Both start and stop have a set of arguments, the first being the position I'm going to affect. Then, following the position, I give a space-separated list of runlevels followed by a '.' (dot) to terminate either that start or stop call.

Let's look at a couple more examples of this 4th syntax, so we can get a better feel for it:

aaron@kratos:~ 1366 % sudo update-rc.d -f sendmail remove
[... snip ...]
aaron@kratos:~ 1367 % sudo update-rc.d sendmail start 45  2 3 4 5 . stop 1 0 1 6 .
[... snip ...]

The above example starts sendmail at position 45 for runlevels 2 through 5 and stops sendmail at position 1 for runlevels 0, 1 and 6. I should probably point out, that you most likely don't want to start a service for runlevels 0 and 6, for obvious reasons. One more example:

aaron@kratos:~ 1368 % sudo update-rc.d -f sendmail remove
[... snip ...]
aaron@kratos:~ 1369 % sudo update-rc.d sendmail stop 0 0 1 2 3 4 5 6 .
[... snip ...]

In that example, I set sendmail to stop at position 0 for all runlevels, thus effectively disabling the service, regardless of the state of my box. If I wanted sendmail to start, I would need to call it directly:

aaron@kratos:~ 1370 % sudo /etc/init.d/sendmail start
 * Starting Mail Transport Agent (MTA) sendmail                          [ OK ] 

One final note about managing runlevels. All this is fine and dandy with the update-rc.d command, however, we're just removing and creating soft links. We are not affecting the running process at all. In other words, just because I've configured sendmail to start in runlevels 2-5 doesn't mean that I've started the service. If sendmail is not running, I will still have to start it by hand. Same goes for creating K-scripts in those directories. The service won't be stopped because I have created those K-scripts. I'll still have to stop it by hand. All we're doing is affecting if it starts or stops when I enter a new runlevel.

There is some philosophy about when to start and stop services, and definitely some logic behind the order. However, the position at when to start and stop these services is entirely up to you. If you find that you're starting a service too early (or too late), change the soft links. It may be of interest to you to start your X display manager first, before starting services. Now that you're familiar with the update-rc.d command, you know how to do this (this approach may have some consequences as some services, such as xfs or gdm may provide dependency to X).

At this point, we'll stop here with this post in the series, and pick up again tomorrow with more service management. If you followed both of these posts, you should be fairly comfortable with runlevels, and managing when to start and stop services in specific runlevels. You're turning into a system adminstrator.

{ 12 } Comments