Previous Next

Home Up


Adding items to startup files


You've probably seen the run-level editor RedHat provides in their system configuration tools, and all the services you can toggle off and on from the editor. You've probably also seen the colored OK, PASSED and FAILED indicators during boot. You may, though, be puzzled as to exactly how to get those indicators for your own things in the rc.local script, and how to add your own services to what's available in the run-level editor. Actually it's not all that hard to do either of those things.

Making your commands show boot-time indicators

This is most useful for commands in the rc.local script ( which is located in /etc/rc.d ). This is the script where you can throw site-specific commands that get executed every time you boot the computer, regardless of run-level. It can seem like kind of a kludge after you get used to SysV runlevel entries, but it's still a convenient kludge when you've got something simple that should always be done. Any time you want something done at startup, you just add the commands to do it to rc.local and it'll happen. But none of those commands show the OK/FAILED indicators that the RedHat stuff does. Why? Because you're missing a small wrapper RedHat uses called 'action'.

RedHat provides a standard rc.local script, but I find it lacks a few things. First, since it overwrites the /etc/issue and /etc/issue.net files every time it runs, you can't make permanent changes to the banners shown above the login: prompt just by editing those files. It also lacks the function definitions of other startup files, and runs every time you change runlevels which results in commands incorrectly being executed more often than just at startup, causing problems at times. I prefer a slightly different rc.local framework. This one corrects the deficiencies of RedHat's standard one without being incompatible. Among other things it does subsystem locking to prevent multiple executions when changing runlevels, and sources RedHat's startup functions so they are available.

You'll notice two lines in it that begin with five pound-signs. Between these two lines is where you add your commands to the file, in the order you want them executed. It's a shell script run as root, so feel free to use anything you would in any other script. Let me give you a few examples of ones I use.

First, a couple of convenient ones if you have a VESA monitor that can power itself down under program control. I add these lines to rc.local:

# Set text-mode console VESA blanking interval
echo -e "\033[9;15]" >/dev/console
echo -e "\033[14;30]" >/dev/console
The first echo sets the blanking interval to 15 minutes. After 15 minutes of no activity at the keyboard, if you are at a text console ( as opposed to the graphical X11 screen ) the kernel will blank the screen. The second echo sets the standby powerdown interval to 30 minutes. After 30 minutes of no activity, at a text console the monitor will switch into standby mode, which usually means that the electron guns and beam scanning switch off, leaving the screen dark, but the rest of the monitor's circuitry will remain active to minimize the stress of restarting the monitor. On my Mag DX1795 monitor, standby mode cuts power consumption by 70%. There's an additional suspend mode, which powers off most of the circuitry and reduces power consumption by 90%, but I consider the extra 20% savings to be not worth the loss in operating life caused by the extra stress of powering up the monitor's coils and high-voltage circuitry each time. Any keyboard activity will turn the monitor back on and unblank the screen regardless of which mode it has been set to.

Another one I use sets the keyboard repeat rate and delay. I use 24 repeats per second and a 500ms delay before repeating starts. This is also an example of how to get the OK/FAILED indicators on the screen during boot:

# Set keyboard rate
action "Setting keyboard rate" kbdrate -r 24 -d 500
Note the use of the action function here. It's first argument is the string to display on the screen during boot, and the remainder of the line is the command to execute. If the command returns a 0 status ( the canonical success indication ) the green OK will print on the screen during boot. If it returns a non-zero status, a red FAILED will be printed. The results will also be logged into the system log, along with any output produced by the command including error messages. You can then review /var/log/messages for details if things went wrong.

If you are using the firewall scripts I describe elsewhere, you can use these lines to execute the scripts:

#echo Setting up firewall
action "Setting up boot-time firewall" /etc/rc.d/rc.iprules-boot
action "Setting up firewall rules" /etc/rc.d/rc.iprules
These will always succeed due to the way they are written, but error messages from them will end up in the system log nonetheless so if the firewall doesn't work you can see what happened.

rc.local is a nice place to put items that just set parameters or otherwise modify the system without starting processes running in the background. You can certainly start server processes from here, but it's often more elegant and flexible, if a bit more work, to actually add a script to start them to the standard runlevel setup.

Adding items to the run-level editor

