Monday, May 18, 2009

Init Scripts


If you were to just install the Linux operating system on your hard disk, you would not be able to do very much. What actually makes Linux so useful is all of the extra things which are brought with it. This is essentially true for every operating system.

What makes Linux so useful as well as powerful are all the of services, which are generally referred to as daemons. These daemons typically run without user intervention providing everything from printing to file services to Web pages and beyond. Because they are not part of the operating system proper they are normally loaded separately from the kernel. Although many of these services could be made part of the kernel, they are mostly separate programs. Because they are separate programs something needs to configure and start them.

In most cases, simply installing a particular package is sufficient to activate the appropriate daemon. However, there are times when you need to make changes to how these demons behave, which often means changing the way the program starts up. In order to be able to do that, you obviously need to know just how and where these daemons are started in the first place. That's exactly what we're going to talk about here.

Once the kernel is loaded, one of the last things it does is to start the init process. The job of the init process (or simply init) is to start all of the daemons at the appropriate time. What the appropriate time is depends on a number of different things. For example, you may be performing administrative tasks and you do not want certain daemons to be running. Although you can stop those demons you do not need, the system provides a mechanism to do this automatically.

To understand this mechanism we need to talk about something called "run states" or "run levels". Most users (and many administrators, for that matter) are familiar with only one run level. This is the run level in which the system is performing all of its normal functions. Users can login, submit print jobs, access Web pages, and do everything else one would expect. This run level is commonly referred to as multiuser mode. In contrast, maintenance or single user mode is normally recommended for administrative tasks.

Each run level is referred to by its number. When the system is not doing anything, that is the system is stopped, this is run level 0. Single user mode is run-level 1. Multiuser mode is actually multiple runs levels. Depending on which distribution or which version of Unix you ahve, this can be run-level 2, run-level 3 and run-level 5. Most Linux systems automatically booting into run-level 3 when the system starts. Run level 2 is very similar to run level 3, although a number of things do not run in level 2. In fact, on some systems (SCO UNIX for example), run level 2 is the standard multi-user mode. Run-level 5 is where the GUI starts automatically. (For more details on the run levels, take a look at the init(8) man-page.)

Like many other aspects of the system, init has its own configuration file: /etc/inittab (see the table below). This file contains the init table (inittab), which tells it init what to do and when to do it. Each activity init does is represented by a single line in the inittab, which consists of four entries, separated by a colon. The first field is a unique identifier for that entry, which enables init to keep track of each daemon as it runs. The second field is the run level in which each particular entry is run.

The third entry is the action, which tells init how to behave in regard to this entry. For example, some entries are only processed when the system boots. Others are automatically re-started should that particular process stop (such as terminal logins). The last entry is what program will be started and often a number of options for that program.

If you take look in inittab on your system you may notice something peculiar. More than likely, you are not going to find any entries for the system demons we have been talking about. The reason is quite simply that the daemons are not started through the inittab, but rather through scripts which are started from the inittab. These scripts we see as the entries labeled l0 through l6, for run levels 0 through 6 (the letter "ell", not the number one).

In the example below, the "action" is that init waits until the program has terminated before continuing on and processing other entries for this run level. This also means that the entry will only be processed once as the system enters that particular one level.

The key to all of this is the program which is run for each run level. In every case, it is the shell script rc, which is given the appropriate run level as an argument. This script is often called the "run level master script" as it is responsible for loading all of the other init scripts. Where this script lies and what it is called will be different for different Linux distributions. Under older versions of SuSe it in /etc/rc.d, but now it's in /etc/init.d/. Under Caldera the script resides under /etc/rc.d. Note that starting with version 8.0, SuSe also has an /etc/rc.d directory, which is actually a symbolic link to /sbin/init.d.

Not just the location of the script is different between distributions, but so is the actual code. However, the basic functionality is generally the same. That is, to start other scripts which finally start the daemons we have been talking about all along.

One of the key aspects is how the system determines which daemon to start in which run level. As you might guess, this is accomplished through the run-level that is passed as an argument to the RC script. At least that's part of it. In addition, the system needs a list of which scripts should be started in which run level. This is accomplished not by a text file, but rather by separating the programs or scripts into different directories, one for each run level.

If you look in the /sbin/init.d or /etc/rc.d directory you'll see a number of subdirectories of the form rc#.d, where # is a particular run level. For example, the directory rc3.d is for run level 3. Within the subdirectories are not the actual scripts, as you might have guessed, but rather symbolic links to the actual scripts. The primary reason for this is that a script can be started in more than one run level. If the files were not links, but rather copies, any change would have to be made to every copy. The reason they are symbolic links, is that they may point to files on other file systems which is only possible by using symbolic links.

With SuSe, the /sbin/init.d directory is also where the real scripts reside. On Caldera, the scripts reside under /etc/rc.d/init.d.

At first glance, the filenames may be a little confusing. Although it is fairly simple to figure out what daemon is started by looking at the name, the way these links are named takes a little explanation.

As you might guess, the link ending in "apache" points to the script which starts the Apache Web server.

