Take Command: Init

Take Command: Init
(November 1998)

Reprinted with permission of the Linux Journnal

Init is the driving force that keeps our Linux box alive, and itis the one that can put it to death. This article is meant tosummarize why Init is so powerful and how you can instruct it tobehave differently from its default behaviour (yes, Init is powerful,but the superuser rules over Init).

by Alessandro Rubini

Which Init?

In Unix parlance, the word ``init'' doesn't identify a specificprogram, but rather a class of programs. The name ``init'' isgenerically used to call the first process that is executed at systemboot -- actually, the only process that is executed at systemboot. When the kernel is done with setting up the computer's hardware,it invokes init and gives up controlling the computer. From now on thekernel only processes system calls, without taking any decisional rolein system operation. After the kernel is done mounting the rootfilesystem, everything is controlled by init.

Currently, there are several choices as far as init is concerned: youcan use the now-classic program that comes in the SysVinit package byMiquel van Smoorenburg, or simpleinit by Peter Orbaek (found in thesource package of util-linux), or a simple shell script (like the oneshown in this article, which has a lot of functionality less than anyC-language implementation). If you set up embedded systems you caneven just run the target application like it was init. Insane peoplewho dislike multitasking could even port command.com to Linux and runit as the init process, although you won't ever be able to restrictyourself to 640k when running a Linux kernel.

No matter what is the program you choose, it needs to be accessed witha pathname of /sbin/init, /etc/init or /bin/init, because thesepathnames are compiled in the kernel. If neither of them can beexecuted than the system is severely broken, and the kernel will spawna root shell to allow interactive recovery (i.e., /bin/sh is used asan init process).

To achieve maximum flexibility, kernel developers offered a way toselect a different pathname for the init process. The kernel accepts acommand line option of init= exactly for that purpose. Kerneloptions can be passed interactively at boot time, or you can use theappend= directive in /etc/lilo.conf. Silo, Milo, Loadlin andother loaders allow specifying kernel options as well.

As you may imagine, the easiest way to get root access to a Linux boxis by typing init=/bin/sh to the Lilo prompt. Note that thisis not a security hole per se, because the real security holehere is physical access to the console. If you are concerned about theinit= option, Lilo can prevent interaction using its ownpassword protection.

The task of init

Ok, so init is a generic naming, and almost anything can be usedas init. The question now is what is a real init supposed to do.Being the first (and only) process spawned by the kernel, the task ofinit consists in spawning every other process in the system. Thisusually includes the various daemons used in system operation as wellas any login session on the text console.Init is also expected to restart some of its child processes as soonas they exit. This typically applies to the login sessions running onthe text consoles: as soon as you logout the system should run another``getty'' to allow starting another session.Init should also collect dead processes and dispose of them. In theUnix abstraction of processes, a process can't be removed from thesystem table unless its death is reported to its parent (or anotherancestor in case its parent doesn't exist anymore). Whenever a processdies, by calling exit or otherwise, it remains around in thestate of a zombie process until someone collects it. Init, being theancestor of any other process, is expected to collect the exit statusof any orphaned zombie process -- note that every well-written programshould reap its own children, zombies only exist when some program ismisbehaving. If init wouldn't collect zombies, lazy programmers couldeasily consume system resources and hang the system by filling theprocess table.The last task of Init is handling system shutdown. The init programmust stop any process and unmount all the filesystems when thesuperuser tells that shutdown time has arrived. The shutdownexecutable, actually, doens't do anything but tell init thatanything is over.As we have seen, the task of init is not too hard to implement, and ashell script could well perform most of the required tasks. Note thatevery decent shell collects its dead children, so this is not aproblem with shell scripts.What real init implementations add to the simple shell scriptapproach is a greater control over system activity, and thus a hugebenefit in overall flexibility.This article will now proceed by showing different implementations ofthe init concept, in ascending order of complexity.

Using /bin/sh as a minimal choice