Adding an entry to the set of items available through the runlevel editor is mostly a matter of creating a script in /etc/rc.d/init.d following a basic template, testing that it works correctly and then using the chkconfig tool to make the symlinks in the /etc/rc.d/rc.N directories to actually have the script run during startup. The script provides 4 basic functions, to stop, start, restart and check the status of the process, and optionally a fifth, to reload the process's databases without completely restarting it. Writing this script isn't too hard, but you do need to be reasonably knowledgeable about shell scripting. To be honest, your best reference on how to create this script isn't the template, but the actual scripts in /etc/rc.d/init.d for existing server processes. Once you know the basic structure, browse them to find one close to what you need and edit it appropriately. You won't need to use the action command in them, oddly, because the entire script is run from the equivalent of an action line by the global startup scripts.

First, let's look at the template script in a bit of detail to see how it's basically put together.

#!/bin/sh
#
# Startup script for XYZZY
#
# chkconfig: 345 STARTORDER STOPORDER
# description: 
# processname: XYZZY
# pidfile: /var/run/XYZZY.pid
The XYZZY stands in for the the name of the program being run. For the Apache Web server, for example, it would be httpd. You'll need to substitute the executable name wherever you see XYZZY in this template.

The chkconfig line specifies which runlevels the process should be started in by default, and the place in the startup order it should occupy. The 345 means that the process normally starts in runlevels 3, 4 and 5, which is usually correct. The other common one is 2345, adding runlevel 2 to the list. 3 is the normal text-mode startup, 5 is the XDM/GDM graphical login with the X server automatically started. 2 is a level similar to 3 but with network filesystems not mounted and some or all network services not available. The intent is to provide a runlevel you can use safely when the machine isn't connected to the network, but most people never actually use it. The STARTORDER and STOPORDER strings are replaced by 2-digit numbers indicating the position in the startup sequence in which the script should be run. Higher start orders are started later, and the stop order is usually complementary to the start order, eg. if the start order was 85 ( late in the process ) then the stop order would be 15 ( stopped early in the shutdown process ). The usual formula is STOPORDER = 100 - STARTORDER. You can look at the contents of the /etc/rc.d/rc.3 and /etc/rc.d/rc.5 directories, which contain the links for processes started in the two most common runlevels, to see what is started when normally, then pick an unused start order near the point you want your process started and use that. You can re-use an existing start order number, but I normally try to avoid this to keep things clean. Substitute the numbers you chose for the STARTORDER and STOPORDER in the script. This insures that the chkconfig command and other utilities can find out these values.

You can then add a description of what the server is, and note the file under which it will store the PID of it's running instance, if any. Some servers don't store a PID, in which case you can omit the pidfile line. The one given here mentions the canonical name of the file, which you should try to use if you're going to have PID file at all.

# Source function library.
. /etc/rc.d/init.d/functions
This reads in RedHat's startup functions. Interesting reading if you know shell scripting fairly well, brain-bending otherwise.

Now we get to the meat of the template: the sections that actually start and stop the process.

# See how we were called.
case "$1" in
  start)
	echo -n "Starting XYZZY: "
        ##### Commands to start the process running
	echo
	touch /var/lock/subsys/XYZZY
	;;
The comment marks the spot where you actually insert the commands to start the process running. The most common command used here would be "daemon XYZZY args if any" to fork the server process off as a background daemon. This assumes that the program will create it's own PID file, which usually means it will either use /var/run by default or has an option to specify the location or exact name of the PID file that you can include on the command line to force it to the correct place. You can use more complex sets of commands, but ultimately they should result in the server process running in the background and execution of this script continuing. The last line creates the subsystem lock file that tells the startup system the process is running and should not be restarted if you switch to a runlevel that says to start it and should be killed if you switch to a runlevel that says it should not be running.

  stop)
	echo -n "Shutting down XYZZY: "
	killproc XYZZY
	echo
	rm -f /var/lock/subsys/XYZZY
	rm -f /var/run/XYZZY.pid
	;;
This section stops a running process. The killproc line is again fairly typical, taking the name of the program and stopping all running instances of it by sending them the standard KILL signal. Some processes need other commands to shut them down, which you should use here in preference to killproc. Basically whatever commands go here should cleanly shut down the server process. It then removes first the subsystem lock file for the process so the startup system knows it's no longer running, then removes the PID file. You can remove the line about the PID file if the process doesn't create one.

  status)
	status XYZZY
	;;
  restart)
	$0 stop
	$0 start
	;;
  *)
	echo "Usage: $0 {start|stop|restart|status}"
	exit 1
