Troubleshooters.Com®, Linux Library, and DIY Linux Present:
Installing Suckless Init/Felker Init Plus Daemontools-Encore on Plop Linux
Copyright © 2015 by Steve Litt
See the Troubleshooters.Com Bookstore.
Contents:
You're DIY. If you weren't DIY, you wouldn't be reading this. This material isn't easy, and it isn't something you do in an afternoon.
If others find out you installed Suckless Init on Plop Linux, many will ask you why you're wasting your time. Try as you might, you'll never get them to understand why you care about what's under the hood. Their exclusive focus is learning the latest cockpit controls --- leave what's under the hood for the developers.
That's OK, you're DIY and you've heard it all before. You're DIY and you like it that way. And you like the DIY side benefit: You're one of the vanishing breed who can systematically troubleshoot. When their trial and error and lookup and ask and speculation, guesswork and prayer don't work, and the answer isn't on linuxquestions.org or stackoverflow.com, the cockpit control aficionados come to you to troubleshoot the problem. Under the hood. And you always get your bug.
When you've reproduced the work in this document, you'll know the exact basics of an init process. You'll have a pretty good idea how to configure or troubleshoot any init process. You built one up from scratch. To get any more down and dirty you would have had to code it yourself in C.
So welcome aboard and buckle up. It's going to be a long, wild ride, and when it's done, in most init discussions, and more than a few systems-programming discussions, you'll be the smartest guy in the room.
Here's the overview of the init system you'll build in this document:
That's it. If you understand any daemontools like program, and you understand a 16 line init program that basically forks off a shellscript and then kills zombies, you understand this init system.
Of all the workable init systems for Linux, Suckless Init is probably the simplest (except for Felker Init, which is discussed at the end of this document). As a matter of fact, Suckless Init (and Felker Init) cannot even manage daemons, so it must pass that task on to a daemon manager like daemontools-encore. As previously mentioned, this document walks you through installing Suckless Init with daemontools-encore on a VirtualBox VM with Plop Linux installed. Plop Linux is one of the simplest Linux distros available. To work with this document, you need to be familiar with the following prerequesites:
I'd recommend performing this set of tasks on a VirtualBox VM. That's how I did it, it worked perfectly, and iterative experimentation was very fast. Besides VirtualBox, you'll need to download the following software:
Plop Linux comes stock initted by Sysvinit (the one with /etc/inittab and all the run scripts in /etc/rc.d/init.d with symlinks in /etc/rc.d/rc3.d). This document walks you through replacing Sysvinit, by the combination of Suckless Init plus Daemontools Encore, in discreet steps.
Plop Linux is so simple it's perfect for experimentation and proofs of concept. That's why I used it as the Linux distro for this document. To install Plop Linux on a VirtualBox VM, follow these instructions:.
Daemontools is so good and so unusual that many people have written process managers and even whole init systems that are supersets of Daemontools. Daemontools-encore is one such superset: a very modest superset. If you know daemontools, you pretty much know daemontools-encore, and vice versa. For the rest of this section, I'll just use the world "daemontools".
Daemontools manages processes. That's all it does. When a process dies, daemontools restarts it. The default way to use daemontools is to use a directory called /service. If a new direct-under-root directory violates your principles, you can put it anywhere on the root partition, even under /etc, but for the purpose of this document it's /service. Actually, it doesn't need to be under the root partition, it could be in a mounted partition, but then that partition must be mounted before starting daemontools.
Daemontools basically follows this pseudocode algorithm:
forever: foreach symlinked dir under /service: if its service isn't running: exec its run script to make it run
To make a new service, make a directory, not in the /service tree, somewhere that's mounted before daemontools starts. Let's call the new directory /root/sshd.
Note:
In reality, most folks put such directories under a common parent directory. In this document, I use /var/service as that parent. Having them all under the common parent makes things conceptually easier, and less error prone. The parent must be mounted and accessible at all times when daemontools is running. Which means that if the parent is on a non-root partition, that partition must be mounted before your calls to lk_prepare and svscanboot.
Anyway, back to the generic service creation description: Within /root/sshd, create a shellscript, executable by root, that exec's sshd in the foreground. So it would look like the following:
#!/bin/sh exec /usr/sbin/sshd -d
In the preceding run script, the -d stands for "daemon" and means that sshd stays in the foreground, which is what you want for all daemontools daemons. Note that this run script has no code for checking that processes it depends on, like the network, are already running. Such dependency checking is simple and discussed later in this document.
Anyway, once you've finished the run script, you add it to daemontools with a simple symlink command:
ln -s /root/sshd /service/sshd
Once you do that, daemontools runs sshd.
The command to run daemontools is svscanboot. The following outline shows the command tree for daemontools:
Here's a ps axf session, which is too wide for some handhelds, showing my daily driver desktop, where daemontools manages a few processes:
A few things to note about the preceding ps output:
Daemontools starts up its processes in no particular order. Many times this is a good thing, but occasionally it makes life difficult. So I wrote a set of shellscripts called LittKit that make it easy to specify the order daemontools runs its process in, when daemontools first runs.
One more thing about daemontools: It makes huge use of the Unix filesystem. Most of your daemon config is done via files and directories. There are no configuration files: Just files and directories. For instance, a file called down within a service directory tells daemontools not to start that service. The more you work with it, the more sense this makes.
Daemontools has many commands, but two are very useful to the user (or admin):
You can learn more about these two commands in other documents.
Here are some good documents to add a little more info:
To recap, here are some facts:
Download daemontools-encore from http://untroubled.org/daemontools-encore/. It is a tarball, a .tar.gz. Untar it, and follow the instructions in the README file inside the directory made by the untar. Your mileage may differ, but I didn't need to change any of the conf-* files, including the conf-bin or the conf-man. In my case, all I needed was make, followed by make install performed as user root.
For me, the only immediate evidence that the installation succeeded was that addition of several files to /usr/local/bin, including svscanboot, svscan, supervise, svstat, and svc.
You need to test that daemontools-encore really works. This is a several-step process.
First, create and permission executable by all the following /usr/local/bin/dolog.sh:
#!/bin/sh date >> /tmp/junk.log sleep 10 date >> /tmp/junk.log sleep 10 date >> /tmp/junk.log sleep 10 date >> /tmp/junk.log sleep 10 date >> /tmp/junk.log sleep 10 date >> /tmp/junk.log sleep 10
The preceding shellscript is the program to daemonize. It is on the path, and writes /tmp/junk.log every ten seconds, six times. It runs a total of a minute, updating /tmp/junk.log every ten seconds. When running, its progress can be monitored, on a separate terminal, with the following command:
tail -fn0 /tmp/junk.log
Run this program, and make sure it writes /tmp/junk.log six times and then quits.
Because this program runs for a minute and stops, it tests not only daemontools-encore's ability to start it, but daemontools-encore's ability to respawn it after it dies.
#!/bin/sh echo Starting dolog exec /usr/local/bin/dolog.sh
If everything's gone as planned, /usr/local/bin/dolog.sh is running, and every sixty seconds, after it terminates, it gets run again. Here's how to test:
tail -fn0 /tmp/junk.log
Expected output is a new line with a time ten seconds than the last, every ten seconds. However, it might occasionally skip more than 10 seconds. This corresponds to times when dolog.sh terminates and must be restarted by daemontools.
For a more complete test, create the following testjunk.sh and make it executable:
#!/bin/sh while read theline; do echo -n "$theline %%% " svstat /service/dolog done
Now, as user root (or else the following command partially errors out), run the following command:
tail -fn0 /tmp/junk.log | ./testjunk.sh
The preceding shows you, upon each and every log file output, the log entry, which consists of the time, followed on the same line by information about the service. You'll notice that one out of every six outputs, the PID changes and the uptime drops back down to zero, proving that daemontools is respawning it. If you get output like this text, which is too wide for some handhelds, you're good:
If at first it doesn't work, keep troubleshooting until it does. Keep referring to my Daemontools Intro for troubleshooting ideas, and remember, the ps command is your friend.
Being a process/daemon manager, the time for daemontools-encore to start is at boot, after everything else has been done. Because Plop Linux natively uses Sysvinit with /etc/inittab, the best way to have daemontools-encore start at boot is to put the following line as the final executable line of /etc/inittab.
Once you've done that and saved /etc/inittab, reboot the VM, run the same tests, and see if daemontools-encore continues to run the dolog service.
SV:12345:respawn:/usr/local/bin/svscanboot
Once you've done that and saved /etc/inittab, reboot the VM, run the same tests, and see if daemontools-encore continues to run the dolog service. Troubleshoot as necessary, starting with determining whether the ps command shows a process called svscanboot.
Note:
Once you've outgrown the usefulness of the dolog daemon, you can prevent it from automatically starting, and kill the currently running copy, with these three commands:
Note:
Once you've made /service/dolog a symlink to /var/service/dolog, /service/dolog and /var/service/dolog are different names for the same directory, so that changes you make in one get identically changed in the other.
This section is theory. The actual procedures are enumerated in later sections.
Plop Linux ships with the Sysvinit init system, and Sysvinit does a perfectly fine job of managing processes, at least in the unchallenging conditions of Plop Linux. Because you'll be replacing Sysvinit with Suckless Init, which does not manage processes at all, you'll need to have daemontools-encore manage all the processes formerly managed by Sysvinit. Because daemontools-encore (and most daemontools inspired software) doesn't straightforwardly allow boot time ordering of processes, and doesn't facilitate run-once type processes, you'll be adding my LittKit daemontools enhancement scripts to easily add that capability to daemontools-encore.
The fact that Plop comes from the factory with sysvinit booting means we can deduce what the init process needs to do by looking at the /etc/inittab and the contents of /etc/rc.d/rc3.d. Those resources will tell you what processes need to be run, in what order, and to some extent give you a clue as to whether the processes should be respawn-managed or run-once.
You might despair while looking at the contents of /etc/rc.d/rc3.d. Complex, ugly, and very sysvinit-centric. It's not as bad as it seems, because you don't need to make an init process that works on every possible computer: You just need to make one that works on yours. Just as one example, /etc/rc.d/rc3.d/S20network must loop through all sorts of stuff to find out the name of the network adapter. But you know, from running Plop, that the network adapter is named eth0, so you can hardcode that.
Another example: /etc/rc.d/rc3.d/S81cups is hopelessly convoluted, at 225 lines, as it ships from the Plop factory, because it needs to work with any possible computer, and probably any possible distro. That gigantic mess becomes the following daemontools-encore run script:
#!/bin/sh # NO USE RUNNING CUPS UNTIL THE NETWORK IS FUNCTIONAL if ! ping -c1 -W1 10.0.2.2 1>&2 > /dev/null; then # NETWORK NOT UP, WAIT AWHILE AND EXIT. # svscan will try again after run script exits. # 10 seconds is a good interval to wait # for the network to come up. sleep 10 exit 1 fi exec /opt/sbin/cupsd -f -c /opt/etc/cups/cupsd.conf
The preceding command is an adaptation of the command, that actually does the job, in the 225 line original /etc/rc.d/rc3.d/S81cups. The -f option keeps the program running in the foreground, which is how you run all services administered by daemontools inspired software.
Warning!
As the cupsd command ships in Plop's /etc/rc.d/rc3.d/S81cups script, the cupsd command uses an uppercase -C option, which is undocumented, instead of the lower case -c option, which means "use this config file". The upper case option causes daemontools-encore to keep spawning more and more cupsd instances, gobbling up your RAM and more importantly, preventing you from logging into http://127.0.0.1:631. If you can't log in, check the case of that parameter.
Note:
The if statement inside the run script tests for the network being up. The common ways to enforce process dependencies in daemontools-inspired programs is to test for the dependency being ready, and if it's not, sleep and exit, knowing that daemontools will retry within five seconds of the exit. Note that the 10.0.2.2 is the default address for VirtualBox VMs. If this is on bare metal, you'll need a different test to make sure the network's ready.
Notice that without the dependency checking, the run script is only two lines long. A lot different from the 225 line original.
Look at /etc/inittab. The only processes it really spawns are the virtual terminals (gettys): The agetty commands.
Now look at the /etc/rc.d/rc3.d startup scripts:
Those four are spawned in that order. Note that S50lrc.local is really a script, meant to do lots of bootup-introductory things. It's probably not meant to respawn: It's probably a run-once. Sysklogd and network spawn before it, Cups spawns after it. The Virtual Terminals certainly can spawn after it, although maybe they can spawn before it too.
The trick is to transfer all of this to management by daemontools-encore. We'll do it a step at a time...
Virtual terminals are the non-GUI terminals you get on bare metal Linux when you press Ctrl+Alt+F3 or whatever. On VM's, you go to them when you press RightCtrl+F3 or whatever, assuming that RightCtrl is the key used to remove focus from the VM. The official name for such a key is the "host key". Anyway, Virtual Terminals on Linux are normally run by respawning the agetty program.
Take a look at /etc/inittab:
# Begin /etc/inittab id:3:initdefault: si::sysinit:/etc/rc.d/init.d/rc sysinit l0:0:wait:/etc/rc.d/init.d/rc 0 l1:S1:wait:/etc/rc.d/init.d/rc 1 l2:2:wait:/etc/rc.d/init.d/rc 2 l3:3:wait:/etc/rc.d/init.d/rc 3 l4:4:wait:/etc/rc.d/init.d/rc 4 l5:5:wait:/etc/rc.d/init.d/rc 5 l6:6:wait:/etc/rc.d/init.d/rc 6 ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now su:S016:once:/sbin/sulogin 1:2345:respawn:/sbin/agetty --noclear tty1 9600 -a root 2:2345:respawn:/sbin/agetty tty2 9600 3:2345:respawn:/sbin/agetty tty3 9600 4:2345:respawn:/sbin/agetty tty4 9600 5:2345:respawn:/sbin/agetty tty5 9600 6:2345:respawn:/sbin/agetty tty6 9600 SV:12345:respawn:/usr/local/bin/svscanboot # End /etc/inittab
The only things from /etc/inittab that need transferring to daemontools-encore are the calls to /sbin/agetty.
Let's start by replacing one virtual terminal (tty). Specifically, tty2. Comment it out of /etc/inittab, and perform the following steps to get it set up with daemontools-encore:
#!/bin/bash echo Starting tty2 exec /sbin/agetty tty2 9600
Once you've gotten Virtual Terminal 2 working, do the same thing with 3 through 6. Remember to comment them out of /etc/inittab as well as putting them in daemontools-encore. Reboot, and you should have Virtual Terminal 1 still controlled by Sysvinit, but 2 through 6 controlled by daemontools-encore and working well.
One look at /etc/rc.d/rc3.d/S81cups can make you a pessimist. But you have an advantage with the following command:
[root@ploplinux ~]# ps ax | grep cupsd | grep -v grep
2879 ? Ss 0:00 /opt/sbin/cupsd -f -C /opt/etc/cups/cupsd.conf
[root@ploplinux ~]#
The preceding is basically the command you use in your daemontools-encore run script, although you'll use a lowercase -c rather than the uppercase. Do the following as user root:
#!/bin/sh # NO USE RUNNING CUPS UNTIL THE NETWORK IS FUNCTIONAL if ! ping -c1 -W1 10.0.2.2 1>&2 > /dev/null; then # NETWORK NOT UP, WAIT AWHILE AND EXIT. # svscan will try again after run script exits. # 10 seconds is a good interval to wait # for the network to come up. sleep 10 exit 1 fi exec /opt/sbin/cupsd -f -c /opt/etc/cups/cupsd.conf
The preceding steps stop Cups, prevent sysvinit from starting Cups again at runlevel 3, enable daemontools-encore to run and manage Cups, and actually start Cups.
cd /var/service
To check whether you've succeeded, perform the following command. Your output should have one supervise cupsd and one /opt/sbin/cupsd, just as shown in this output, which is too wide for some handhelds.
Once you see exactly one of each of those two commands, you can use Firefox to test Cups at 127.0.0.1:631. Be sure to try to create a new printer, to make sure Cups let's you log in as user root.
Danger !!!
Log into Cups as root, never as a lesser user. The lesser user will be refused, and Firefox won't ask you for another username and password. I had to subsequently use Chromium for web access to my local Cups server after trying to log into Cups as user slitt.
This section is all theory. Application will be covered in later sections.
We've gone as far as we can go without introducing LittKit. LittKit is a collection of shellscripts that makes startup ordering and run-once processes easy in daemontools-encore, probably daemontools itself, and likely in most daemontools-inspired software. You're going to need startup ordering and run-once in order to use daemontools-encore as the process management part of an init.
When djb created daemontools, I'm pretty sure his main purpose was to run his daemons like djbdns and qmail. I doubt he was envisioning daemontools as being part of system startup, so, to keep things simple, he didn't include these two features:
By the way, when I say "run-once capability", I mean to run the process once, and if or when the process terminates, don't do anything more. Daemontools' innate behavior is to rerun any of its managed processes that have stopped. Some daemontools-inspired software, and I think that includes daemontools-encore, have additional capabilities that do enable native run-once processing.
Theoretically, daemontools and its ideological descendents don't need startup ordering. Startup ordering could be handling with dependencies, like the if statement you remember from the Cups run script. There are two problems with handling order as dependencies:
Using dependencies this way, on bootup of any kind of substantial system, is theoretically possible but too problematic in the real world. Fortunately, there's another way to invoke startup ordering in daemontools and its friends: By installing and removing down files, in each process' directory, at just the right time. This method has four disadvantages:
So to fix these problems, I made a set of shellscripts called LittKit, which address the ordering problems in the following ways...
LittKit consists of the following shellscripts, all of which should be on the executable path:
So the boot goes something like this:
Adminning a daemontools-inspired system equipped with LittKit would be almost the same as without LittKit. Here are the differences:
So much for theory. Let's start converting to LittKit...
Like everything else, introducing yourself to LittKit is easier before you switch the PID1 part of your init. This section starts you off gently.
Installing LittKit is merely a case of copying lk_* from your LittKit distribution to a directory on your computer that's part of the executable path. Be careful though: Just because it's part of the executable path in the steady state does not mean it's on the executable path early in the boot. This document assumes your lk_* files are in /usr/local/bin.
Before beginning this exercise, it's a good idea to get a look at the pre-LittKit baseline. If you've been following along with the exercises in this document, you have Cups and tty2 through tty6 started by daemontools. Here are the relevant parts of your ps axjf command's output, which are too wide for some handhelds.
The important thing to notice is that svscanboot, whose parent's PID is 1, spawns svscan. The svscan executable, whose parent is svscanboot, spawns all the supervise processes. The supervise processes spawn the actual process like agetty and cupsd.
Now let's make a slight change. Create the following /usr/local/bin/rc.littkit:
#!/bin/sh exec /usr/local/bin/svscanboot
Make sure /usr/local/bin/rc.littkit is executable by user root.
Next, in /etc/inittab, change the reference to svscanboot to rc.littkit by copying, changing, and commenting out, as shown in the following example from /etc/inittab:
#SV:12345:respawn: /usr/local/bin/svscanboot SV:12345:respawn: /usr/local/bin/rc.littkit
What you've done is create a /usr/local/bin/rc.littkit that does nothing but exec svscanboot. Because exec simply runs the called process in the current process ID, theoretically, after booting this, ps axjf should still show svscanboot directly descended from PID1, and all the rest descended directly or indirectly from it.
Reboot now. If you've done everything right, it should boot just like before, and the relevant output of ps axjf should be the same, with svscanboot directly descended from PID1, and the ancestor of all the ttys and Cups.
Once you have that running correctly, you'll add two lines just before the exec in /usr/local/bin/rc.littkit. The revised file appears as follows:
#!/bin/sh export PATH=$PATH:/usr/local/bin lk_prepare /service exec /usr/local/bin/svscanboot
You can see the PATH and lk_prepare lines have been added. I needed to add the PATH line because, at that early point of the boot, /usr/local/bin was not on the executable path. The lk_prepare line writes down files to everything direct subdirectory of /service that doesn't contain an explicit nodown file. Because you haven't yet put a nodown file anywhere, the effect is to prevent daemontools from starting up Cups or the virtual terminals (ttys). That's not a problem, though, because /etc/inittab still directs sysvinit to run tty1.
Now that you've added those two lines to /usr/local/bin/rc.littkit, reboot. Verify that ps axjf shows an absence of the agetty and cupsd processes, although the supervise processes are all there. This is what a down file does: The directory still gets supervised, but the actual command is not spawned.
The next step is to explore nodown files. Perform the following:
touch /service/tty2/nodown touch /service/cupsd/nodown
Reboot, run ps axjf, and note that now cupsd and the getty executable for tty2 are running, and you can switch to Virtual Terminal 2. This is what the nodown file does for you: It enables a service to run even after you've run lt_prepare. If things are running as described in this paragraph, you've just proven the concept of nodown files.
In this subsection you'll do an ordered startup of daemontools-encore managed processes, even though sysvinit orders processes just fine. The purpose is to make sure you can order things before you switch to the much simpler Suckless Init.
First, delete the nodown files you put in /service/tty2 and /service/cupsd. Next, created the following /root/dtinit.sh file and make it executable by user root:
#!/bin/sh initialsleep=12 sleepafter=6 sleep $initialsleep lk_runsvc /service/tty2 $sleepafter lk_runsvc /service/tty3 $sleepafter lk_runsvc /service/tty4 $sleepafter lk_runsvc /service/tty5 $sleepafter lk_runsvc /service/tty6 $sleepafter lk_runsvc /service/cupsd $sleepafter lk_forever 10
In order to present you with observable diagnostic output, the preceding script starts the ttys in numerical order, six seconds apart, and then starts cupsd, and then goes into an infinite sleep loop (each sleep is specified to be 10 seconds). The last argument on the lk_runsvc calls is optional: It's the number of seconds to sleep after starting the service. It defaults to 1 second, and you can specify it as 0 for quicker boots if that makes sense. The reason for the initial sleep 12 is to give you a chance to log in and run a diagnostic shellscript. Obviously, the sleep 12 should be removed in production, and the "sleepafter" times of the ttys should be 1 or 0 in production.
When all is said and done, the /root/dtinit.sh script will be your main init script, and it will be executed by daemontools as the earliest-started daemontools service. It will fire off all the other daemontools services, in the desired order.
The reason for the lk_forever call at the end of /root/dtinit.sh is to have it simulate a daemon rather than a simple script. This fools daemontools into not restarting it every five seconds, so it runs just once. Keep in mind, with most newer daemontools-like implentations, such as daemontools-encore, there are less kludgy ways to achieve a run-once.
Anyway, for the time being, right now, /root/dtinit.sh is a simple diagnostic script.
In order to ascertain that it's doing what it's supposed to, you need to see the relevant ps axjf output relevant to daemontools-encore. Here's how you do it. First, create the following fromuntil.awk somewhere on your executable path, make it executable by all:
#!/usr/bin/awk -We # PUBLIC DOMAIN, NO WARRANTY function usage(args){ printf "Error: ARGC was %d, should ", args print "have been 2.\n\n" print "USAGE: cat file | fromuntil.awk " print "regex_start regex_ignore\n" print "or upto.awk regex_start " print "regex_ignore < file\n" exit 1 } BEGIN{ if((ARGC < 2)||(ARGC > 3)){ usage(ARGC) } found=0 alltheway = 1 start = ARGV[1] if(ARGC == 3){ ignorefrom = ARGV[2] alltheway = 0 } else { ignorefrom = "infinity" } ARGC = 0 } $0 ~ start { if(found == 0) found = 1 } $0 ~ ignorefrom { if((found == 1) && (alltheway == 0)) { found = 2 } } found == 1 { print $0 }
Next, put the following show_dt_procs.sh on your executable path, make it executable for all:
#!/bin/sh while true; do echo; echo; echo rm -f junk.jnk ps axjf > junk.jnk cat junk.jnk | grep -v readproctitle | \ fromuntil.awk svscanboot readproctitle rm -f junk.jnk sleep 2 done
Why the Kludge?
Why did the preceding code redirect ps axjf into junk.jnk, and parse junk.jnk? Why not simply pipe the output of ps axjf into fromuntil.awk?
The answer is that on Plop Linux in a VirtualBox VM, the direct pipe produces no output, for reasons I don't have time to fully investigate. Hence the kludge.
Finally, on the executable path, create and make executable by all the following file called jjj
#!/bin/sh /root/dtinit.sh & show_dt_procs.sh
jjj is what you'll run the instant you reboot, in order to observe the startup created by /root/dtinit.sh.
Once again, make sure you've deleted all the nodown files, and reboot. You should emerge from the reboot with no ttys spawned by daemontools (remember, tty1 is still spawned by sysvinit), and no cupsd spawned by daemontools. To test that, run the following command:
test_dt_procs.sh
The preceding should, over an extended period (as long as you'd like, actually), show svscanboot spawning svscan, which spawns supervise for tty2-6 and the cupsd service, but those supervise commands spawn nothing, so cupsd and the ttys are not really running. This is LittKit performing as designed: lk_prepare put down files in all the directories without noboot files, and you previously deleted all the noboot files, didn't you?
Now watch what happens as the ordered startup specified by /root/dtinit.sh is run. Perform the following command:
jjj
If all is good, you'll start out seeing no supervise processes spawning anything, and then 12 seconds later the supervise process for tty2 spawns agetty, and then every 6 seconds the next numerically higher tty spawns its agetty, and last but not least, the supervise for cupsd specifies its cupsd process.
Once you get things to work as in the preceding paragraph, you've proven your ability to perform an ordered start on daemontools-encore. But wait, there's more.
The proof of concept was nice, but what you really want is for daemontools-encore to run the ordering script itself!
The solution's conceptually simple:
Now let's do it:
#!/bin/sh exec /root/dtinit.sh
Now, just for confirmation, perform the actions in the following session, and compare your results:
[root@ploplinux ~]# cd /service [root@ploplinux service]# svstat * cupsd: up (pid 2170) 256 seconds, running dtinit: up (pid 1797) 348 seconds, running tty2: up (pid 1857) 336 seconds, running tty3: up (pid 1921) 320 seconds, running tty4: up (pid 1978) 304 seconds, running tty5: up (pid 2042) 288 seconds, running tty6: up (pid 2106) 272 seconds, running [root@ploplinux service]#
In the preceding, notice that all services are up and running. Notice that the service that's been up for the longest is dtinit, and it's 12 seconds older than service tty2, which made sense because in my /root/dtinit.sh, I had set my $initialsleep variable to 12 seconds and my $sleepafter variable to 16 seconds. Notice in the preceding session printout, the intervals correspond to my $initialsleep and $sleepafter variables, and their age is in descending order of their calls in /root/dtinit.sh. This is proof that daemontools-encore ordered its own startup, with a little help from LittKit.
Stuff happens. Things go wrong. If the preceding didn't work, start by making sure you really followed the instructions. Is svscanboot really running, and is there only one of them? Is /service/dtinit a symlink to /var/service/dtinit? Is /var/service/dtinit/run executable, and does it end with an exec statement? Did you remove the call to /root/dtinit.sh from /usr/local/bin/jjj?
Beyond the preceding paragraph, you'll always troubleshoot productively if you believe the Troubleshooter's Philosophy and consistently perform the Troubleshooter's Mantra after every diagnostic test.
Troubleshooter's Philosophy
Don't try to fix it, just try to narrow it down.
Troubleshooter's Mantra
How can I narrow it down just one more time?
The Troubleshooter's Philosophy and Troubleshooter's Mantra are right out of my instructional materials on Troubleshooting, and they work wonders.
Here are a few references that might be helpful in troubleshooting daemontools-encore and related problems:
LittKit consists of several shellscripts and a couple reserved filenames (nodown and reallydown). LittKit is syntactic sugar to make hacking of down files, for the purpose of controlling daemontools startup order, appear less kludgy.
You've now used LittKit to have daemontools-encore control its own startup order. This is very helpful, if not vital, in a bootup/init situation. You'll see that when we finally substitute Suckless Init for Sysvinit on Plop Linux.
Many, perhaps most of the services run by daemontools-encore, require the network to be up. Cups and sshd are just a couple. This is so common that, to save time and writing in various run files, you'll make a program called netisdown, so within your span.run scripts you can simply do the following:
#!/bin/sh if netisdown; then sleep 10 exit 1 fi exec /opt/sbin/cupsd -f -c /opt/etc/cups/cupsd.conf
The following is the code for /usr/sbin/netisdown:
#!/bin/sh ping -W 1 -w 1 -c1 10.0.2.2 > /dev/null rtrn=$? if test "$rtrn" = "0"; then rtrn=1 elif test "$rtrn" = "1"; then rtrn=0 else ### SHOULD NEVER HAPPEN rtrn=0 ### SOMETHING MUST BE WRONG, SAY ITS DOWN. fi exit $rtrn
The netisdown program will be used a great many times throughout this document.
Now that we have a self-ordering daemontools-encore, let's transfer some more processes and commands from the infamous /etc/rc.d/rc3.d/S50lrc.local to daemontools-encore. For the most part, we'll start from the end of the file and work back. First subject, the call to alsactl restore.
#!/bin/sh /opt/sbin/alsactl restore lk_forever 3600
By the way, this might be a good time to crank down the $initialsleep in /root/dtinit.sh to 4, and the $sleepafter to 2. Eventually you'll hand craft the file and make most of them either 1 or 0, but for the time being, try 4 and 2.
Now do the same for acpid:
#!/bin/sh exec /usr/sbin/acpid -f
Danger!
Notice the -f on the end of the run command. That -f runs acpid in the foreground, which is how all daemontools-inspired programs are designed to run processes. Without the -f, you'd have some mighty strange symptoms and a hard time troubleshooting.
named is a DNS lookup engine. This is a tiny bit tougher because /etc/rc.d/rc3.d/S50lrc.local runs a program called startnamed rather than named, and the fact that to run in the foreground, named needs a -f argument. A which command quickly finds /usr/bin/startnamed, which looks like the following:
echo Starting named named -t /var/named/chroot -u named
OK, the first echo isn't necessary in this "Hello World". Before moving this to daemontools-encore, let's see what the second line, with a -f inserted right before the -t. Run it manually. It doesn't put itself in the background, but instead does nothing until you press Ctlr+C. Good, you'll want the -f. So perform the following steps:
#!/bin/sh if netisdown; then sleep 10 exit 1 fi exec named -f -t /var/named/chroot -u named
When you check, be sure to ping 8.8.8.8 to determine connectivity, and then use the lynx browser to browse to different websites. If you can browse to a website by domain name, that's pretty good proof that named is working.
This is /usr/bin/startdbus-daemon, and is a shellscript that does a bunch of simple stuff and then calls dbus-daemon. Here's the original shellscript:
#!/bin/sh echo Starting dbus-daemon killall -HUP dbus-daemon >& /dev/null rm -rf /var/run/dbus mkdir -p /var/run/dbus dbus-daemon --system
So we're pretty much going to just copy that into the run script, as follows:
#!/bin/sh killall -HUP dbus-daemon >& /dev/null rm -rf /var/run/dbus mkdir -p /var/run/dbus exec dbus-daemon --nofork --system
Notice the --nofork in the final command of the preceding run script? That's so it runs in the foreground, like all good daemontools processes should. On another subject, as far as I know, dbus doesn't require the network, and I don't know what process dependencies it might have, so I'm not putting any if statements in the run script. Now follow these steps:
The best way of checking is with ps axjf | less, in CLI mode (before running any X, because a lot of modern window managers or desktop environments run dbus).
This one's pretty simple, as long as you remember that to run sshd in the foreground, you use the -d option (stands for "debug"). So it's what you've come to expect:
#!/bin/sh if netisdown; then sleep 10 exit 1 fi exec /usr/sbin/sshd -d
The remainder of /etc/rc.d/rc3.d/S50lrc.local is just run-once type stuff that belongs in a "rc" type script. It's fairly early stuff too.
So we're going to put it all in a run-once service called "bringup". Like all run-once services in this document, we'll terminate it with lk_forever, although, as mentioned many times, with something as sophisticated as daemontools-encore there are better ways of doing it.
This "bringup" service will be added to, more and more, until it replaces the early calls in /etc/rc.d/rc3.d/. Once that's done, the main remaining need for sysvinit is for accurate, non-destructive shutdowns.
But for now, we're just moving the remainder of /etc/rc.d/rc3.d/S50lrc.local into the "bringup" service. Perform the following steps:
#!/bin/sh PATH=$PATH:/opt/bin:/opt/sbin mkdir -p /dev/pts mount -t devpts devpts /dev/pts mkdir -p /dev/shm mount -t tmpfs tmpfs /dev/shm ifconfig eth0 up dhclient eth0 lk_forever 3600
I have some misgivings about the path here. I think the path modifier statement might need to be moved back to the /root/dtinit script. We'll see.
In the preceding section, I ran dhclient inline with a script. That kinda-sorta works, but dhclient is really a daemon that should be managed with daemontools-encore. So delete it from the /service/bringup/run script and give it its own service:
#!/bin/sh exec dhclient -d -q eth0 > /dev/null
All this time we've been moving functionality from sysvinit to daemontools-encore, and we've done it quite well. When it's all moved over, theoretically we can copy the content of /usr/local/bin/rc.littkit to /bin/rc.init, boot into Suckless Init, and bang, we're initting via Suckless Init. There are many small problems with that view of the world, and one huge problem: What to put in /bin/rc.shutdown, the program used to shut down the computer, either by reboot or poweroff. Write that script wrong, and you can corrupt your hard disk. Probably nothing you can't journal or fsck your way out of, but not pleasant.
Rather than go into a huge discussion, let me give you the code I used for /bin/rc.shutdown. But before I do, there's one gotcha you should know about: This script depends on killall5, which is a sysv component. If you don't have killall5, you're probably going to need to modify Suckless Init to run /bin/rc.shutdown as part of PID1, instead of forking it. I'll discuss that in other documents. Meanwhile, if you're following along with Plop Linux, you do have killall5, so the following is a reasonable /bin/rc.shutdown:
#!/bin/sh killall515delay=4 killall59delay=4 waitsecs(){ numsecs=$1 echo -n "Wait $numsecs seconds please " while test $numsecs -gt 0; do sleep 1 echo -n "." let numsecs=$numsecs-1 done echo } shutdown_mode=$1 ## CAPTURE "poweroff" or "reboot" if test $# -lt 1; then shutdown_mode="poweroff" fi echo "Killing non-tty daemontools-encore services..." lk_killsvc /service/cupsd 0 lk_killsvc /service/alsactl_r 0 lk_killsvc /service/acpid 0 lk_killsvc /service/named 0 lk_killsvc /service/dbus 0 lk_killsvc /service/sshd 0 lk_killsvc /service/dhclient 0 lk_killsvc /service/bringup 0 echo "Unmounting /dev/pts and /dev/shm..." umount /dev/pts umount /dev/shm echo "Downing the network..." ip link set dev lo down ip link set dev eth0 down echo "Killing klogd and syslogd..." killall klogd killall syslogd echo "killall5 -15 (requesting)..." killall5 -15 waitsecs $killall515delay echo "killall5 -9 (murdering)..." killall5 -9 waitsecs $killall59delay echo "swapoff -a..." swapoff -a echo "remounting read-only /dev/hda1..." mount -o remount,ro /dev/hda1 if test "$shutdown_mode" = "reboot"; then echo "rebooting..." /sbin/reboot else echo "powering off..." /sbin/poweroff fi
Once you can shut down with the preceding rc.shutdown, without having all sorts of partition journal messages when you boot up again, you're ready to install and try out Suckless Init.
We've done everything we can without installing Suckless Init. Like all Suckless Tools, Suckless Init is trivial to install if you accept the defaults, which is what I did. To install Suckless Init, perform the following steps:
The key distinction of Suckless Init is that it was, and I quote the README file, "sinit is a simple init. It was initially based on Rich Felker's minimal init[1]."
Now, looking over at the blog entry in which Rich Felker exposed his init and gave it a free software license. Rich spends a great deal of time on the subject of "get everything unnecessary for PID1 out of PID1." His 16 line init program does no process management at all. He makes it clear that can be done elsewhere, by another program that isn't PID1 and doesn't have PID1's special privileges and responsibilities. Smarter people than I argue both sides of this "sparse PID1" principle, but until someone can convince me otherwise, I'm a big believer in the sparse PID1 principle.
Now comes Dimitris Papastamos, the author of Suckless Init, who obviously also believes in the "sparse PID1" principle, because his 89 line init basically forks off /bin/rc.init, then spends the rest of the OS' uptime handling signals. Process management is done by /bin/rc.init or something it runs or forks. Sparse PID1. In the case of the document you're reading, /bin/rc.init runs lk_prepare and then execs daemontools.
A special thanks to Rich Felker and Dimitris Papastamos, who both stood behind their belief in a sparse PID1.
Anyway, you've already compiled Suckless Init. Spend a few minutes looking at config.h and sinit.c. This section is a very basic overview of what sinit.c does. Starting at the highest level, here's what sinit.c does:
Each iteration of the infinite signal inspection loop contains the following steps:
To put it in a sentence, sinit.c operates as PID1, and stops all interrupts from interrupting it, forks off /bin/rc.init, and then spins forever, handling any signals it receives.
One more sentence explains the end of the infinite loop: Signals SIGUSR1 and SIGINT cause sinit.c to fork /bin/rc.shutdown, which kills all processes including PID1, thus shutting down the computer.
Here's the bottom line: You just saw a perfectly functional PID1 written in 89 lines of C code. Try not to laugh when, during systems programming bull sessions, someone confidently and authoritatively assures you that PID1 is necessarily tremendously complicated. Just silently smile as you remember hooking up this 89 line C PID1 to daemontools-encore and LittKit, to make a perfectly functional init system.
Up to this moment, we've transferred services and commands from sysvinit to daemontools-encore, starting with the latest and going through the earliest, at least the earliest in /etc/rc.d/rc3.d/. It looks like we've relieved sysvinit of all process management duties (we haven't, but it looks that way), so the remaining challenge is to boot to Suckless Init, and then call daemontools-encore.
Learn from my misfortunes --- things go wrong. Therefore, your first use of Suckless Init should do nothing but bring you to a bash prompt so that you can look around and experiment. If you were to try init right into lk_prepare and svscanboot, I think it highly likely that some little thing you forgot would cause you to boot to a state from which you have no input method.
To boot to a bash-only state, perform the following steps:
#!/bin/sh echo;echo;echo;echo;echo echo PRESS ENTER TO GET COMMAND PROMPT!!!!!! echo;echo;echo;echo;echo /bin/bash exit 0
If you've installed Suckless Init correctly, booting the hard disk tells you to press the Enter key to get a bash prompt, and when you do, you get a bash prompt, overseeing a very defective Linux:
Look at the bright side. You just initted with Suckless Init. That's huge. All you need to do is solve the problems listed above, then run lk_prepare and then svscanboot. It's not all that hard.
The big problems preventing you from running lk_prepare and then svscanboot are an inadequate $PATH and a read-only filesystem. Before remounting the filesystem read-write, it's best to take care of /proc and /sys. And you might as well turn on swap right after /proc and /sys are taken care of.
So, from your defective command prompt, perform the following steps manually:
Note:
If you wondered what hat I pulled the preceding steps out of, what I did was look in all the startup files under directory /etc/rc.d/rcsysinit.d. I found that directory from tracing everything. Notice the the preceding steps implement only the most vital elements of the startup code under /etc/rc.d/rcsysinit.d. One thing left out is the facility for periodic fsck runs. For the time, every once in a while boot to a live CD and fsck /dev/hda1, umounted.
Now look around. Notice you can create files on the root tree. The mount command with no arguments gives you a list of mounts. You have a working /proc and /sys directory. Look around. Get used to this environment.
Once you've explored, perform the following two commands:
With the preceding two commands performed, you should have a pretty much functional Linux box, complete with networking, an SSH server, and all the usual suspects. This is because you just ran daemontools-encore, with your ordered startup, and in doing so you ran everything that you transferred from sysvinit to daemontools-encore.
Which leaves only one remaining question: How do you shut this thing down, now that you don't have sysvinit anymore? Here are the two ways:
Danger!
Do not attempt to Ctrl+Alt+Del your VM without first performing the following command:
echo 0 > /proc/sys/kernel/ctrl-alt-del
0 in that file indicates Ctrl+Alt+Del gets delivered as a SIGINT to PID1, which then performs rc.shutdown. If that file doesn't contain 0, then Ctrl+Alt+Del just reboots the computer, without closing and unmounting and the like. You can lose data. Plop Linux puts a 1 in this file upon every bootup.
Note:
Remember that when you're working inside a VirtualBox VM, the way to perform a Ctrl+Alt+Del is to perform HostKey+Del, where HostKey is the key to escape the VM's focus. It defaults to RightCtrl. But reading the preceding danger warning before performing any kind of Ctrl+Alt+Del.
Now that you can boot up to a bash session and issue the proper commands to fully instantiate all services via daemontools-encore, the only thing left to do is put those proper commands into /bin/rc.init, and comment out the call to /bin/bash, the call to exit 0, and the echo commands about pressing Enter. Once you've done these tasks, your rc.init should look something like this:
#!/bin/sh export PATH=$PATH:/usr/local/bin #echo;echo;echo;echo;echo #echo PRESS CTRL+F2 TO LOG IN!!!!!!! #echo;echo;echo;echo;echo #/bin/bash #exit 0 ### FROM S00mountkernfs mount -n /proc mount -n /sys ### from s20swap swapon -a ### from S40mountfs mount -n -o remount,rw / &>/dev/null # Remove fsck-related file system watermarks. rm -f /fastboot /forcefsck #Recording existing mounts in /etc/mtab... mount -f / mount -f /proc mount -f /sys #Mount everything not depending on thenetwork mount -a -O no_netdev &>/dev/null ###### TELL USER TO GO TO A VIRTUAL TERMINAL echo;echo;echo;echo;echo echo PRESS CTRL+F2 TO LOG IN!!!!!!! echo;echo;echo;echo;echo ###### RUN DAEMONTOOLS WITH LITTKIT lk_prepare /service /usr/local/bin/svscanboot
This section contains the final result of the conversion of Plop Linux from sysvinit to Suckless Init plus daemontools-encore plus LittKit:
Upon receiving a SIGINT or SIGUSR1, Suckless Init forks /sbin/rc.shutdown with an argument of either "reboot" or "poweroff". Here's what /sbin/rc.shutdown looks like:
#!/bin/sh killall515delay=4 killall59delay=4 waitsecs(){ numsecs=$1 echo -n "Wait $numsecs seconds please " while test $numsecs -gt 0; do sleep 1 echo -n "." let numsecs=$numsecs-1 done echo } shutdown_mode=$1 ## CAPTURE "poweroff" or "reboot" if test $# -lt 1; then shutdown_mode="poweroff" fi echo "Killing daemontools-encore services..." lk_killsvc /service/cupsd 0 lk_killsvc /service/alsactl_r 0 lk_killsvc /service/acpid 0 lk_killsvc /service/named 0 lk_killsvc /service/dbus 0 lk_killsvc /service/sshd 0 lk_killsvc /service/dhclient 0 lk_killsvc /service/bringup 0 echo "Unmounting /dev/pts and /dev/shm..." umount /dev/pts umount /dev/shm echo "Downing the network..." ip link set dev lo down ip link set dev eth0 down echo "Killing klogd and syslogd..." killall klogd killall syslogd echo "killall5 -15 (requesting)..." killall5 -15 waitsecs $killall515delay echo "killall5 -9 (murdering)..." killall5 -9 waitsecs $killall59delay echo "swapoff -a" swapoff -a echo "remounting read-only /dev/hda1..." mount -o remount,ro /dev/hda1 if test "$shutdown_mode" = "reboot"; then echo "rebooting..." /sbin/reboot else echo "powering off..." /sbin/poweroff fi
In its default configuration, upon startup Suckless Init forks off /bin/rc.init. Here's what /bin/rc.init looks like:
#!/bin/sh export PATH=$PATH:/usr/local/bin #echo;echo;echo;echo;echo #echo PRESS CTRL+F2 TO LOG IN!!!!!!! #echo;echo;echo;echo;echo #/bin/bash #exit 0 ### FROM S00mountkernfs mount -n /proc mount -n /sys ### from s20swap swapon -a ### from S40mountfs mount -n -o remount,rw / &>/dev/null # Remove fsck-related file system watermarks. rm -f /fastboot /forcefsck #Recording existing mounts in /etc/mtab... mount -f / mount -f /proc mount -f /sys #Mount everything not depending on thenetwork mount -a -O no_netdev &>/dev/null ###### TELL USER TO GO TO A VIRTUAL TERMINAL echo;echo;echo;echo;echo echo PRESS CTRL+F2 TO LOG IN!!!!!!! echo;echo;echo;echo;echo ###### RUN DAEMONTOOLS WITH LITTKIT lk_prepare /service /usr/local/bin/svscanboot
This setup uses two scripts, in directory /usr/local/bin, to check network status:
netisdown follows:
#!/bin/sh ping -W 1 -w 1 -c1 10.0.2.2 2> /dev/null 1> /dev/null rtrn=$? if test "$rtrn" = "0"; then rtrn=1 elif test "$rtrn" = "1"; then rtrn=0 else rtrn=9 fi exit $rtrn
And the following is dhcpisready
#!/bin/sh ### KLUDGE, IFCONFIG IS DEPRECATED. if ifconfig eth0 | grep "\s*inet addr:" > /dev/null; then exit 0 else exit 1 fi
Both of the preceding scripts are typically used in if statements and loops within other shellscripts, for timing and process dependency.
The final two tasks of /bin/rc.init are to run lk_prepare /service and to run /usr/local/bin/svscanboot, which starts up daemontools. Because it's the only subdirectory under /service with a nodown file, the dtinit service runs first. Daemontools runs it first. Here's the /service/dtinit/run script's code:
#!/bin/sh exec /root/dtinit.sh
That's right. The dtinit service does nothing but run /root/dtinit.sh in the dtinit service's own process with the dtinit service's own PID. Obviously, the contents of /root/dtinit.sh should be copied to the dtinit service, after which the dtinit service should not call /root/dtinit.sh. The reason I've left /root/dtinit.sh separate is in case I need to swap sysvinit back in.
/root/dtinit.sh gets executed by the dtinit service. /root/dtinit.sh starts other daemontools services, one at a time, in order, and can also have delays to make sure things are up before continuing. The code for /root/dtinit.sh follows:
#!/bin/sh initialsleep=0 sleepafter=0 lk_runsvc /service/tty2 $sleepafter sleep $initialsleep lk_runsvc /service/syslogd $sleepafter lk_runsvc /service/klogd $sleepafter lk_runsvc /service/tty3 $sleepafter lk_runsvc /service/tty4 $sleepafter lk_runsvc /service/tty5 $sleepafter lk_runsvc /service/tty6 $sleepafter lk_runsvc /service/bringup $sleepafter lk_runsvc /service/dhclient $sleepafter while ! /usr/local/bin/dhcpisready; do sleep 1 echo waiting for network to come up done lk_runsvc /service/sshd $sleepafter lk_runsvc /service/dbus $sleepafter lk_runsvc /service/named $sleepafter lk_runsvc /service/acpid $sleepafter lk_runsvc /service/alsactl_r $sleepafter lk_runsvc /service/cupsd $sleepafter #lk_runsvc /service/tty1 $sleepafter lk_runsvcs /service $sleepafter lk_forever 3600
The preceding brings up an early tty, tty2. For practical purposes, the other tty's come about the same time, but they don't need to. They could be much later, and as long as you have one early tty, you can access the computer to fix it.
The commented out reference to tty1 is for ease in troubleshooting through repeated reboots. In such a case, you'd uncomment the reference in /boot/dtinit.sh and you'd make a tty1 service that uses the -a root argument for agetty in order that you don't need to repeatedly log in. Once you're done with troubleshooting, you can create file /service/tty1/reallydown file so that it doesn't come up, and re-comment it in /boot/dtinit.sh.
The TTYs are virtual terminals, the command line interfaces you get to with Ctrl+Alt+F2 through Ctrl+Alt+F6. They're created by respawning the agetty command. The code for /service/tty6/run follows:
#!/bin/sh exec /sbin/agetty tty6 9600
You can clone the preceding to /service/tty2 through /service/tty5, obviously changing the first argument of the /sbin/agetty command accordingly.
Reminder:
You're not really creating /service/tty6 or any of the others. You're creating /var/service/tty6, and then making /service/tty6 a sylink to /var/service/tty6. Remember also that the constructed services don't need to be under /var/service, they could be under anything that's on the root partition and is readable and writeable.
Note:
The reason I included the 9600 baud rate in all the ttys is because that's how they were in the original /etc/inittab. The baud rates really aren't necessary in virtual terminals, and once you get everything running well you can simply delete the baud rates from the tty run commands.
As discussed before, you might want to make a tty1 for the purpose of instant login for troubleshooting through repeated reboots. If you do, it would look like this:
#!/bin/sh echo;echo; exec /sbin/agetty -noclear --noissue -a root tty1 9600
For security reasons, obviously you need to disable this no-login terminal once you're done troubleshooting.
#!/bin/sh exec syslogd -n -m 0
#!/bin/sh exec /sys/klogd -n
#!/bin/sh PATH=$PATH:/opt/bin:/opt/sbin mkdir -p /dev/pts mount -t devpts devpts /dev/pts mkdir -p /dev/shm mount -t tmpfs tmpfs /dev/shm ##### BEGIN NETWORK SETUP hostname -F /etc/hostname ip link set dev lo up ip link set dev eth0 up ##### END NETWORK SETUP lk_forever 3600
#!/bin/sh exec dhclient -d -q eth0 > /dev/null
#!/bin/sh if netisdown; then sleep 10 exit 1 fi exec /usr/sbin/sshd -d -q
#!/bin/sh killall -HUP dbus-daemon >& /dev/null rm -rf /var/run/dbus mkdir -p /var/run/dbus exec dbus-daemon --nofork --system
#!/bin/sh if netisdown; then sleep 10 exit 1 fi exec named -f -t /var/named/chroot -u named
#!/bin/sh exec /usr/sbin/acpid -f
#!/bin/sh /opt/sbin/alsactl restore lk_forever 3600
#!/bin/sh # NO USE RUNNING CUPS UNTIL THE NETWORK IS FUNCTIONAL if netisdown; then # NETWORK NOT UP, WAIT AWHILE AND EXIT. # svscan will try again after run script exits. # 10 seconds is a good interval to wait # for the network to come up. sleep 10 exit 1 fi exec /opt/sbin/cupsd -f -c /opt/etc/cups/cupsd.conf
Curiosity killed the kitty, and I just had to find out whether I could swap Rich Felker's 16 line init program instead of Suckless Init. First step, read the source code. Rich Felker's appears more clever, Suckless Init appears more like the straightforward code I write, but a few minutes of examination indicated to me that the main functional difference is that Suckless Init responds to incoming signals, whereas Rich Felker's Init just ignores them. Which also means that Rich Felker's Init needn't have code to deal with signals, like invoking /bin/shutdown or running the reap() function. With Rich Felker's Init, you need to run /bin/rc.shutdown manually.
Rich Felker's Init's failure to respond to kill commands gives us a method of determining which init we're working with. Cool!
So what I did was this: I compiled Rich Felker's code:
gcc -o felker.bin -Wall felker.c
I kept running the preceding until I got no errors, no warnings. I had to add #include<wait.h> in order to kill the warning.
Next, I copied felker.bin to /f. This makes explicit booting from the LILO prompt easier.
Then, because felker.c forks off /etc/rc, I created and made executable the following /etc/rc:
#!/bin/sh exec /bin/rc.init
The preceding means that running /etc/rc is, for all practical purposes, just like running /bin/rc.init. The reason I didn't simply change Felker's code to call /bin/rc.init directly is I suspected I might need some diagnostic commands specifically when running Rich Felker's Init.
So anyway, I rebooted, and at the LILO prompt I typed:
ploplinux init=/f
It booted up exactly as with Suckless Init, but when I used kill to send PID1 a SIGINT or SIGUSR1, instead of rebooting or powering off like Suckless Init would, it did nothing at all. This proves I was initted to Rich Felker's Init. I powered it off by typing the following command as root at the command prompt:
rc.shutdown poweroff
Rich Felker's Init was a success on my Plop system. Which brings up the question: "Should I init with Rich Felker's Init, or with Suckless Init?" The tale of the tape shows felker.c with 16 non-blank lines of code, and sinit.c with 76 non-blank lines of code. One way of looking at it is that felker.c has 450% more code. Another way of looking at it is they're both tiny and both very understandable. To me, length isn't an issue in either one. Suckless Init gives me signal handling, and even gives me the opportunity to have PID1 react to additional signals. I think that would be unwise of me, but I could do it.
So for demonstration of a really tiny init, I'd use Rich Felker's Init. For something I'd actually use, assuming there was some reason I wouldn't use runit, s6 or Epoch, I'd use Suckless Init.
Congratulations! You've 100% replaced your old sysvinit init system with a combination of the 89 line Suckless Init, daemontools-encore, and LittKit. You now have an instinctual feel for what PID1 does, and what process management does, and how the first passes off to the second. You've seen first hand how PID1 is responsible to hang around forever, listening to signals, and when it gets the appropriate signal, it must shut down the computer: In this case by forking off /bin/rc.shutdown with an argument of either poweroff or reboot
You feel at home in those init discussions where they argue whether process management should be done by PID1, or by a forked process (in this case /bin/rc.init calling daemontools-encore).
In a "can't boot" situation, you understand how to replace the current init with /bin/bash to see whether boot even got to the hard disk init, or whether it failed in the bootmanager or the initramfs. This gives you troubleshooting tools others just don't have. If need be, you can install Suckless Init and a proper /bin/rc.shutdown, and use that environment to further investigate problems happening at or after the original disk init.
If you ever decide that the init system your distro gives you doesn't fit your needs, what you did in this document gives you the confidence and knowledge to install alternative init systems, especially daemontools-inspired init programs runit and s6.
Congratulations. Seriously. I'll bet you less than 1/10 of those who started reading this doc got past the third section. I'll bet you that of those who got past the third section, 19 out of 20 gave up before the end, because they considered it too tedious. That leaves you, the 1 out of 200, the elite 0.5%, DIY enough to complete this, and really understand the init part of what's under the Linux hood. In a world where people think they're "technical" because they've learned the cockpit controls of the latest technology, you know you're the real deal because you can operate under the hood.
[ Training | Troubleshooters.Com | Email Steve Litt ]