Cover V04, I05
Article
Listing 1
Listing 2
Listing 3

sep95.tar


Understanding Run Levels

Emmett Dulaney

UNIX systems, as well as BSD flavors, have only two run levels -- single user and multiple user. Under SV, run levels have become to a machine what permissions are to a user. Operating at one run level restricts a machine from performing certain tasks (such as networking), while running at another enables these functions. Which functions are enabled at each run level has changed over the years (for example, run level 1 used to be equivalent to multi-user state on many older machines), but under current definitions, there are eight accepted run levels:

0 -- Shutdown state requiring a manual reboot. When you change to this level, files are synchronized with the disk and the system is left in a state where it is safe to power it off. This level is also known on some systems as "Q" or "q".

Q or q -- On systems where these are not equal to zero, they force the system to reread the inittab files and take into account any changes that have occurred since last boot.

1 -- Single user mode, also known as "S" or "s" mode on many systems. This allows only one user (traditionally the root user) to access the system and prevents anyone else from getting in. Additionally, it allows only one terminal to login. If the change is to level 1, then the one terminal allowed to login is the one defined as the console (and this command can be given only at the console). If the change is to S/s, then the only terminal allowed to log in is the one that changed the run level. This is the level used when performing kernel rebuilds, installing software that rebuilds the kernel, performing troubleshooting, etc.

2 -- Multiple user mode, the traditional state allowing more than one user to login at a time. This is the level where background processes (daemons) startup and additional file systems, if present, are mounted. (Note: the root file system is always mounted.)

3 -- Network mode. This is the same as level 2, but with networking or remote file sharing enabled.

4 -- User defined.

5 -- Hardware state.

6 -- Shutdown and automatic reboot. This performs the same action as changing to run level 0 then rebooting the machine. This can be called a "warm boot" (in the language of the DOS world), because power is never removed from the components. Run levels 0 and 5 represent a "cold boot," since power must be turned off and then restored.

An easy way to summarize the run levels is that 2, 3, and 4 are operational states of the computer -- it is up and running and users are allowed to conduct business. All other run levels involve some sort of maintenance or shutdown operation that prevents users from processing.

Identifying and Changing Run Levels

The -r parameter of the who command gives you your machine's current run level as well as the two most recent previous run levels. For example,

$ who -r
.   run level 2  May 4 10:07	2	1	0
$

shows that the current run level is 2 and that that has been the run level since May 4th at 10:07. The three numbers to the right show the current run level, the previous run level, and the next previous run level on some systems. On other systems, the three numbers represent the process termination status, process id, and process exit status.

Changing levels requires root permission and can be done with either the init or shutdown command. Depending upon your vendor and system, the init utility is located in either /etc or /sbin. The shutdown command, is traditionally in /usr/sbin. The init utility is very simple: it allows you to specify a number and the machine then changes to that run level. Thus

# init 3

would immediately change the machine to run level 3.

The shutdown command interacts with init and offers substantially more parameters and options. A -g option allows you to specify a grace period of seconds to use before beginning the operation (the default is 60), -i signifies which run level you want to go to, and -y carries out the action without asking for additional confirmation. To change to run level 3 in 15 seconds, the command would be:

# shutdown -g15 -i3 -y

Once the command is typed, a warning message is broadcast telling users that the run level is changing (this is true with init as well). The system then waits the specified number of seconds -- allowing users to save files and log off -- before making the change. By contrast, init, would tell users the run level is changing and would immediately begin changing it without giving the users time to protect their work.

Actions Defined in inittab

The inittab file, located in /etc or /sbin, is an initialization table. It contains a list (or table) of the actions that take place when a machine changes to a different run level. Listing 1 shows a section of an inittab file from an SCO system. The line numbers to the left have been added to aid this discussion. Each entry is a colon-delimited field consisting of four entries:

id:rstate:action:process

id is the identification of the process, usually only two or three letters in length -- one to four is the allowable range.

rstate is the required state, or run level, necessary for the process to execute.

action is a keyword telling init what to do with the process.

process is the actual command to be carried out.

Line 3 of Listing 1 is extremely important: it is the initdefault line. In this example, each time the system is rebooted, it will attempt to change to an initial state of S -- single user. It could just as easily be 2 -- multi-user. The file is plain text, so to make the system boot to a different level, you can use any editor to change this line. The inittab file is not required for the operating system to boot -- it is only a convenience. On most systems, if the file is erased or damaged, the system will boot to single user, and any attempt to change to another state can cause it to hang. On other systems, if the entry or file is missing, a prompt will ask the console what the run level should be.

