home   articles   archive   forum   masthead  
Published at 12.10.2001
Author: Ronny Ziegler
Translator: Andy Ziegler
Languages: de nl
Printer printer-version
Support Us!
 

Init scripts

Init Scripts A lot of programs and services should be started during booting the system. Here it is explained howto create a simple init script and where to place it so everything works fine.

The boot process

After the basic boot process which is in the hands of BIOS and Linux kernel the remaining work is given to the program init. This program is responsible for starting processes, daemons, scripts and at the end the tty console (partially the X server, too) to prepare the user's login.

The configuration of init is quite simple but since many distributions with different concepts have existed it became very confusing.

The differences among the distributions are more basit than just different file and directory names but different ideas in the realisation of the boot process. Later more about this topic.

Basic structure of SysV

Linux knows different (so called) runlevels. With modern distributions the Linux user gets in contact with his name indirectly.
In general the installation of the system would ask if the user wanted to login on a console or already with X (using xdm, gdm or kdm). In the latest releases even this question vanished and the X-server is started automatically.

Interpreted internally both variants belong to different runlevels. A login with console or X is aquivalent to the runlevels 2 (Multi-User with network) and runlevel 3 (Multi-User with network and graphical login), respectively.
In these two points nearly all distributions agree.

However you are not restricted to these runlevels. You can create and configure unlimited runlevels on your system. At least 6 runlevels exist by default with most of the distributions although a few could be empty.
A more detailed description which function corresponds to which runlevel, can be found in /etc/inittab.
In the following listing taken from the inittab of a SuSE distribution:

/etc/inittab (SuSE)
   # runlevel 0 is halt
   # runlevel S is single-user
   # runlevel 1 is multi-user without network
   # runlevel 2 is multi-user with network
   # runlevel 3 is multi-user with network and xdm
   # runlevel 6 is reboot
  


Changing from one runlevel to another is possible in a running system. Therefore the command

  >> init X
  
exists, where X stands for the desired runlevel. This way you can restart all daemons without rebooting or you could test a changed X configuration (first you change into the single user mode with init S followed by a e.g. init 3).

In addition it could help if you wanted to disconnect a Linux PC from the network (e.g. you had the suspicion that an unauthorised access via the network exists) without taking out the network cable. A simple

  >> init S
  
cuts all network connections and then the superuser can search for a Trojan horse.

In addition you see that a reboot or halt of the system is nothing else than a change to the runlevel 6 or 0, respectively. The commands reboot -n and halt are aquivalent to init 6 and init 0 respectively.

Also the default runlevel which is used during booting is set in the /etc/inittab. The entry

  id:3:initdefault:
  
defines it. Using SuSE with Yast and changing from the graphical login to the console login, in this line 3 is replaced by a 2. Nothing else!.

Configuration

Linux would not be Linux/Unix if you were not able to configure until eternity.
It can be very important which hardware is initialised in which runlevel.

For example a Laptop that is used mobile in one (or more) company(ies) only needs support for the PCMCIA network card if it is placed in the company. Using it mobile the printer drivers and all netowrk software (drivers, apache, NFS/mounting, NIS ...) are not needed and resources can be saved. This can be very simply realised with different runlevels.

One runlevel for a single computer, another with a network configuration.
It is even possible to configure different runlevels for different networks. If you changed from one network to another for a running system you need not reboot but simply start the corresponding runlevel and the network configuration fits to the network.
However this is just one of many ways to use runlevels.

The configuration of runlevels depends on the distribution which is used. Using SuSE you find the configuration files/scripts in /sbin/init.dwhile e.g. RedHat based installations place them at /etc/rc.d/.

Often a symbolic link exists from one directory to the other hoping that the compatibility among the distributions is kept (not more than a sad try).

Unfortunately a consensus is not near and every distributor points out the advantages of his concept.

In the corresponding subdirectory you find all scripts which start several tasks, daemons etc. which are necessary during booting and halting:

ls /sbin/init.d/ (SuSE)
  i4l         network      random       single
  alsasound   i4l_hardware nfs          reboot    skeleton
  apache      cron         idedma       nfsserver route     smbfs
  apmd        dhclient     inetd        scd       routed    sshd
  at          dummy        init.d       pcmcia    rstatd    syslog
  autofs      firewall     irda         pcnfsd    rusersd   usb
  boot        gpm          kbd          portmap   rwhod     wvdial.dod
  halt        kerneld      powerfail    sendmail  xdm
  lpd         pppoed       serial       ypclient
  