However, you'll see there are two files with this ending. The really odd thing is that both of these links point to the exact same file. So, what's the deal?

Part of the explanation lies in the first letter of each of these links. As you see, each starts with either the letter S or the letter K. Those which begin with the letter S are used to start the particular service and those which begin with the letter K are used to stop or kill that same service.

That leaves us with just the numbers. These are used to define the order in which the scripts are run. When the files are listed, they automatically appear in numerical order. In this way, the system can ensure the scripts are run in the correct order. For example, you do not want to start the Apache Web server before you start the network. Therefore, the linked used to start the network is S05network whereas the link used to start Apache is S20apache as S05 comes before S20 no matter what comes afterwards.

Note also, the same applies when the system shuts down. K20apache is used to shut down the Apache server and K40network is used to shut down network. As in the first case, the network is not shutdown until after Apache has.

It is interesting to note that this system could work even if the name of the link consisted of just S or K and the appropriate number. That is, it would still work if the link told us nothing of the service being started. There is actually more to it than making things simpler for us non-computers. Having the names at the end allows the system to avoid unnecessary the unnecessary stopping and starting of the various services. When a lower level is entered, only those of services are started which were not started in previous run level. When leaving a run level, the only services that are stopped are those that are not started in the new level.

Let's look at an example. In the directory /etc/init.d/rc3.d (for run level 3), there are links used to both start and stop the network. However, this means the network will always be re-started when moving from run level 1 to run level 3. This also means the network will always be stopped when moving from run level 3 to run level 1. On the other hand, both links exist in rc.2 (for run level 2). Therefore, when leaving either run level 2 or 3 and moving to the other, the network is not stopped as there is a start link for it in the new run level. When entering the new run level, the network is not started, as there was already a start link for the previous level. However, in moving from a run level when network is running (e.g. 2,3 or 5) to run level 1, the network is stopped because there is no link to start the network in run level 1.

We're not done yet.

Since the links to both start and stop a service can be to the exact same file, the script needs some way of knowing whether it should start or stop the service. This is done by passing an argument to the script: start to start the service and stop to stop the service (simple, huh?). Inside each script, this argument is read (typically $1) and different activities are performed based on what the argument was.

Note that for many scripts, you can pass other arguments than just start and stop. For example, one common argument is restart. As its name implies, this is used to stop then start the service again, in other words, restart a running service. Many will also accept the argument status, which is used to deliver status information about that service.

# Default runlevel.
id:3:initdefault:

# System initialization.
si::sysinit:/etc/rc.d/rc.modules default
bw::bootwait:/etc/rc.d/rc.boot

# What to do in single-user mode.

~1:S:wait:/etc/rc.d/rc 1
~~:S:wait:/sbin/sulogin

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

Figure - Excerpts from the /etc/inittab file

On some systems, the scripts we have talked about so far are not the only scripts which are started when the system boots. Remember that init reads the inittab to find out what to do, so there are any number of things that "could" be started through the inittab, as compared to the rc-scripts. Even so, for people who are used to other versions of UNIX, the inittab looks pretty barren.

One type of script that is often run from the inittab deals with system initialization. For example, the boot script, which is found directly in /sbin/init.d. The entry in the inittab might look like this:

si:I:bootwait:/sbin/init.d/boot

The run level this script runs in is "I", which is not a traditional run level, but used by some distributions (i.e. SuSe) to indicate system initialization. However, because the action is bootwait, the run-level field is ignored. Bootwait means that this entry will be processed while the system boots, and init will wait until the command or script has completed.

In this case, the script is /sbin/init.d/boot, which performs basic system initialization such as starting the bdflush daemon (which writes dirty buffers to the disk), checking the filesystems (with fsck), mounting filesystems, starting the kernel module daemon (kerneld), and many other things. Other versions (as well as other UNIX dialects) may have several different entries in the inittab that combine to do the same work as the /sbin/init.d/boot script under SuSe Linux.

The counterpart to the /sbin/init.d/boot script /sbin/init.d/halt. These are the procedures that are carried out when the system is brought down. In general, these are the reverse of the procedures in the boot scripts, such as stopping kerneld and unmounting filesystems.

SuSe also uses the system configuration file /etc/rc.config. This file contains a large number of variables that are used to configure the various services. Reading this file and setting the variables is one of the first things done by the script /sbin/init.d/rc. The counterpart to this file on Caldera is /etc/syconfig/daemons. Instead of a single configuration file, you will find separate files for a number of different daemons.

Creating your own init scripts

Sometimes the scripts your particular distribution provides are not sufficient and you need to add your own. On a number of systems where I have needed to add my own system services, I have needed to create my own init scripts. The method that works on any system is to simply follow the conventions used by your distribution.

SuSe has realized the need for creating your own init scripts, so has provided a template for you. This is the file /sbin/init.d/skeleton and as its name implies, is a "skeleton" init script. In its default state, this is a completely runnable init script. At the same time it is completely useless as there is no daemon behind it. Instead, you simply uncomment the lines you need, change the name of the daemon or service and you are ready to run.

No comments:

Post a Comment