Line 3 of Listing 1 is the only place where the action field is defined as initdefault. Other actions that can be specified are:

boot -- The entry is processed only on bootup and is not restarted if it dies. init does not wait for it to complete running before continuing on to the next command, and can run many of these simultaneously. This action is rarely used.

bootwait -- The entry is processed only on bootup; init waits for it to finish running and then die before continuing. It does not restart the process once it finishes or dies off. Line 2 of Listing 1 uses this option with a utility to mount and check file systems.

off -- If the process is currently running, a warning signal is sent, and after 20 seconds the process is killed with a kill -9 command. Line 16 shows that when the run level is changed to 2 (multiple user) terminal 1 is killed. This has the effect of logging that user off; it adds a level of security that protects against the possibility of the root user changing run levels and walking away from the terminal, thus allowing access to anyone who happens to sit there.

once -- When the specified run level is reached, the process is started. init does not wait for the process to terminate before continuing on, and does not restart it if it dies. Like boot, this option is not used very often.

ondemand -- This option has the same meaning as respawn but is used mostly with a, b, and c levels (user defined) (see respawn for more information).

powerfail -- The action is triggered only when a power failure is at hand. A signal 19 is the most common indication of a power failure. A powerfail typically calls a sync operation.

powerwait -- When a power failure occurs, this process is run, and init waits until the processing finishes before processing anymore commands. Again, sync operations are the main purpose of the action.

respawn -- If the process dies after it has been started, it gets restarted. init, does not wait for it to complete execution before continuing on to other commands. As lines 8-15 in Listing 1 show, respawn is associated with terminals; once a terminal is killed, you want it to respawn and allow another login.

syncinit -- This option is not available on all systems. It tells init to reset the default sync interval, that is, the number of seconds until modified memory disk buffers are written to the physical disk. Default time is 300 seconds, but the interval can be set to anything between 15 and 900.

sysinit -- Before init tries to access the console, it must run this entry, which is usually reserved for devices that must be initialized before run levels are determined. Line 1 in Listing 1 shows that the Trusted Computing Base, which is used for user login and authentication, is initialized even before the console is made active, allowing any user to log in.

wait -- This option starts the process at the specified run level and causes init to wait until the process completes before moving on. This option is associated with scripts that perform the run level changes. Lines 4-7 in Listing 1 use this option for every run level change.

Line 2 of Listing 1 shows that the same processes will be carried out for run level changes to 2 or 3. Similarly, line 4 shows that the same processes will be carried out for changes to run levels 0, 5, or 6. The process carried out by line 4 is a shell command script in the /etc directory called rc0.

Listing 2 shows several key routines from the rc0 script of an SCO machine. I chose to use SCO because it illustrates nicely the steps that are carried out. I added line numbers to the file for purposes of this discussion, and then stipped away those lines that were not pertinent.

Lines 24 and 81 of Listing 2 work together, first saving the terminal settings and then restoring them after completing other processes. Line 26 echoes that the system is coming down. Line 42 is where things get interesting: a test is performed to see if there is a directory called /etc/rc0.d. If there is, then any file within that directory that begins with a "K" is executed (lines 44-50). Next any file within that directory beginning with an "S" gets executed (lines 54-60). Line 74 effectively ignores the standard kill signal by mapping it to nothing -- theoretically, up until this point, it is still possible to stop the shutdown. A killall is performed to kill all processes with a signal of 9, then three sync operations are performed. Finally, drives are unmounted (line 80), two more syncs are done (line 82), and a message is echoed that the system is down. One last sync rounds out the change to run level 0.

Similar scripts exist under the names of rc1, rc2, and rc3. Each performs similar operations. For example, rc1 checks to see if there is an rc1.d subdirectory. If there is, it executes any scripts within that subdirectory starting with "K", that any scripts within that subdirectory starting with "S".

About K and S Scripts

