1 # TITLE: Runlevel control using eight runlevel files in one directory.
2 # LFS VERSION: Tested on LFS-3.0
6 # SYNOPSIS: If you find symbolic start and kill links in eight runlevel
7 # directories hard to manage, you could use eight rc files
8 # (e.g. /etc/rc.d/rc[0-6,S].scripts) containing only the
9 # basenames of the init scripts in /etc/init.d.
14 # Version 1.0 (17 dec 2001)
19 # 3. /etc/rc.d/convert
21 # 5. /etc/rc.d/template
22 # 6. /etc/init.d/functions
24 # For the sake of clarity all runlevels are handled in the same manner.
25 # As a result it's possible to call a start script within runlevels 0
26 # and 6. The script used to behave the exact same way as the old rc file,
27 # which had a bug when checking for start scripts in the sysinit runlevel.
28 # In the process of fixing this I removed the inconsistencies. If you need
29 # 'mountfs' to stop after starting 'sendsignals', you need to make a
30 # 'umountfs', which should function like 'mountfs stop'.
32 # To implement all this you need to execute this hint and put the extracted
33 # files in the right places. Then start the 'convert' script in /etc/rc.d
34 # to generate the /etc/rc.d/rc[0-6,S].scripts. Execute 'check' to see if
35 # every init script exists. Now you have runlevel control.
38 # - Don't forget to make 'umountfs' if you didn't already have one!
39 # - Use a '#' at the beginning of start or stop entries inside the
40 # rc[0-6,S].scripts to comment them out.
41 # - Use 'check' to see what happens.
47 #-------------------------------/etc/inittab----------------------------------
49 echo "Extracting etc/inittab..."
51 cat > etc/inittab << "EOF"
54 si::sysinit:/etc/rc.d/rc S
55 l0:0:wait:/etc/rc.d/rc 0
56 l1:S1:wait:/etc/rc.d/rc 1
57 l2:2:wait:/etc/rc.d/rc 2
58 l3:3:wait:/etc/rc.d/rc 3
59 l4:4:wait:/etc/rc.d/rc 4
60 l5:5:wait:/etc/rc.d/rc 5
61 l6:6:wait:/etc/rc.d/rc 6
62 ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
63 su:S016:respawn:/sbin/sulogin
64 1:2345:respawn:/sbin/agetty tty1 9600
65 2:2345:respawn:/sbin/agetty tty2 9600
66 3:2345:respawn:/sbin/agetty tty3 9600
67 4:2345:respawn:/sbin/agetty tty4 9600
68 5:2345:respawn:/sbin/agetty tty5 9600
69 6:2345:respawn:/sbin/agetty tty6 9600
73 #-------------------------------/etc/inittab----------------------------------
76 #-------------------------------/etc/rc.d/rc----------------------------------
78 echo "Extracting etc/rc.d/rc..."
80 cat > etc/rc.d/rc << "EOF"
86 # Thanks to Jason Pearce - jason.pearce@linux.org
87 # and Gerard Beekmans - gerard@linuxfromscratch.org
88 # print_error_msg based on ideas by Simon Perreault -
89 # nomis80@videotron.ca
92 ################################################################
94 # This script does two things in the following order:
96 # 1. Stop the init scripts for the current runlevel that
97 # were started in the previous runlevel.
99 # 2. Start the init scripts for the current runlevel if they
100 # got stopped (as described above) or if they have not
101 # been started already by the previous runlevel.
103 # Ergo: Stop only if it was started and start only if it
104 # wasn't already or got stopped.
106 # The init scripts to be stopped or started are assumed to
107 # be in /etc/init.d/ and are referred to by the rc scripts
108 # in /etc/rc.d/rc[runlevel].scripts.
110 # NB: For the sake of clarity all runlevels are handled in
111 # the same manner. As a result it's possible to call a start
112 # script within runlevels 0 and 6. The script used to behave
113 # the exact same way as the old rc file, which had a bug
114 # when checking for start scripts in the sysinit runlevel.
115 # In the process of fixing this I removed the
116 # inconsistencies. If you need 'mountfs' to stop after
117 # starting 'sendsignals', you need to make a 'umountfs',
118 # which should function like 'mountfs stop'.
120 ################################################################
123 # Include the functions declared in the /etc/rc.d/functions file
126 source /etc/init.d/functions
129 # The print_error_msg function prints an error message when an unforeseen
130 # error occurred that wasn't trapped for some reason by a evaluate_retval
131 # call or error checking in different ways.
137 echo -n "You should not read this error message. It means "
138 echo "that an unforeseen error "
139 echo -n "took place and subscript $i exited with "
140 echo "a return value "
141 echo -n "of $error_value for an unknown reason. If you're able "
142 echo "to trace this error down "
143 echo -n "to a bug in one of the files provided by this book, "
144 echo "please be so kind to "
145 echo -n "inform us at lfs-discuss@linuxfromscratch.org"
149 echo "Press a key to continue..."
154 # If you uncomment the debug variable below none of the scripts will be
155 # executed, just the script name and parameters will be echo'ed to the
156 # screen so you can see how the scripts are called by rc.
159 # Un-comment the following for debugging.
163 # Start script or program.
170 # Ignore CTRL-C only in this shell, so we can interrupt subprocesses.
173 trap ":" INT QUIT TSTP
175 echo -n "Current runlevel: " $RUNLEVEL
176 echo " Previous runlevel: " $PREVLEVEL
179 # Now find out what the current and what the previous runlevel are. The
180 # $RUNLEVEL variable is set by init for all it's children. This script
181 # runs as a child of init.
187 # Get first argument. Set new runlevel to this argument. If no runlevel
188 # was passed to this script we won't change runlevels.
191 [ "$1" != "" ] && runlevel=$1
192 if [ "$runlevel" = "" ]
194 echo "Usage: $0 <runlevel>" >&2
201 # If $PREVLEVEL is set to "N" and we're not in the sysinit
202 # level then we assume the previous runlevel to be sysinit.
205 [ "$previous" == "N" ] && [ "$runlevel" != "S" ] && previous="S"
208 # If previous is empty set it to 'N' c.q. 'No previous'.
211 [ "$previous" == "" ] && previous="N"
213 export runlevel previous
216 # Is there a rc script for the new runlevel?
219 if [ -f $RCDIR/rc$runlevel.scripts ]
223 # If so, first collect all the stop scripts in the new run level.
225 get_stop_scripts rc$runlevel.scripts STOP
230 if [ ! -f $SCRIPTDIR/$i ];
233 echo "Error: rc$runlevel.scripts:$SCRIPTDIR/$i not found"
239 # Determine if there is a start script for this stop script in the
242 if [ "$previous" != "N" ]
244 get_start_scripts rc$previous.scripts previous_start $i
247 [ ! "$previous_start" ] && continue
249 [ "$FIRSTTIME" ] && FIRSTTIME="" && $WHITE &&
250 echo "Stop:" && $NORMAL
253 # If we found previous_start, run the stop script
256 startup $SCRIPTDIR/$i stop
259 # If the return value of the script is not 0, something went wrong with
260 # error checking inside the script. The print_error_msg function will be
261 # called and the message plus the return value of the stop script will be
262 # printed to the screen
264 if [ $error_value != 0 ]
271 # Now run the START scripts for this runlevel.
273 get_start_scripts rc$runlevel.scripts START
278 if [ ! -f $SCRIPTDIR/$i ];
281 echo "Error: rc$runlevel.scripts:$SCRIPTDIR/$i not found"
286 if [ "$previous" != "N" ]
290 # Find current start script in previous runlevel and stop script in this
294 get_stop_scripts rc$runlevel.scripts stop $i
295 get_start_scripts rc$previous.scripts previous_start $i
298 # If there is a start script in the previous level level and no stop
299 # script in this level, we don't have to re-start the service;
300 # abort this iteration and start the next one.
302 [ "$previous_start" ] &&
307 [ "$FIRSTTIME" ] && FIRSTTIME="" && $WHITE &&
308 echo "Start:" && $NORMAL
310 startup $SCRIPTDIR/$i start
314 # If the return value of the script is not 0, something went wrong with
315 # error checking inside the script. the print_error_msg function will be
316 # called and the message plus the return value of the stop script will be
317 # printed to the screen
320 if [ $error_value != 0 ]
331 chmod 754 etc/rc.d/rc
333 #-------------------------------/etc/rc.d/rc----------------------------------
336 #-------------------------------/etc/rc.d/convert-----------------------------
338 echo "Extracting etc/rc.d/convert..."
340 cat > etc/rc.d/convert << "EOF"
345 # This script will convert runlevel directories to files
346 # in the current directory.
349 RCDIRS=`find /etc -type d -name "rc[0-9,S].d"`
354 RCFILE=`basename $i | sed 's/\.d/\.scripts/g'`
358 echo -n "RC file '$RCFILE' already exists! Overwrite ? (yes/no) [n] "
363 echo "Creating backup of $RCFILE called $RCFILE.old"
364 cp -v $RCFILE $RCFILE.old
367 echo "Generation of $RCFILE aborted."
374 echo "Generating $RCFILE..."
376 echo "#----------START----------" > $RCFILE
379 [ ! -h $x ] && continue
380 echo `basename $x` | sed 's/^.[0-9]*//' >> $RCFILE
383 echo "#----------STOP----------" >> $RCFILE
386 [ ! -h $x ] && continue
387 echo `basename $x` | sed 's/^.[0-9]*//' >> $RCFILE
393 chmod 754 etc/rc.d/convert
395 #-------------------------------/etc/rc.d/convert-----------------------------
398 #-------------------------------/etc/rc.d/check-------------------------------
400 echo "Extracting etc/rc.d/check..."
402 cat > etc/rc.d/check << "EOF"
404 source /etc/init.d/functions
406 SCRIPTS=`ls *.scripts`
408 function find_scripts()
410 if [ "$1" == "" ]; then echo; fi
413 echo -n -e "\t" $SCRIPT
414 if [ ! -f $SCRIPTDIR/$SCRIPT ];
425 for SCRIPTFILE in $SCRIPTS;
431 get_start_scripts $SCRIPTFILE START
432 find_scripts "$START"
434 get_stop_scripts $SCRIPTFILE STOP
441 chmod 754 etc/rc.d/check
443 #-------------------------------/etc/rc.d/check-------------------------------
446 #-------------------------------/etc/rc.d/template----------------------------
448 echo "Extracting etc/rc.d/template..."
450 cat > etc/rc.d/template << "EOF"
452 #----------START----------
454 #----------STOP-----------
458 #-------------------------------/etc/rc.d/template----------------------------
461 #-------------------------------/etc/init.d/functions-------------------------
463 echo "Extracting etc/init.d/functions..."
465 cat > etc/init.d/functions << "EOF"
468 # Begin /etc/init.d/functions
470 SCRIPTDIR="/etc/init.d"
475 # get_scripts (rc script file, return variable, do start or stop init scripts
476 # switch, [optional init script to search])
478 function get_scripts()
480 export $2="`echo -e $3"\n"$4 | cat - $RCDIR/$1 |
482 if (NR==1) {switch=$0;x=0;}
483 if (NR==2) {searchscript=$0;}
487 if ($0 ~ /.*START.*/ ) {
488 (switch) ? begin=1 : begin=0;
490 if ($0 ~ /.*STOP.*/) {
491 (switch) ? begin=0 : begin=1;
493 if ($0 ~ /^ *#/) continue;
496 if (searchscript!="") { if ($0==searchscript) print 1 }
506 # get_start_scripts (rc script file, return variable, [init script to search])
508 function get_start_scripts()
511 get_scripts $1 $2 $switch $3
515 # get_stop_scripts (rc script file, return variable, [init script to search])
517 function get_stop_scripts()
520 get_scripts $1 $2 $switch $3
524 # Set a few variables that influence the text that's printed on the
525 # screen. The SET_COL variable starts the text in the column number
526 # decided by the COL and WCOL section (as defined by the COL
527 # variable). NORMAL prints text in normal mode.
528 # SUCCESS prints text in a green colour and FAILURE prints text in a red
532 # If COLUMNS hasn't been set yet (bash sets it but not when called as
537 # Get the console device if we don't have it already
538 # This is ok by the FHS as there is a fallback if
539 # /usr/bin/tty isn't available, for example at bootup.
540 test -x /usr/bin/tty && CONSOLE=`/usr/bin/tty`
541 test -z "$CONSOLE" && CONSOLE=/dev/console
543 # Get the console size (rows columns)
544 SIZE=$(stty size < $CONSOLE)
546 # Strip off the rows leaving the columns
551 WCOL=$[$COLUMNS - 30]
552 SET_COL="echo -en \\033[${COL}G"
553 SET_WCOL="echo -en \\033[${WCOL}G"
554 NORMAL="echo -en \\033[0;39m"
555 SUCCESS="echo -en \\033[1;32m"
556 WARNING="echo -en \\033[1;33m"
557 DARK="echo -en \\033[1;30m"
558 WHITE="echo -en \\033[1;37m"
559 BLUE="echo -en \\033[1;34m"
560 LIGHTBLUE="echo -en \\033[1;36m"
561 FAILURE="echo -en \\033[1;31m"
564 # The evaluate_retval function evaluates the return value of the process
565 # that was run just before this function was called. If the return value
566 # was 0, indicating success, the print_status function is called with
567 # the 'success' parameter. Otherwise the print_status function is called
568 # with the failure parameter.
582 # The print_status prints [ OK ] or [FAILED] to the screen. OK appears
583 # in the colour defined by the SUCCESS variable and FAILED appears in
584 # the colour defined by the FAILURE variable. Both are printed starting
585 # in the column defined by the COL variable.
592 # If no parameters are given to the print_status function, print usage
598 echo "Usage: print_status {success|failure}"
632 # The loadproc function starts a process (often a daemon) with
633 # proper error checking
640 # If no parameters are given to the print_status function, print usage
646 echo "Usage: loadproc {program}"
650 # Find the basename of the first parameter (the daemon's name without
652 # that was provided so /usr/sbin/syslogd becomes plain 'syslogd' after
656 base=$(/usr/bin/basename $1)
658 # the pidlist variable will contains the output of the pidof command.
659 # pidof will try to find the PID's that belong to a certain string;
663 pidlist=$(/bin/pidof -o $$ -o $PPID -o %PPID -x $base)
669 if [ -d /proc/$apid ]
675 # If the $pid variable contains anything (from the previous for loop) it
676 # means the daemon is already running
682 # Empty $pid variable means it's not running, so we run "$@" (all
683 # parameters giving to this function from the script) and then check the
691 # The variable $pid was not empty, meaning it was already running. We'll
696 echo -n "Already running"
703 # The killproc function kills a process with proper error checking
710 # If no parameters are given to the print_status function, print usage
716 echo "Usage: killproc {program} [signal]"
721 # Find the basename of the first parameter (the daemon's name without
723 # that was provided so /usr/sbin/syslogd becomes plain 'syslogd' after
727 base=$(/usr/bin/basename $1)
730 # Check if we gave a signal to kill the process with (like -HUP, -TERM,
731 # -KILL, etc) to this function (the second parameter). If no second
732 # parameter was provided set the nolevel variable. Else set the
733 # killlevel variable to the value of $2 (the second parameter)
744 # the pidlist variable will contains the output of the pidof command.
745 # pidof will try to find the PID's that belong to a certain string;
749 pidlist=$(/bin/pidof -o $$ -o $PPID -o %PPID -x $base)
755 if [ -d /proc/$apid ]
762 # If $pid contains something from the previous for loop it means one or
763 # more PID's were found that belongs to the processes to be killed
770 # If no kill level was specified we'll try -TERM first and then sleep
771 # for 2 seconds to allow the kill to be completed
774 if [ "$nolevel" = 1 ]
779 # If after -TERM the PID still exists we'll wait 2 seconds before
780 # trying to kill it with -KILL. If the PID still exist after that, wait
781 # two more seconds. If the PIDs still exist by then it's safe to assume
782 # that we cannot kill these PIDs.
785 if /bin/ps h $pid >/dev/null 2>&1
788 if /bin/ps h $pid > /dev/null 2>&1
791 if /bin/ps h $pid > /dev/null 2>&1
797 /bin/ps h $pid >/dev/null 2>&1
801 # If after the -KILL it still exists it can't be killed for some reason
802 # and we'll print [FAILED]
809 # It was killed, remove possible stale PID file in /var/run and
813 /bin/rm -f /var/run/$base.pid
819 # A kill level was provided. Kill with the provided kill level and wait
820 # for 2 seconds to allow the kill to be completed
823 /bin/kill $killlevel $pid
824 if /bin/ps h $pid > /dev/null 2>&1
828 /bin/ps h $pid >/dev/null 2>&1
833 # If ps' return value is 0 it means it ran ok which indicates that the
834 # PID still exists. This means the process wasn't killed properly with
835 # the signal provided. Print [FAILED]
842 # If the return value was 1 or higher it means the PID didn't exist
843 # anymore which means it was killed successfully. Remove possible stale
844 # PID file and print [ OK ]
847 /bin/rm -f /var/run/$base.pid
854 # The PID didn't exist so we can't attempt to kill it. Print [ ATTN ]
858 echo -n "Not running"
864 # The reloadproc functions sends a signal to a daemon telling it to
865 # reload it's configuration file. This is almost identical to the
866 # killproc function with the exception that it won't try to kill it with
867 # a -KILL signal (aka -9)
874 # If no parameters are given to the print_status function, print usage
880 echo "Usage: reloadproc {program} [signal]"
885 # Find the basename of the first parameter (the daemon's name without
886 # the path that was provided so /usr/sbin/syslogd becomes plain 'syslogd'
887 # after basename ran)
890 base=$(/usr/bin/basename $1)
893 # Check if we gave a signal to send to the process (like -HUP)
894 # to this function (the second parameter). If no second
895 # parameter was provided set the nolevel variable. Else set the
896 # killlevel variable to the value of $2 (the second parameter)
907 # the pidlist variable will contains the output of the pidof command.
908 # pidof will try to find the PID's that belong to a certain string;
912 pidlist=$(/bin/pidof -o $$ -o $PPID -o %PPID -x $base)
918 if [ -d /proc/$apid ]
925 # If $pid contains something from the previous for loop it means one or
926 # more PID's were found that belongs to the processes to be reloaded
933 # If nolevel was set we will use the default reload signal SIGHUP.
936 if [ "$nolevel" = 1 ]
938 /bin/kill -SIGHUP $pid
943 # Else we will use the provided signal
946 /bin/kill $killlevel $pid
952 # If $pid is empty no PID's have been found that belong to the process.
957 echo -n "Not running"
963 # The statusproc function will try to find out if a process is running
971 # If no parameters are given to the print_status function, print usage
977 echo "Usage: status {program}"
982 # $pid will contain a list of PID's that belong to a process
985 pid=$(/bin/pidof -o $$ -o $PPID -o %PPID -x $1)
990 # If $pid contains something, the process is running, print the contents
991 # of the $pid variable
994 echo "$1 running with Process ID $pid"
999 # If $pid doesn't contain it check if a PID file exists and inform the
1000 # user about this stale file.
1003 if [ -f /var/run/$1.pid ]
1005 pid=$(/usr/bin/head -1 /var/run/$1.pid)
1008 echo "$1 not running but /var/run/$1.pid exists"
1012 echo "$1 is not running"
1017 # End /etc/init.d/functions
1021 #-------------------------------/etc/init.d/functions-------------------------