Ninit instructions

From Without Systemd
Jump to: navigation, search

These instructions detail how to set up the ninit init system. It can be used for users of both Systemd and non-Systemd Linux (and possibly Unix) systems.

If you are replacing Systemd with Ninit, then you should follow this article closely (unless you're an expert). If you're not migrating from Systemd, then obviously the systemd-specific instructions don't apply to you, but everything else does.

The steps, broadly, are:

  1. Uninstall systemd
  2. Install a "normal" init system like SysVinit as a starting point and "safety net"
  3. Build and install ninit
  4. Configure ninit
  5. Tweak ninit


Contents

Init Systems Explained

Traditionally, the init (initialisation) system is a series of steps that a unix-like OS takes in order to get from being powered down to being a functional operating system. A quick-and-dirty tour goes a little something like this:

  1. The computer receives power.
  2. Inbuilt firmware on a motherboard triggers a probe of harddrives for a recognisably bootable partition in currently defined boot device (usually a hard drive, flagged as the primary boot device in BIOS or UEFI)
  3. The code on the boot device is executed. For Linux. this is usually a boot loader such as GRUB or LILO.
  4. The kernel is loaded into memory.
  5. The init sequence, as defined by the init application in /sbin, is launched. This usually means that a sequence of smaller daemons are launched such that important hardware becomes available to the computer and the user.
  6. A login shell or desktop login is presented to the user.

The stuff that goes on in the motherboard is external of the OS, and still mostly proprietary. The boot loader is open source and available, but not part of the init sequence, as it is deciding which init sequence to spawn. The init process, proper, begins after the kernel has been decompressed and loaded into memory, at which point some executable binary on the system needs to be launched; traditionally, the application that gets executed is /sbin/init.

You can see this on your own system right now:

ps -p 1

As you can see, PID 1 is occupied by init (or systemd, depending on whether you've removed it yet).


Thoroughly Modern Init Systems

The appeal in using a "modern" init system (instead of sysVinit) is twofold: dependency-based initialisation of services, and process monitoring or service supervision.

Dependency-based initialisation is significant because processes that might take a long time to start (such as successfully getting a DHCP address) can not only delay the entire boot process, but it could also theoretically cause services that rely on having a network connection to fail.

Service supervision, rather than just launching a service once at boot time, creates a monitoring system that watches each service (or each one marked as supervisable) and ensures that it gets re-launched should it crash.

So while it's an important process itself, there's nothing magical about init or an init system; it's just another executable application on your system which happens to be the first thing launched after you power up your computer. This means that you are free to use something other than what shipped with your distribution.

Before Ninit

If you are migrating away from Systemd on Debian or similar, you should uninstall Systemd and install SysVinit before trying ninit. It seems redundant to switch from Systemd to SysVinit to ninit, but the default init scripts that get installed with SysVinit will be useful later.

Again, for emphasis:

Install SysVinit (or some native init process) on your machine before trying ninit. It will make the process much easier!


Getting Ninit

Ninit is a fork of the minit project. Your repository may offer ninit as an installable package. If not, download tho stable release of the source code. Its original website has more or less vanished, so at least for some time you can use a backup location (though there is no guarantee that the backup copies that exist will be maintained over time):

$ git clone https://gitlab.com/notklaatu/ninit

or

$ git clone https://gitlab.com/ShadowKyogre/ninit


Building Ninit

Ninit has the option of compiling against dietlibc, an optimised C library (as opposed to glibc, for instance), but unless you have a particular reason for doing that, it's a little simpler to just compile against the C library you already have on your system:

$ make nodiet

To make sure everything worked as expected, run the inbuilt tests:

make tests

And finally, assuming all went well, install the application. How you choose to install will depend on your distribution and how you manage your own software. If your system uses a database-driven package manager, like RPM, APT, or similar, use the application checkinstallto generate an RPM or DEB package for you. Your repository should have checkinstall available for install, but if not get it from the website directly.

Once checkinstall is installed, just run it from within the ninit source directory:

$ checkinstall

Follow the prompts to create a package and install ninit.

If you're trying ninit out on Slackware, the native packaging tools do basically the same thing:

$ mkdir /tmp/ninit $ make install distro $ cd /tmp/ninit.distro $ su -c 'makepkg -l y -c -n ninit-0.14-`uname -m`.tgz' $ su -c 'installpkg /tmp/ninit*tgz'

Do not uninstall SysVinit yet. You want it to still be active so you can still reboot if you need to.


Configuring Ninit

Part of what makes ninit so easy to try is its very good init script configuration script, ninit-inittab. As long as your current system uses /sbin/init as its init process (that is, anything but systemd), you can easily get the basic scripts required for a basic, minimal login. This only works if you installed SysVinit (or some other native init system) before trying ninit; if you are coming straight from systemd to ninit, there are no init scripts for this script to convert, so you will have to make them all yourself.

Assuming you did install some intermediary init system after uninstalling systemd (like this article advised), then you can run:

  1. mkdir /etc/ninit
  2. /sbin/ninit-inittab /etc/inittab /etc/ninit /tmp/ninit.sh
  3. /tmp/ninit.sh

This establishes the very basic necessities for a bootable system, specifically the creation of a TTY (the text login screen you see when you hit ctrl-alt-F{1,2,3,4,5,6}), and a few other basic services.

By this time, you should no longer be running systemd, but do not uninstall SysVinit (or whatever non-systemd init system you are using as an intermediate init) yet.

Reboot your machine, just to test out whether or not your new init system will bring up a somewhat usable OS. When you reboot, stop at your LILO or GRUB menu and choose to modify the boot command by appending this kernel arg:

init=/sbin/ninit

Your computer should uncompress the kernel as usual, and then attempt to launch the first process: /sbin/ninit. Assuming that works, your computer should continue to boot more or less as usual.

You have not permanently changed how your computer boots, you just used an override variable as part of your boot command. So if anything goes wrong, you can shutdown and reboot without doing anything; your computer will boot using /sbin/init as usual.


Ninit Scripts

Whether you succeeded or ran into problems, you might notice that your /etc/ninit directory is pretty sparse compared to a traditional SystemV or BSD init directory. That's because you have yet to write init scripts for your system so that the computer knows what processes to start for you upon boot.

If you are trying all of this on a Systemd computer, then you need to build the entire init directory from the ground up.

Luckily, the ninit author has posted his personal stash of (n)init scripts, available at https://gitlab.com/notklaatu/ninit/tree/master/examples (the examples directory of the ninit source code that you downloaded).

$ cd ~/path/to/ninit/examples/etc/ninit

The sample etc/ninit directory contains many of the most common init scripts that most computer users need for day-to-day computer use. Even so, you cannot just copy his scripts over to your system without at least looking at them to make sure that they make sense for your system (and unless you are running a qmail server, many will not).

The example ninit scripts do provide a good starting point. Specifically, they reveal the file structure of the ninit infrastructure.

directory: name of service to start, containing one or more of these files:

- environ: new-line delimited list of env variables to include when launching the service.
- out: a symlink to a sensible destination for data sent to stdout.
- params: new-line delimited list of arguments to include when
launching the service.
- run: a symlink to the executable binary that launches a
service. Less often, a shell script that is itself the service being
launched.
- respawn: an empty file, the presence of which dictates that the service should be respawned
upon failure.

Starting from the beginning: you need to be able to login. This is handled, traditionally, by an application called getty (you can read the man page for detail; it's an interesting read if you're not familiar with it). Of course, getty has been around for a while so naturally there are alternatives, many of which are in use by popular Linux distributions, so you should see what is available to you. Most distributions, even when using an alternative getty, link /sbin/getty to whatever they are using, but it doesn't hurt to take a look in /sbin just to see what you have available to you. .

TTY Login

Create a getty directory in your /etc/ninit directory, and place one directory within that for each login screen you want to have available. Traditionally, X is launched at tty7, so providing six getty screens would keep with this tradition:

  1. mkdir -p /etc/ninit/getty/{1,2,3,4,5,6}

Ninit's own pututmpid application handles the launching of getty, so all you need do is create a symlink run to pututmpid and touch a respawn file so that the tty's are supervised:

  1. for dir in getty/* ; ln \
 -s /sbin/pututmpid /etc/ninit/"${dir}"/run \
 && touch /etc/ninit/"${dir}"/respawn \
 ; done

Then create a params file for each. You can create a loop to automate this or do it manually; the parameters required are:

c1 /sbin/agetty tty1 </tt>

and so on for each tty (so 'c1' becomes 'c2' for the second tty, and 'tty1' becomes 'tty2', and so on).

Save this to the file params located in each directory.


Halt

Booting to a login prompt is a good start but at some point you want to turn your computer off. For that, you need a way to stop PID 1 from running, preferably gracefully.

In ninit, all you need to do to create a new service is create a directory in /etc/ninit. So first of all, create a directory called level0 (or similar) in /etc/ninit, and create a link to it called halt. It's not necessary to provide both an init runlevel 0 and a halt service, but most experienced unix users would expect to be able to call either runlevel 0 or halt, so providing a service called halt is user-friendly and more backwards-referential.

<code>

  1. mkdir /etc/ninit/{level0}
  2. ln -s /etc/ninit/level0 /etc/ninit/halt

In the level0 directory, create a link run that points to /sbin/ninit-runlevel:

  1. ln -s /sbin/ninit-runlevel /etc/ninit/level0/run

And then create a setup file called /etc/ninit/level0/setup.

Service management with ninit is performed with the nsvc command. Creating a shell script that stops each service that ninit launched ensures that each service is kindly shut down before turning off your computer.

Running the nsvc command with no arguments reveals some pertinent options (this is only a selection; run the command to see a full list):

  1. /bin/nsvc

-u up -d down -L list services

So the contents of /etc/ninit/level0/setup file can be as simple as listing all running services, and stopping each one:

  1. !/bin/sh

all=`/bin/nsvc -L` /bin/nsvc -C0 $all

Make it executable with chmod +x, and now create the final three files:

  1. echo "INIT_HALT=POWERDOWN" > /etc/ninit/level0/environ
  2. ln -s /sbin/ninit-runlevel /etc/ninit/level0/run
  3. echo "0" > /etc/ninit/level0/params
  4. echo "/etc/ninit/level0/halt" >> /etc/ninit/level0/params

The run script points to /sbin/ninit-runlevel, which more or less is the ninit equivalent to init's telinit. As arguments (found in the params file, of course), ninit-runlevel takes the level to switch to (in the case of halting, that's 0) and the file to execute as the system's final farewell.

In this example, I am calling the final script /etc/ninit/level0/halt but you can call it anything you please, as long as it makes logical sense to you and you remember to make /sbin/ninit-runlevel reference it by way of /etc/ninit/level0/params.

There are very good examples of how to kindly shutdown a system available from the Linux From Scratch documentation, Slackware's init sources, and the old SystemV scripts that shipped with Debian and Red Hat pre-Systemd. Use these examples to create your own halt script if you do not have one on your system already.

In fact, if you already have a usable shutdown script on your system, then you can just reference it. As long as the script is POSIXly correct and does not require external services that you have not launched in order to complete, then your computer will successfully halt. There's certainly no shame in re-using scripts from your previous init system where possible.

If you are stuck creating your own, keep it simple at first. Use an example script and trim it down to the services you know apply to your system. Here is a trimmed version of the default rc.0 by Miquel van Smoorenburg and Patrick Volkerding:

  1. !/bin/sh

PATH=/sbin:/etc:/bin:/usr/bin

  1. Save the system time to the hardware clock

/sbin/hwclock $CLOCK_OPT --localtime --systohc

  1. turn process accounting off

/sbin/accton off

  1. Carry a random seed between reboots.

echo "Saving random seed from /dev/urandom in /etc/random-seed."

  1. Use the pool size from /proc, or 4096 bits:

if [ -r /proc/sys/kernel/random/poolsize ]; then

 /bin/dd if=/dev/urandom of=/etc/random-seed count=1 bs=$(expr $(cat /proc/sys/kernel/random/poolsize) / 8) 2> /dev/null

else

 /bin/dd if=/dev/urandom of=/etc/random-seed count=1 bs=512 2> /dev/null

fi /bin/chmod 600 /etc/random-seed

  1. Before unmounting file systems write a reboot or halt record to wtmp.

$command -w /bin/rm -f /var/lock/subsys/* /sbin/swapoff -a /bin/sync /bin/umount -v -a -t no,proc,sysfs /bin/mount -v -n -o remount,ro / /bin/sync /bin/sleep 3

  1. Ensure all procs have completed on SMP machines:

wait

  1. and halt:

/sbin/poweroff


The Big Reboot

Now, regardless of your previous init system, you can reboot your computer and start it using the init=/sbin/ninit kernel argument to see how you've done. If all goes well, you should find yourself able to boot, to login, and to do any number of activities that do not require special daemons to be running in the background. And in the end, you should be able to halt your system.

Assuming all goes well, you've successfully set up a new init system for yourself.

Make ninit the Default

Make ninit your default init system by adding the kernel arguments to your Lilo or GRUB configuration. See your distribution documentation to learn how to do that.

From that point, create new ninit scripts to handle important services that you use (such as an NTP daemon, or SSH daemon, or HTTP, and so on). Use "prior art" as the basis for your work (Linux From Scratch and Slackware are excellent projects to "steal" from) and remember to use respawn for those important ones that you want to keep alive no matter what. After all, you're using a fancy new init system, so you may as well take advantage of its benefits!


Dependencies and More

Ninit has many more features, such as dependency-based loading of services, so explore it. And since there are so many more init systems out there, and you now see how to modify your init system, you can now freely explore other alternatives.

Happy hacking!

Personal tools