These scripts are for e.g. starting the cron-daemon (cron) which takes care of commands that are executed regularly, starting the web server (apache) up to the initialisation of the USB hardware (usb).

Most scripts have a common structure:

Structur of an Init-script
   #! /bin/sh
   case "$1" in
      start)
          echo -n "Start service XYZ"
          /path/to/XYZ
          ;;
      stop)
          echo -n "Stop service XYZ"
          killall  XYZ 
          # oder /path/to/XYZ -quit if supported by the program
          ;;
      restart)
          $0 stop  &&  $0 start
          ;;
      status)
          echo -n "Status printout of the process:"
  	# A command which prints out information of the running program.
          ;;
      *)
          echo "Usage: $0 {start|stop|status|restart}"
          exit 1
          ;;
   esac
  


To set which script is executed in which runlevel you set a symbolic link to the script into the subdirectory rcX.d (where X stand for the runlevel) But a few details have to be mentioned:

The symbolic link has to follow a defined name structure. If the script had to be executed during booting the correspoinding link should start with a capital "S", folowed by a two digit number that sets the order in which the scripts are executed and then the name of the script. Starting with S01xxx the scripts are executed with rising numbers up to S99xxxx. The option "start" is added to the script automatically.

The execution of the scipts in a fixed order is necessary because many programs need others before they can be initalised. For example the network could only be used if the PCMCIA network card was initialised.

Similarly the processes are stopped when leaving a runlevel. The corresponding scripts are linked with the pattern K01xxxxx to K99xxxxx.

Thus the directory /sbin/init.d/rc2.d/ of the runlevel 2 could look like:

ls /sbin/init.d/rc2.d/ (SuSE)
  K19cron        K24ypclient      S03pcmcia          S19idedma
  K19nscd        K30idedma        S04dummy           S20apache
  K19smbfs       K30random        S04firewall_init   S20apmd
  K20apache      K35routed        S04irda            S20at
  K20apmd        K35syslog        S05dhclient        S20gpm
  K20at          K36nfs           S05i4l             S20inetd
  K20gpm         K37portmap       S05network         S20lpd
  K20inetd       K38route         S07firewall_setup  S20rstatd
  K20lpd         K40dhclient      S07route           S20rusersd
  K20rstatd      K40i4l           S08portmap         S20rwhod
  K20rusersd     K40network       S09nfs             S20sendmail
  K20rwhod       K42pcmcia        S09syslog          S20sshd
  K20sendmail    K44i4l_hardware  S10kbd             S21alsasound
  K20sshd        K45dummy         S10routed          S21cron
  K21alsasound   K45irda          S13random          S21nscd
  K22wvdial.dod  K51firewall      S16ypclient        S21smbfs
  K23nfsserver   K99kerneld       S17autofs          S22wvdial.dod
  K23pcnfsd      S01kerneld       S17nfsserver       S99firewall_final
  K24autofs      S03i4l_hardware  S17pcnfsd
  


Already here you see a difference between SuSE distributions and the presented scheme: You find many links in the subdirectories than you wanted to start in general.

What remains ...

As mentioned before, every distribution has its own idea of what a "good boot process" should look like.
At present while aiming more and more to the desktop market, there is a "need" to hide any complexity of the system from the users. This step covers a lot of problems because a user who does not know this system cannot fix problems which might occur or fit to his needs.

This "userfriendly" start-up has gone so far that any messages from the programs are suppressed at all and only a green done or a error reports if the task was executed successfully.
If the start failed the user does not get any additional help and has the problem where and how to fix the error.