As suggested above, the shell can be used as an init program.Using a bare shell, in the init=/bin/sh way, only opens aroot shell in a completely unconfigured system. This section shows howa shell script can perform all of the tasks you need to have a minimalrunning system. This kind of tiny init can be used in embedded systemor similar reduced environments, where you must squeeze out everysingle byte out of the system. Note that the most radical approach toembedded systems is directly running the target application as theinit process; this results in a closed system (no way for theadministrator to interact should problems arise), but it sometimessuites the setup. The typical example of non-init-driven Linux systemis the installation environment of most modern distributions, where/sbin/init is a symbolic link to the installation program.

    #!/bin/sh

    # avoid typing full pathnames
    export PATH=/usr/bin:/bin:/sbin:/usr/sbin

    # remount root read-write, and mount all
    mount -n -o remount,rw /
    mount -a
    swapon -a

    # system log
    syslogd
    klogd

    # start your lan
    modprobe eth0 2> /dev/null
    ifconfig eth0 192.168.0.1
    route add 192.168.0.0 eth0
    route add default gw 192.168.0.254

    # start lan services
    inetd
    sendmail -bd -q30m

    # Anything else: crond, named, ...

    # And run one getty with a sane path
    export PATH=/usr/bin:/bin
    /sbin/mingetty tty1

   Listing 1
To make a long story short, Listing 1 shows a script that can performacceptably as init. The script is very short and incomplete; inparticular, note that it only runs one getty, which isn't restartedwhen it terminates. Be careful if you try to use this script, as eachLinux distribution chooses its own flavour of getty. Try grepgetty /etc/inittab to know what you have and how to call it.The script shown has another misfeature: it doesn't deal with systemshutdown. Adding shutdown support, however, is pretty easy; just bringeverything down after the interactive shell terminates. Adding thetext shown in Listing2 at the end of Listing1 does the trick.
    # killa anything you started
    killall inetd
    killall sendmail
    killall klogd
    killall syslogd

    # kill anything else
    kill -TERM -1
    sleep 2
    kill -KILL -1

    # release the disks
    swapoff -a
    umount -a
    mount -n -o remount,ro /
    echo "The system is halted"
    exit

   Listing 2
Whenever you boot with a plain init=/bin/sh, you should atleast remount the root filesystem before you'll be able to doanything; you should also remember to umount -a before pressingctrl-alt-del, because the shell doesn't intercept the three-fingersalute.

Simpleinit, from util-linux

The util-linux package includes a C version of an initprogram. It's quite featured and can work well for most personalsystems, although it doesn't offer the huge amount of configurabilityoffered by the SysVinit package, which is the default on moderndistributions.The role of simpleinit (which should be called init to work properly,as suggested above) is very similar to the shell script just shown,with the added capability of managing single-user mode and iterativeinvocation of console sessions. It also correctly processes shutdownrequests.Simpleinit is interesting to look at, and well documented too, so youmight just enjoy reading the documentation; I suggest using the sourcedistribution of util-linux to get up to date information.The implementation of simpleinit is actually simple, like its namesuggests. The program executes a shell script (/etc/rc) and parses aconfiguration file to know what processes need to be respawned. Theconfiguration file is called /etc/inittab, like the one used by thefull-featured init; note however that its format is different.If you plan to install simpleinit in your system (which most likelyalready includes SysVinit) you must proceed with great care, and beprepared to reboot with a kernel argument of ``init=/bin/sh'' torecover from instable situations.

The Real Thing: SysVinit