On older systems, all executable scripts are kept in a single directory, with each script combining the S* and K* action (hard links point to the relevant script). On newer systems, script files within each rcx.d subdirectory that begin with a "K" kill a process that is already running; those that begin with an "S" start a process that is not yet running. The names of these scripts typically begin with the "K" or "S", followed by a two-digit number and a name for the operation. Listing 3 shows a listing of the rc0.d and rc2.d subdirectories. The two digit number has no special significance, and can fall anywhere between 00 and 99. The numbers need not increment in any certain order, and a newly created script can take any number not already in use within that directory. The only thing to bear in mind is the scripts are called for execution with "K*" and "S*"; thus they are executed in numerical order with 00 (providing it exists) executed first and 99 executed last.

It is relatively simple to understand what most of the scripts do, since to their names are descriptive. The files displayed in Listing 3 have the following consequences:

K00ANNOUNCE -- simply echoes "System services are now being stopped."

K70uucp -- cleans up miscellaneous uucp locks (/usr/spool/uucp/LCK.*)

K75cron -- links to S75cron. Depending upon which way it is called, it will either kill the cron pid, or start /etc/cron the same script is capable of doing both.

K80lp -- links to S80lp. It will either summon /usr/lib/lpshut or start /usr/lib/lpsched. If a printer lock exists (/usr/spool/lp/SCHEDLOCK), it also removes that.

K86mmdf -- links to S86mmdf. Depending upon how it is called, it will either start the mmdf daemon or kill its associated pid. Like several of these scripts, K86mmdf uses the following syntax to find the pid:

pid=`/bin/su root -c "/bin/ps -e" | grep whatever | sed -e 's/^ *//' -e 's/.*//'

S00SYSINIT -- runs additional scripts in /etc/rc.d/0 and /etc/rc.d/1. This is used only for historical purposes and to maintain compatibility with older UNIX\XENIX versions.

S01MOUNTFSYS -- performs a mountall to bring up the filesystems and initiate auditing.

S03RECOVERY -- provides crash recovery after a boot.

S04CLEAN -- removes temporary and lock files. This is where /tmp gets emptied out.

S05RMTMPFILES -- cleans up any other existing temporary files.

S15HWDNLOAD -- performs a hardware download.

S16KERNINIT -- performs a kernel initialization.

S20sysetup -- prints system id information, if it exists; creates the information by sending the output of uname -n to /etc/systemid.

S21perf -- fires up the administrative utility sadc.

S87USRDAEMON -- starts up the user daemons.

S88USRDEFINE -- provides a location where system specific routines can be placed.

S90RESERVED -- sets several variables, and mails information about the boot to the root user's mail file.

You can start and stop additional processes when you change to one of the already defined run levels simply by creating a shell script, preceding it with "K" or "S", and placing it in the appropriate rcx.d subdirectory.

Creating a Run Level

Using the information presented here, you can easily create your own run level. Run level 4 is, by default, not used by the UNIX operating system. As mentioned earlier, you can use it to customize your system. To do so follow these four steps:

1. Create an entry in /etc/inittab. It should resemble the following:

r4:4:wait:/etc/rc4 1> /dev/console 2>&1 </dev/console

2. Create a script in /etc called rc4 by copying rc0 to rc4, then changing all references from rc0.d to rc4.d. The only references should be as shown in Listing 1 on lines 42, 44, and 54. Comment out lines 26, 76-80, 82, and 85. Then change the echo statement on line 84 to say "Change to Run Level 4 completed."

3. Make an rc4.d subdirectory below /etc.

4. Within /etc/rc4.d create your command scripts. If, for example, you want to start a database process when you change to this run level, call the script S00DATABASE, and place the command to start it within that script. Then create a corresponding "K" script to close the database when you change from that run level.

Significant Files

Several files maintain run level information. /etc/default/boot is an SCO-specific file that maintains information on the console keyboard. /etc/wtmp and /etc/utmp work as opposite log files to each other. /etc/wtmp keeps a record of all processes that are spawned, while /etc/utmp keeps a record of all processes that have died. These log files do tend to grow and should be routinely trimmed.

Summary

Changing run levels simply entails calling an additional set of scripts. Those scripts can either start processes that are not running or kill processes that are. Run levels 2 and 3 are operational states allowing users to interact with the operating system. Run levels 0, 1, 5, and 6 involve downing the computer or performing maintenance operations. Run level 4 is available for administrator definition and can easily be used to stop and start additional processes.

About the Author

Emmett Dulaney is a product developer for New Riders Publishing and co-author of Inside TCP/IP. He can be reached on CompuServe at 74507,3713.