esac

exit 0
The status and restart sections are relatively standard for all processes, so you probably won't need to tweak those any.

The script, once finished, goes as /etc/rc.d/init.d/XYZZY and should be owned by root and be executable. Be careful not to overwrite an existing script when you save it. You can run the script with the start argument to manually start the server for testing and such, or with the stop argument to shut it down manually. The restart argument will stop and then start the server, and is just a shorthand for seperate stop and start steps.

Probably it would be useful to look at a couple of real example scripts at this point, to see how the template actually gets implemented in the real world. The first will be the Apache httpd script. This one is started in runlevels 3, 4 and 5, started late in the process with a start order of 85 and shut down early with a stop order of 15. The only exceptional thing in it you haven't seen is the reload stanza near the bottom. This is often added to scripts for processes that, upon receipt of a signal, reinitialize themselves without shutting down and reload all their configuration. The Apache Web server does this when it receives the HUP signal, so a reload stanza sending it that signal is included.

A different example would be the distributed.net script that I use to start up the RC5 cracking client on my system. This one has a fair number of differences from the Apache one. It starts in runlevels 2, 3, 4 and 5, with a start order of 99 making it the last thing started and a stop order of 01 making it the first thing shut down. The start section shows a radical departure. First, I actually cd to a directory under a user's home directory ( mine in this case ) and use su to run the client as me instead of root. This process won't create a PID file, so no mention of one is made. The client, though, forks itself off as a daemon, so there is no need to use the daemon command. In the stop section, again I cd to the directory under my home directory where the client lives. Then instead of killproc I use the option to the client that causes it to shut itself down cleanly. In the status section, I use the name of the client program and not the name of the script to make the status function work properly.

Once you've written the script and put it into place, manually run it to test it, starting and stopping the server process and making sure all is working well. Once you are satisfied, you can make the links to have it started during startup by doing chkconfig --level 0123456 XYZZY reset which will reset all the links in the /etc/rc.d/rc.N directories to their default states. You can use off in place of reset to set the process to be shut down in all runlevels, preventing it from running at all, if you decide you don't want it started automatically. chkconfig will use the chkconfig line from the script to determine which runlevels to start the process in and what order numbers to give it. I always use the --level argument to chkconfig to make it set links appropriately in all runlevels. Note that this does not, in the case of the reset function, control whether it will be started or stopped, merely which runlevels will have their links set to match what's specified on the chkconfig line in the script.

The names used for the links are relatively simple, if you're interested. Each runlevel N has a directory /etc/rc.d/rc.N which contains links for everything that should be started or stopped when switching into that runlevel. For a process XYZZY, the link causing it to be started is named SxxXYZZY, where the xx is the start order number of the process. The matching link causing it to be stopped is KyyXYZZY, where the yy is the stop order of the process. The startup scripts run all K scripts for processes that currently have lock files in /var/lock/subsys, smallest stop level to largest, and then run all the S scripts for processes that do not have lock files in /var/lock/subsys, smallest start order to largest.

The whole startup script setup here is called the SysV init scripts setup after the version of Unix which introduced it, AT&T System V Unix. It is somewhat more convoluted than the all-in-one-script method typical of early Unixes and seen in rc.local, but at the same time it allows you to package the details of each server process up in a script and then maintain the startup configuration via tools like the runlevel editor and chkconfig, instead of having to manually edit a large script every time you want to make a change and having to use some moderately hairy script boilerplate to insure that processes aren't started twice or stopped when they aren't running. It's also not specific to RedHat Linux, it can be found in other Linux distributions and in other Unixes than Linux. Although it's more work to set up and create the proper scripts for, it works well because of one concept: you switch processes off and on much more often than you create entirely new ones. You need to put more effort into the creation of the scripts, but that's a one-time investment up front. The payback is that it's significantly easier to maintain things in normal use, which is an ongoing thing. You can think of it as analogous to buying a car or a house: you trade off a higher down-payment up front for a lower monthly payment over the life of the loan.


Previous Next

Home Up


tknarr@silverglass.org