Most Linux distributions come with the version of init written byMiquel van Smoorenburg, this version is similar the approach taken bySystem-V (five) Unix.The main idea here is that the user of a computer system can wish tooperate his box in one of several different ways (not just single-userand multi-user). Although this feature is not usually exploited, it'snot so crazy as you might imagine. When the computer is shared by twoor more people in the family, different setups can be needed; anetwork server and a standalone playstation can happily coexist in thesame computer as different runlevels. And although I'm the only userof my laptop, I sometimes want a network server (through PLIP) andsometimes a netless environment, to save resources when I'm working onthe train.Each operating mode is called ``runlevel'', and you can choose therunlevel to use either at boot or at runtime. The main configurationfile for init is called /etc/inittab, which defines what to do atboot, when entering a runlevel or when switching from one runlevel toanother. It also tells how handle the three-finger salute and how todeal with power fails, although you'll need a power-daemon and an UPSto benefit from this feature.The inittab file is organized by lines, where each line is made up ofseveral colon-separated fields:``id:runlevel:action:command''The inittab(5) man page is well written and comprehensivelike a man page should be, but I feel worth repeating here one of itsexamples: a stripped-down /etc/inittab that implements thesame features and misfeatures of the shell script shown above:

    id:1:initdefault:
    rc::bootwait:/etc/rc
    1:1:respawn:/sbin/getty 9600 tty1
    

This simple inittab tells init that the default runlevel is ``1'',that at system boot it must execute /etc/rc waiting for itscompletion, and that when in runlevel 1 it must respawn forever thecommand ``/sbin/getty 9600 tty1''. As you may suspect, you'renot expected to test this out, because it doesn't handle the shutdownprocedure.Before proceeding further, however, I must fill a pair of gapsI left behinf. Let'd reply to the questions you keep asking:

  • ``How can I boot into a different runlevel than thedefault?'' That's easy, just add the runlevel on the kernel command line;for example tell ``Linux 2'' to your Lilo prompt, if ``Linux'' is thename of your kernel.
  • ``How can I switch from a runlevel to another one?''That's easy, either; as root call ``telinit 5'' totell the init process to switch to runlevel 5. Different numbersare different runlevels.

Configuring Init

Naturally, the typical /etc/inittab file is much more featured thanthe three-liner shown above. Although ``bootwait'' and ``respawn''are the most important actions, in order to deal with several issuesrelated to system management several other actions exist, but I won't detail them here.Note that SysVinit can deal with ctrl-alt-del whereas the versions ofinit shown earlier didn't catch the three-finger salute (i.e., themachine would reboot if you press the key sequence). Who is interestedin how this is done can check sys_reboot in/usr/src/linux/kernel/sys.c (if you look in the code you'll note theuse of a magic number of 672274793: can you imagine why Linus chosethis very number? I think I know what it is, but you'll enjoy findingit by yourself).So, let's see how a fairly complete /etc/inittab can take care ofeverything that's needed to handle the needs of a system's lifetime,including different runlevels. Although the magic of the game is always on show in /etc/inittab, youcan choose between several different approaches to systemconfiguration, the simplest being the three-liner shown above. In myopinion, two approaches are worth discussing in some detail: I'll callthem ``the Slackware way'' and ``the Debian way'' from two renownLinux distributions that chose to follow them.

The Slackware way

Although it's quite some time I don't install Slackware, thedocumentation included in SysVinit-2.74 tells that it still works thesame, less featured but much faster than the Debian way describedlater. My personal 486 box runs a Slackware-like /etc/inittab just forthe speed benefit.The core of an /etc/inittab as used by a Slackare system is shown inListing 3.

    # Default runlevel.
    id:5:initdefault:

    # System initialization (runs when system boots).
    si:S:sysinit:/etc/rc.d/rc.S

    # Script to run when going single user (runlevel 1).
    su:1S:wait:/etc/rc.d/rc.K

    # Script to run when going multi user.
    rc:2345:wait:/etc/rc.d/rc.M

    # What to do at the "Three Finger Salute".
    ca::ctrlaltdel:/sbin/shutdown -t5 -rf now

    # Runlevel 0 halts the system.
    l0:0:wait:/etc/rc.d/rc.0

    # Runlevel 6 reboots the system.
    l6:6:wait:/etc/rc.d/rc.6

    # Runlevel 1,2,3,5 have text login
    c1:1235:respawn:/sbin/agetty 38400 tty1 linux

    # Runlevel 4 is X only
    x1:4:wait:/etc/rc.d/rc.4
    # But run a getty on /dev/tty4 just in case...
    c4:4:respawn:/sbin/agetty 38400 tty1 linux

   Listing 3