Program which optimised to hide are e.g. LPP (Homepage: http://lpp.freelords.org/) which completely hide the boot process using colorful boot logos and status repots. It will only be a matter of time before common desktop oriented distributions will surprise as with these cruelities.

Taking SuSE the Init concept looks like:
All installed and system important tools are automatically built to the corresponding rcX.d subdirectories. The existence fo the init does not mean that it is started automatically but the script does only include a test of the global configuration file /etc/rc.config. In this file the correspoinding variable START_XXX (e.g. START_HTTPD for apache web-server) has to be set to yes before the script is executed.

What you win is only the transport of all processes which have to be started during booting to Yast. For a beginner this looks simpler than creating the lniks in the /sbin/init.d subdirectories. However instead of putting the control into another file Yast could directly create or remove the symbolic link. The result would be the same and more standard, too.

On the other hand YAST has a weakness which should not be underestimated. The variables START_XXX are not independent of the runlevels any longer but have an influence to all runlevels.
The only way to handle this is to create links only in the runlevles where needed (e.g. the link to xdm is only in runlevel 3).

If you wanted to create you own runlevel or if you wanted to start processes, which are administrated by YAST only in selected runlevels, you would have to do it yourself. This means you have to remove or create a link, eventually you have to replace existing scripts by you own written scripts.

boot.local

If all this was too complicated for you, you could avoid the scripts and instead use the boot.local. This is a simple script which you find in the /sbin/init.d/ directory and where simple commands can be placed by the system administrator which will be executed during booting the PC.

This file offers a place for processes which have to be started independently of the runlevel.

A short entry of the command which is necessary often saves the creation of a whole init script with all its options (start, stop, status ...).

Unfortunately different ways of realisation exist. The SuSE distribution up to version 6.x has executed the script boot.local at the end after all init-scripts were called. A link to the boot.local which is called S99local(??) exists in all runlevels.

From version 7.0 on the boot.local is called at the beginning, before all other init scripts.
This can be important because usually you use the boot.local to execute small tools and programs, where a complete init script would be too much.
If the execution of the boot.local is placed at the beginning many programs would not work because the hardware they need (e.g. the network card) might not be initialised.

A solution could be your own batch-file in which you place all your small commands and which you link from all runlevels with a link called S99xxxx.

The different distributions

  • SuSE:
    The processes which are called in the init scripts are administrated by the programs startproc and killproc. startproc created for every process which was started a file with the name process+suffix".pid" in which the process ID (PID) is stored. This file is placed at /var/run/. The program killproc can kill all processes by sending a kill signal to the process-ID which is stored. In addition killproc removes the corresponding PID files.

    If the start of the process was successful the script would send a $rc_done to inetd which would print the success.
    If an error occured ti would send a $rc_failed.
    The text which is put to $rc_done and $rc_failed can be set using YAST (or in /etc/rc.config).

    A typical init script (for version 7.0) has the following structure:

    Init-Skript
       #! /bin/sh
       #
       . /etc/rc.status
       . /etc/rc.config
      
       base=${0##*/}
       link=${base#*[SK][0-9][0-9]}
       
       rc_reset
       case "$1" in
          start)
              echo -n "Starting XYZ daemon"
      	startproc /usr/sbin/XYZ
      	rc_status -v
      	;;
          stop)
      	echo -n "Shutting down XYZ daemon"
      	killproc -TERM /usr/sbin/XYZ
      	rc_status -v
      	;;
          restart)
      	$0 stop && $0 start
      	rc_status
      	;;            
          reload)
      	# Comman to re-read the configuration file
      	;;
          status)
      	echo -n "Checking for XYZ daemon"
      	checkproc /usr/sbin/XYZ && echo "XYZ is up" || echo "No XYZ daemon"
      	;;
          probe)
      	# Command to test XYZ
      	;;
          *)
      	echo "Usage: $0 {start|stop|status|restart|reload|probe}"
      	exit 1
       esac
       rc_exit
      


    A speciality of SuSE 7.0 which was already mentioned above is the start of the script boot.local. In 7.0 it is placed at the beginning before all init-scripts are started while older distributions start this file after executing all init-files.
    This could lead into trouble with hardware related programs if the hardware was not initialised before.
    A solution could be your own script e.g. /sbin/init.d/boot.last which is called after all scripts were started (symbolic link S99zzzboot.last -> .../boot.last.

  • RedHat:
    If the structure of scripts made by RedHat increased the overview should be decided by yourself. Placing the different calls (start, stop ...) in separate functions does not really improve things.
    In addition a lock-file in the /var/lock/subsys/ directory is created

    Init script
       #! /bin/sh
       #
       # skeletond          Start/Stop the skeleton daemon.
       #
       # chkconfig: 2345 90 60
       #
       # processname: skeleton
       # config: /etc/skeleton
       # pidfile: /var/run/skeleton.pid
        
       #   Source function library.
       .  /etc/init.d/functions
         
       RETVAL=0
       
       # See how we were called.
         
       start() {
       	echo -n "Starting skeleton daemon: "
       	daemon skeleton
       	RETVAL=$?
       	echo	[ $RETVAL -eq 0 ] && touch /var/lock/subsys/skeleton
       	return $RETVAL
       }
       
       stop() {
       	echo -n "Stopping skeleton daemon: "
       	killproc skeleton
       	RETVAL=$?
       	echo
       	[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/skeleton
       	return $RETVAL
       }	
       
       rhstatus() {
       	status skeleton
       }	
       
       restart() {
         	stop
       	start
       }	
       
       reload() {
       	echo -n "Reloading skeleton daemon configuration: "
       	killproc skeleton -HUP
       	retval=$?
       	echo
       	return $RETVAL
       }	
       
       case "$1" in
         start)
         	start
       	;;
         stop)
         	stop
       	;;
         restart)
         	restart
       	;;
         reload)
         	reload
       	;;
         status)
         	rhstatus
       	;;
         condrestart)
         	[ -f /var/lock/subsys/skeleton ] && restart || :
       	;;
         *)
       	echo "Usage: crond {start|stop|status|restart|condrestart}"
       	exit 1
       esac
                      
       exit $?
      


  • Mandrake:
    Another skeleton as an example (the distribution itself does not have a skeleton file. The following file was taken from the start script of the cron daemon and was changed slightly.)

    The init script of the Mandrake distribution has the simplest structure of the three presented scripts and shows nealy no properties which depend on the distribution.

    Init-Script
       #! /bin/sh
       #
       # skeleton          Start/Stop a daemon.
       #
       # chkconfig: 2345 40 60
       #
       # processname: skeleton
       # config: /etc/skeleton
       # pidfile: /var/run/skeleton.pid
       
       # Source function library.
       . /etc/rc.d/init.d/functions
       
       # See how we were called.
       case "$1" in
         start)
       	echo -n "Starting daemon skeleton: "
       	daemon skeletond
       	echo
       	touch /var/lock/subsys/skeletond
       	;;
         stop)
       	echo -n "Stopping skeleton daemon: "
       	killproc skeletond
       	echo
               rm -f /var/lock/subsys/skeletond
       	;;
         status)
       	status skeletond
       	;;
         restart)
       	killall -HUP skeletond
       	;;
         *)
       	echo "Usage: skeleton {start|stop|status|restart}"
       	exit 1
       esac
       
       exit 0
      


  • Overview:
    A short list of skeleton files for different distrubutions:

Hint: Special case laptops

The use of runlevels can be a very good tool, in particular for laptops which are used in different networks. For example a laptop which is used at work but also in your local LAN at home.
Usually you would have to reconfigure the network card, the gateway and the DNS server if you changed the network. In addition a few services which are necessary at work, are not needed at home (lpd, NIS, NFS ...).
If you created two runlevels, each for one network, the correct configuration can be chosen and started. Therefore you copy an existing runlevel to e.g. rc7.d and change it to your desires in the corresponding network.
You should not forget to announce the new runlevel in the file /etc/inittab, in particular the row

  7:123:respawn:/sbin/mingetty tty7
  
has to be added, because otherwise no console is started where you can log in.

In addition you should change the configuration file by a special script which you have to create, i.e. this script copies the existing network configuration to a backup directory and replaces all necessary files by the configuration of the network you need. If the changes are quite exausting you could alternatively replace the total /etc-directory. In any case you have to make sure that the computer is not halted without any /etc-directory, because it cannot start next time.

Finally an entry in the LILO configuration files is necessary which tells the system during booting which configuration has to be started. Therefore you copy the entry for the distribution you used so far and extend it by one append-entry:

/etc/lilo.conf
   [...]
   # entry used so far
   image = /boot/kernel_redhat7_z
     root = /dev/hda2
     label = redhat
  
   #---new entry
   image = /boot/kernel_redhat7_z
     root = /dev/hda2
     append = "init=/sbin/init 7"
     label = firma
  


After calling the command lilo you can chose the option "office" during the next boot process. LILO submits the line which you specified behind append to the kernel and it will use the runlevel 7.
This script creates the /etc-directory which is needed in your office and the system is set-up properly.
You see you can start the system with different configurations without creating a separate partition for each.

Conclusions

The boot process clearly shows the differences between the distributions. Every "huge" distribution has its own imaginiation how the runnlevels should be structured and organized.

The disadvantage is clearly on the side of the users, as usual, who loose the overview when changing the distribution.

A simple and well-thought structure was messed up. Partially the runlevel directories are filled with an enourmous number of links which are not started in the end.

We can only hope that, with upcoming open standards, we get agreement how the sturcture of what the boot process has to look like.


I thank sh0 and zyz for supporting me with the skeleton files.
Homepage: www.libertynet.de




Talkback Area




Enter Own Comment