This article will now explain how to setup the bootup process for Linux so
that single-user mode really works if you are using the Slackware 3.2
distribution (or a derivative). I will begin by assuming that your kernel
is correctly configured and that the init
program starts successfully.
See the Installation-HOWTO at
ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/Installation-HOWTO
for help to get this far. Once you have a system that boots, however, you
have only begun. Why? Most distributions will give you a generic set of
initialization scripts that are designed to work for an average installation.
You will want to customize this in order to run extra things you want and/or
to prevent running things you do not want. With the dozen or so standard
startup scripts things can seem confusing, but after you read this article
you should be able to understand enough to create a custom environment
when you boot that exactly suits you.
As I stated earlier, I will begin by assuming that init
has started
successfully. It will examine the file /etc/inittab
to determine
what to do. In that file are located the lines to activate your login
devices such as terminals, modems, and your virtual consoles. Leave that
stuff alone. What we are interested in are the lines which call the
startup/shutdown scripts. These lines will look something like this:
# Default runlevel. id:3:initdefault: # System initialization (runs when system boots). si::sysinit:/etc/rc.d/rc.S # Script to run when going single user (runlevel 1). l1:1:wait:/etc/rc.d/rc.K # Script to run when going single user (runlevel S or s) mm:S:wait:/etc/rc.d/rc.K2 # Script to run when going multi user. rc:23456:wait:/etc/rc.d/rc.M # 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
The comments are present and are very helpful. First you need to determine
your default runlevel. In this case it is 3. The format of the /etc/inittab
file section we are looking at is simple. Blank lines are ignored. Lines
with '#' as the first character are comments and are ignored. Other lines
have 4 parts separated by the colon character. These parts are 1. symbolic
label, 2. runlevel, 3. action and 4.command to run. These are documented in
the section 5 manual page for /etc/inittab
(man 5 inittab
). First
we must find a line with an action of initdefault
, and then see what runlevel
it has. That will be the default runlevel. Obviously you should not have 2
lines that have initdefault
as the action in an /etc/inittab
file. Once you know
the default runlevel, you will be able to know what /etc/inittab
entries
will be processed by init
. The 1 runlevel is considered single-user
maintenance mode, but it supported multiple simultaneous logins in virtual
terminals with the default /etc/inittab
on my systems. You can prevent this by
removing the 1 from the getty lines of the tty2, tty3, tty4, etc. The 3
runlevel is considered the normal multi-user mode with full networking
support. The S runlevel is supposed to be true single-user, and you can
theoretically enter that runlevel using the lilo
parameter
single
. However, for the Slackware 3.2 distribution, that does
not put you in a single-user mode as you would expect, but instead you wind
up in runlevel 3. The /etc/inittab
file I show here does not have that problem
however. Once you have read this article you can change the system to
behave in the expected manner. So we know we will go to runlevel 3. That
means init
will perform every command in the /etc/inittab
file that has a sysinit
,
then boot
, or bootwait
, and finally any entries for our runlevel 3. When you
want to run a script when entering a runlevel, it doesn't make sense to have
more than one script line in the /etc/inittab
file for that level. Instead, you
should put everything in 1 script, or call scripts from within the script
mentioned in the /etc/inittab
file using the dot method. Once thing to note is
that field 2, the runlevel field, can have more than 1 runlevel specified.
The init
program will first run the si
entry
(and we will wait for it to finish running /etc/rc.d/rc.S
) since it has
sysinit
(which implies wait
) in the third field. Then it will run everything
with 3 specified. So in our example file we will run the si
target, then
the rc
target (and we will wait for it to finish running the
/etc/rc.d/rc.M
script since the third field is wait
), and finally
we it will do the c1
through c6
targets which set up the virtual ttys during
a normal boot.
If we boot (via lilo
) and add the single
parameter, we will still run the si
target (/etc/rc.d/rc.S
) and wait for it to
complete, but then we will run the mm
target (/etc/rc.d/rc.K2
). Keep in mind
that runlevel 1 and runlevel S are essentially the same when you enter them,
but how you get there is very different. Runlevel 1 can be entered by using
the command /sbin/telinit 1
, but /sbin/telinit s
will send you to runlevel 5
often for some reason (some kind of bug). Runlevel 1 will give you a normal
log in, and allows 1 user (any 1 user) to log in at the console.
With this setup, runlevel S will give you a special root-only login that
allows only root to use the console. Since only root can log in, only a
special password prompt is displayed. If you press enter or ctl-D, the system
will return to runlevel 3. This root-only login is accomplished by using
the /bin/sulogin
program. Runlevel S is probably what you want when you think
single-user, but you have to reboot the machine and use lilo
and have the
single parameter to make it work. You can use runlevel 1 to accomplish the
same things, but remember you will have to manually return to runlevel 3
when you are done with another call to /sbin/telinit 3
or a reboot, and you
must insure that nobody else can get to the console but the root user.
WARNING: The true single-user mode entered with the single parameter to
lilo
with my /etc/inittab
and /etc/rc.d/rc.K2
will support only 1 console and no other
virtual terminals. Do not run anything that locks up the terminal!
Ok, so what do we know now? We know what scripts init
will call and when they
will be called. But what can be in those scripts? The scripts should be
written for bash unless you are a real guru and KNOW the other shell you
wrote scripts for will be available during boot. There is nothing preventing
you from using perl
or tcsh
or whatever, but traditionally most everyone uses
bash
scripts (ok, ok, Bourne shell scripts) for unix boot scripts. The /etc/rc.d/rc.S
script which is called at system boot time should take care of things like
fsck
'ing your file systems, mounting them, and starting up swapping and other
essential daemons. These are things that you need independent of runlevel.
The /etc/rc.d/rc.M
script which is called when you enter runlevel 3 should start all
the processes that remain that you usually need during normal system
operation EXCEPT things like getty
. Processes that must be restarted whenever
they stop running like getty
should be placed in the /etc/inittab
file instead of
being started by a boot script. So what is in a typical /etc/rc.d/rc.M
script?
Usually configuring the network , starting web servers, sendmail, and
anything else you want to always run like database servers, quota
programs, etc.
The only startup script I mention in my /etc/inittab
that is not included in the
Slackware 3.2 distribution is /etc/rc.d/rc.K2
, and it is merely a modified version
of /etc/rc.d/rc.K
set up for single user mode. Remember this is the startup script
that will be used if you choose to enter the single
parameter to lilo
. At
the end of this file you will see a line:
exec /bin/sulogin /dev/console
This will replace the current process which is running the script with the
/bin/sulogin
program. This means, of course, that this has to be the last
line in your script, since nothing after this line will be processed by bash
.
After that program starts, it displays a message to either enter the root
password or press ctl-D. If you enter the correct root password, you will
be logged in as root in a true single-user mode. Be careful, though,
because when you exit that shell the machine will go into runlevel 3.
If you want to reboot before entering runlevel 3 you must remember to do
it (via shutdown
) instead of just exiting the shell. If you press ctl-D
instead of the root password, the system will enter runlevel 3. I have
changed the incorrect calls to kill
to use the killall5
program, since
the lines with kill
caused init
to be killed and a runlevel change was
happening incorrectly.
Well, I hope that this description of how I enabled my Linux machine to
have a single-user mode similar to that of the big-name workstations
proves helpful to you. Customizing your boot process is not too hard,
once you understand something about how the /etc/inittab
and /etc/rc.d/*
scripts work. Be sure you 1. backup your entire system, 2. have a boot
floppy, and 3. a rescue floppy that can restore the backup (or any
individual files) you made in step 1 using the boot floppy from step 2 to
boot the machine. If you make a 1 character typo you can prevent the
machine from booting, so the backup steps, while tedious, are really
necessary to protect yourself before you experiment.
Here are the files I used. Use at your own risk. They work for me, but may need to be modified to work for you.
/etc/inittab
# # inittab This file describes how the INIT process should set up # the system in a certain run-level. # # Version: @(#)inittab 2.04 17/05/93 MvS # 2.10 02/10/95 PV # # Author: Miquel van Smoorenburg, miquel@drinkel.nl.jugnet.org # Modified by: Patrick J. Volkerding, volkerdi@ftp.cdrom.com # Modified by: John Gatewood Ham, zappaman@alphabox.compsci.buu.ac.th # # Default runlevel. id:3:initdefault: # System initialization (runs when system boots). si::sysinit:/etc/rc.d/rc.S # Script to run when going maintenance mode (runlevel 1). l1:1:wait:/etc/rc.d/rc.K # Script to run when going single user (runlevel s) mm:S:wait:/etc/rc.d/rc.K2 # Script to run when going multi user. rc:23456:wait:/etc/rc.d/rc.M # What to do at the "Three Finger Salute". # make the machine halt on ctl-alt-del ca::ctrlaltdel:/sbin/shutdown -h now "going down on ctl-alt-del" # 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 # What to do when power fails (shutdown to single user). pf::powerfail:/sbin/shutdown -f +5 "THE POWER IS FAILING" # If power is back before shutdown, cancel the running shutdown. pg:0123456:powerokwait:/sbin/shutdown -c "THE POWER IS BACK" # If power comes back in single user mode, return to multi user mode. ps:S:powerokwait:/sbin/init 5 # The getties in multi user mode on consoles an serial lines. # # NOTE NOTE NOTE adjust this to your getty or you will not be # able to login !! # # Note: for 'agetty' you use linespeed, line. # for 'getty_ps' you use line, linespeed and also use 'gettydefs' # we really don't want multiple logins in single user mode... c1:12345:respawn:/sbin/agetty 38400 tty1 linux c2:235:respawn:/sbin/agetty 38400 tty2 linux c3:235:respawn:/sbin/agetty 38400 tty3 linux c4:235:respawn:/sbin/agetty 38400 tty4 linux c5:235:respawn:/sbin/agetty 38400 tty5 linux c6:235:respawn:/sbin/agetty 38400 tty6 linux # Serial lines #s1:12345:respawn:/sbin/agetty 19200 ttyS0 vt100 #s2:12345:respawn:/sbin/agetty 19200 ttyS1 vt100 # Dialup lines #d1:12345:respawn:/sbin/agetty -mt60 38400,19200,9600,2400,1200 ttyS0 vt100 #d2:12345:respawn:/sbin/agetty -mt60 38400,19200,9600,2400,1200 ttyS1 vt100 # Runlevel 4 used to be for an X-window only system, until we discovered # that it throws init into a loop that keeps your load avg at least 1 all # the time. Thus, there is now one getty opened on tty1. Hopefully no one # will notice. ;^) # It might not be bad to have one text console anyway, in case something # happens to X. x1:4:wait:/etc/rc.d/rc.4 # End of /etc/inittab
/etc/rc.d/rc.K
# /bin/sh # # rc.K This file is executed by init when it goes into runlevel # 1, which is the administrative state. It kills all # deamons and then puts the system into single user mode. # Note that the file systems are kept mounted. # # Version: @(#)/etc/rc.d/rc.K 1.50 1994-01-18 # Version: @(#)/etc/rc.d/rc.K 1.60 1995-10-02 (PV) # # Author: Miquel van Smoorenburg miquels@drinkel.nl.mugnet.org # Modified by: Patrick J. Volkerding volkerdi@ftp.cdrom.com # Modified by: John Gatewood Ham zappaman@alphabox.compsci.buu.ac.th # # Set the path. PATH=/sbin:/etc:/bin:/usr/bin # Kill all processes. echo echo "Sending all processes the TERM signal." killall5 -15 echo -n "Waiting for processes to terminate" for loop in 0 1 2 3 4 5 6 7 ; do sleep 1 echo -n "." done echo echo "Sending all processes the KILL signal." killall5 -9 # Try to turn off quota and accounting. if [ -x /usr/sbin/quotaoff ] then echo "Turning off quota.." /usr/sbin/quotaoff -a fi if [ -x /sbin/accton ] then echo "Turning off accounting.." /sbin/accton fi
/etc/rc.d/rc.K2
# /bin/sh # # rc.K This file is executed by init when it goes into runlevel # 1, which is the administrative state. It kills all # deamons and then puts the system into single user mode. # Note that the file systems are kept mounted. # # Version: @(#)/etc/rc.d/rc.K 1.50 1994-01-18 # Version: @(#)/etc/rc.d/rc.K 1.60 1995-10-02 (PV) # # Author: Miquel van Smoorenburg miquels@drinkel.nl.mugnet.org # Modified by: Patrick J. Volkerding volkerdi@ftp.cdrom.com # Modified by: John Gatewood Ham zappaman@alphabox.compsci.buu.ac.th # # Set the path. PATH=/sbin:/etc:/bin:/usr/bin # Kill all processes. echo echo "Sending all processes the TERM signal." killall5 -15 echo -n "Waiting for processes to terminate" for loop in 0 1 2 3 4 5 6 7 ; do sleep 1 echo -n "." done echo echo "Sending all processes the KILL signal." killall5 -9 # Try to turn off quota and accounting. if [ -x /usr/sbin/quotaoff ] then echo "Turning off quota.." /usr/sbin/quotaoff -a fi if [ -x /sbin/accton ] then echo "Turning off accounting.." /sbin/accton fi # Now go to the single user level exec /bin/sulogin /dev/console