You should not rightahead that the runlevels 0, 1 and 6 have apredefined meaning. This is hardwired into the init command (orbetter, into the shutdown command, part of the same package).Whenever you want to halt or reboot the system, init is told to switchto runlevel 0 or 6, thus executing /etc/rc.d/rc.0 or /etc/rc.d/rc.6.This works flawlessly because whenever init switches to a differentrunlevel it stops respawning any task that is not defined for the newrunlevel; actually, it even kills the running copy of the task (inthis case, the active /sbin/agetty).Configuring this setup is pretty simple, as the role of thedifferent files is pretty clear:
  • /etc/rc.d/rc.S is run at system boot independentyof the runlevel. Add here anything you want to execute right ahead.
  • /etc/rc.d/rc.M is run after rc.S is over, only when thesystem is going to runlevels 2-5. If you boot in runlevel 1 (singleuser) this is not executed. Add here anything you onlyrun when multiuser.
  • /etc/rc.d/rc.K deals with killing processes whengoing from multi-user to single-user. If you added anything in rc.Myou'll probably want to stop it from rc.K.
  • /etc/rc.d/rc.0 and /etc/rc.d/rc.6 shutdown andreboot the computer, respectively.
  • /etc/rc.d/rc.4 is only executed when runlevel 4is entered. The file runs the ``xdm'' process, to allow graphiclogin. Note that no getty is run on /dev/tty1 when in runlevel 4(but you can change this if you want).
This kind of setup is easy to understand, and you can differentiatebetween runlevels 2, 3 and 5 by adding proper ``wait'' (execute oncewaiting for termination) and ``respawn'' (execute forever) entries.By the way, if you ever guessed what ``rc'' means, it's the short formfor ``run command''. I have been editing my ``.cshrc'' and ``.twmrc''for years before being told what this arcane ``rc'' suffix is --there's something in the Unix world that is only handed on by oraltradition. I hope I'm now saving someone from years of unneededdarkness -- and I hope I won't be punished for writing it down.

The Debian way

Although simple, the Slackware way to setup /etc/inittab doesn't scalewell when you add new software packages to the system.Let's imagine, for example, that someone distribute an sshpackage (not unlikely, as ssh can't be distributed in officialdisks due to the insane US rules about crypto). The program sshd is a standalone server that must be invoked at system boot;this means that the package should patch /etc/rc.d/rc.M or one of thescripts it invokes to add ssh support. This is clearly a problem in aworld where packages are typically archives of files; add to this thatyou can't assume that rc.local is always unchanged from the stockdistribution, so even a post-install script that patches the file willmiserably fail most of the times.You should also consider that adding a new server program is only partof the job; the server must also be stopped in rc.K, rc.0 and rc.6. Asyou see, things are getting pretty tricky.The solution to this problem is both clean and elaborate. The idea isthat each package that includes a server must provide the system witha script to start and stop the service; each runlevel than will startor stop the services that are associated to that very runlevel.Associating a service and a runlevel can be as easy as creating filesin a runlevel-specific directory. This setup is common to Debian andRed Hat, and possibly other distributions that I never ran.The core of the /etc/inittab used by Debian-1.3 is shown in Listing 4.
    # The default runlevel.
    id:2:initdefault:

    # This is run first
    si::sysinit:/etc/init.d/boot

    # What to do in single-user mode.
    ~~:S:wait:/sbin/sulogin

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

    # getty
    1:2345:respawn:/sbin/getty 38400 tty1

   Listing 3
The Red Hat setup featuring exactly he same structure for systeminitialization but uses different pathnames; you'll be able to map onestructure over the other. Let's list the role of the different files:
  • /etc/init.d/boot Is the exact counterpart of rc.S. Ittypically checks local filesystems and mounts them, but the real thingis much more featured than that.
  • /sbin/sulogin Allows root to log in a single-userworkstation. Only shown in lising 4 because single-user modeis so important for system maintainance.
  • /etc/init.d/rc Is a script that runs any start/stopscript that belongs to the runlevel being entered.
The last item, the ``rc'' program, is the main character of thisenvironment: it's task consists in scanning the directory /etc/rc$runlevel.d invoking any script that appears in thedirectory.A stripped down version of ``rc'' would look like the following:
    #!/bin/sh
    level=$1
    cd /etc/rc.d/rc$level.d
    for i in K*; do
    	$i stop
    done
    for i in S*; do
	$i start
    done

What does it mean? It means that /etc/rc2.d (for example) includesfiles called K* and S*; the former identify servicesthat must be stopped, and the latter identify services that must bestarted. Ok, but I didn't tell whence do K* and S* come from. This is the smartpart of it all: every software package that needs to be run for somerunlevel adds itself to all the /etc/rc?.d directories, either as a``start'' entry or as a ``kill'' (stop) entry. To avoid codeduplication, the package installs a script in /etc/init.d and severalsymbolic links from the various /etc/rc?.d.To show a real-life example, lets's see what is included in two``rc'' directories of debian:

    rc1.d:
    K11cron         K20sendmail
    K12kerneld      K25netstd_nfs
    K15netstd_init  K30netstd_misc
    K18netbase      K89atd
    K20gpm          K90sysklogd
    K20lpd          S20single
    K20ppp

    rc2.d:
    S10sysklogd     S20sendmail
    S12kerneld      S25netstd_nfs
    S15netstd_init  S30netstd_misc
    S18netbase      S89atd
    S20gpm          S89cron
    S20lpd          S99rmnologin
    S20ppp

This shows how entering runlevel 1 (single-user) kills all theservices and start a ``single'' script; entering runlevel 2 (thedefault level) starts all the services. The number that appears nearthe K or the S is used to order the birth of death of the variousservices, as the shell expands wildcards appearing in /etc/init.d/rcin ascii order. Inovking an ls -l command confirms that allof these files are symlinks, like the following:

    rc2.d/S10sysklogd -> ../init.d/sysklogd
    rc1.d/K90sysklogd -> ../init.d/sysklogd

To summarize, adding a new software package in this environmentmeans adding a file in /etc/init.d and the proper symbolic link fromeach of the /etc/rc?.d directories. To make different runlevels behavedifferently (2, 3, 4 and 5 are configured in the same way by default),just remove or add symlinks in the proper /etc/rc?.d directories.If this scares you as too difficult, not all is lost. If you use RedHat (or Slackware), you can think of /etc/rc.d/rc.local like it wasautoexec.bat -- if you are old enough to remember about the pre-Linuxage. If you run Debian, you could create /etc/rc2.d/S95local and useit as your own rc.local; note however that Debian is very clean aboutsystem setup and I would have better not cast in print such aheresy. You know, powerful and trivial seldom match; you have beenwarned.

Debian-2.0 (hamm)

As I write this article, Debian 2.0 is being released to the public,and I suspect it will be of wide use when you read it.Although the structure of system initialization is the same, it'sinteresting to note that the developers managed to make it faster.Instead of running the files in /etc/rc2.d, the script /etc/init.d/rccan now run them in the same process, without spawning another shell;whether to execute them or source them is controlled by the filename:executables whoe name ends in .sh are sourced, the other onesare executed. The trick is shown in the following few lines, and thespeed benefit is non-negligible:

    case ``$i'' in
        *.sh)
            # Source shell script for speed.
            (
            trap - INT QUIT TSTP
            set start; . $i
            ) ;;
        *)
            # No sh extension, so fork subprocess.
            $i start ;;
   esac
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值