# HOWTO: A modern init system in 3 minutes

## Ant P.

Today we're going to install runit. Some good reasons to switch:

```
# ls -l /sbin/*init

-rwxr-xr-x 1 root root   35308 Mar  8 21:43 /sbin/init

-rwxr-xr-x 1 root root   13952 Aug 23 20:19 /sbin/runit-init

# du -sh /usr/share/doc/runit*

200K   /usr/share/doc/runit-2.1.1-r1/
```

Install it:

```
# emerge -av runit

# $EDITOR /etc/runit/1
```

Make sure to boot into a useful runlevel:

```
...

RUNLEVEL=S /sbin/rc sysinit

RUNLEVEL=S /sbin/rc boot

/sbin/rc default # <== add this line

...
```

The scripts inside /etc/runit/runsvdir are configured by default to run agetty on tty1-6, like sysvinit's inittab does. Be careful about replacing these; I found mingetty to not work.

Edit your bootloader stuff to append "init=/sbin/runit-init" to the command line.

Then reboot:

```
# reboot
```

----------

## wuzzerd

Very interesting, Ant.  It seems to run out of the box.  One snag with the parallel starting of services: ntp-client fails 

with server not found messages.  I moved it to /etc.local.d with a bit of sleep.

<troll> It is much easier to set up and debug than systemd </troll>

----------

## Ant P.

A few other things I've noticed:  There is no shutdown or halt command, and trying to run the sysvinit ones will only complain that /dev/initctl doesn't exist. Shutdown/reboot is done by using runit-init(8).

 Many tools assume those sysvinit commands exist (I wonder if they're equally broken under systemd), and will need reconfiguring (or replacing with less broken ones if they don't allow that).

----------

## Dr.Willy

Hi Ant P.

Brilliant find. I'm still having a look at it, but doesn't this…

 *Ant P. wrote:*   

> 
> 
> ```
> ...
> 
> ...

 

…kindof defeat the purpose of installing it in the first place?

I mean in this setup you're just using it as an openrc wrapper.

Edit:

Ok, so …

```
runit-+-acpid

      |-dwmstatus

      |-gpg-agent

      |-runsvdir-+-4*[runsv---agetty]

      |          |-runsv---xlshd---Xorg---2*[{Xorg}]

      |          |-runsv---dhcpcd

      |          |-runsv---mpd---4*[{mpd}]

      |          |-runsv---vsftpd

      |          `-runsv---syslog-ng

      |-systemd-udevd

      |-tmux-+-zsh-+-pstree

      |      |     `-xclip

      |      |-zsh---htop

      |      `-zsh

      |-wpa_supplicant

      `-xlsh---dwm-+-firefox---35*[{firefox}]

                   `-xterm---su---zsh---tmux

```

I just converted my entire "default" runlevel to runit.

I wonder if and when I will run into problems, because right now I'm liking it: Setup was easy and the cli is pretty nice too.

----------

## Ant P.

Yes, you have a point, it seems pointless at first glance.

The major reason why you'd want runit over sysv is its own process supervision tools, which are good for custom services. I use it for a few game servers and some out-of-tree/self-written programs (things that need to stay running even if they segfault periodically). Most users will only ever care about the stuff given to them in /etc/init.d, and that's fine too. There's nothing wrong with keeping OpenRC (or even anything else that originally ran atop sysvinit...)

One other reason is most of runit's tools exist in busybox, so there's the possibility of using this for doing a tiny install.

But more importantly, without going off on a long tangent about UNIX philosophy, the simple ability to do things like this without having to deal with a flag day demonstrates the system is working correctly.

----------

## Dr.Willy

Ok, so I searched a bit deeper and found out that runit implements a concept by djb (whom you might know) called daemontools.

Now there are two things I was asking myself:

1) There are other programs implementing that concept, most notably (in my opinion) s6.

I find it really interesting and think it might be worthwhile to migrate to it entirely, replacing openrc. On the other hand I've only had a quick look and don't know a lot about init systems in the first place.

So I'd like to hear what other people think of it (s6 and the daemontools concept in general)

2) As a follow up to 1) Do you think it would be viable/worthwhile to implement the services required to bring up a gentoo system?

----------

## Ant P.

I've been looking at s6 too, I don't think I'm ready to go *that* far yet (execline's syntax feels like a cross between sh and Haskell). I haven't had much luck installing it either.

Regarding the second thing, I'm too paranoid to touch anything in OpenRC's boot runlevel (that's probably why runit's configured to do it by default). Apart from that I've been making good progress. On my netbook I've ported over all the simple daemons in the default runlevel (basically anything that fits on one screen), and now it looks like this:

```
runit─┬─cron

      ├─metalog───metalog

      ├─nginx───nginx

      ├─ntpd

      ├─runsvdir─┬─3*[runsv───agetty]

      │          ├─runsv───bluetoothd

      │          ├─runsv───acpid

      │          ├─runsv───avahi-daemon───avahi-daemon

      │          ├─runsv───dbus-daemon

      │          ├─runsv─┬─connmand

      │          │       └─svlogd

      │          └─runsv─┬─svlogd

      │                  └─wpa_supplicant

      ├─slim───X

      ├─sshd───sshd───sshd─┬─bash───pstree

      │                    └─4*[{sshd}]

      └─udevd

```

With rc_parallel=YES, I get to an X login prompt in 10.5 seconds. It could probably be quicker, I have net.lo eating half a second of CPU just to "provide net" to other init.d scripts. OpenRC needs some equivalent of package.provided...

After using runit for a few days I've encountered several other problems, which I'm not clear on how to fix: SysV seems to do some internal magic with VCs that runit doesn't. The getty-tty* services end up spawning bash processes with no job control, which means Ctrl+C doesn't work in them, among other things. I suspect this is the same root cause that's making mingetty misbehave. (I would've tried kmscon, but it fails to build.)

The shutdown (/etc/runit/3) script supplied by Gentoo has syntax errors (sv shutdown -w instead of sv -w shutdown). Correcting those errors causes it to wait 5 minutes on any logged in virtual consoles to exit.

----------

## Ant P.

I wrote this to make sysv's poweroff/reboot programs work. You can just run it directly.

```
#!/usr/bin/perl -T

use v5.18;

use warnings;

use autodie;

$ENV{PATH} = '/sbin:/bin';

my $INIT_FIFO    = '/dev/initctl';

my $REQ_SIZE     = 384;

my $MAGIC_HEADER = 0x03091969;

my $CMD_RUNLEVEL = 0x1;

-e $INIT_FIFO or system("/usr/bin/mkfifo --mode=0600 $INIT_FIFO");

open(my $pipefh, q{<}, $INIT_FIFO);

while () {

    read($pipefh, my $request, $REQ_SIZE) == $REQ_SIZE

        or die "Failed to read $REQ_SIZE bytes";

    my ($magic, $command, $runlevel, $sleeptime, $stuff) = unpack('l l A4 l A*', $request);

    $magic    == $MAGIC_HEADER  or die "Got invalid magic bytes";

    $command  == $CMD_RUNLEVEL  or next;

    $runlevel =~ /^(0|6)$/      and system("/sbin/runit-init $1");

}
```

(Side note: sysv's source code is... "interesting". No wonder everyone just starts from scratch instead of fixing it...)

----------

## Ant P.

I've solved the getty/console weird behaviour - it needs to run in its own process group, i.e.:

```
#!/bin/sh

exec /usr/bin/chpst -P /sbin/agetty 38400 tty1 linux

```

(this also fixes mingetty)

----------

## Ant P.

Follow-up to previous post: it turns out the runit sources already contain a /etc/runit/2 file which uses "runsvdir -P". However Gentoo installs its own incorrect version of that, causing the breakage.

Using that file would be more correct than the fix above, and is what I've fixed the runit ebuild to do in my local overlay.

I have had no response to any of the runit-related bugs I've filed on b.g.o in the last month, FWIW.

----------

## steveL

 *Ant P. wrote:*   

> Follow-up to previous post: it turns out the runit sources already contain a /etc/runit/2 file which uses "runsvdir -P". However Gentoo installs its own incorrect version of that, causing the breakage.
> 
> Using that file would be more correct than the fix above, and is what I've fixed the runit ebuild to do in my local overlay.

 

That makes sense; have you posted the patch to the ebuild to b.g.o? Link (or patch) please, so it's all in one place.

----------

## Ant P.

Thanks for showing interest.

My ebuild's diverged too much at this point to get a clean diff, but the change is simple enough — fix the path in "${S}"/../etc/2 (/service -> /var/service), install that file instead of "${FILESDIR}"/2, and then the latter can be removed from the tree.

For reference, I'll start keeping track of all the runit bugs I've filed here:

521918 (shutdown is broken) 522204 (login shells are broken - patch obsoleted by above fix) 522786 (2.1.2 version bump)

----------

## steveL

522204 (login shells are broken - patch obsoleted by above fix)

You should point that out on the bug then, by obsoleting that patch in favour of a minimal patch to the ebuild, as you describe, with comment about no longer needing one of the files etc.

The shutdown (sv -w) one looks like it's installing its own file too; is there good reason for that, or would upstream's be better?

----------

## Shamus397

Just wanted to chime in and say brilliant work Ant P., I'll be watching this thread with interest.  :Smile: 

----------

## Ant P.

 *steveL wrote:*   

> 522204 (login shells are broken - patch obsoleted by above fix)
> 
> You should point that out on the bug then, by obsoleting that patch in favour of a minimal patch to the ebuild, as you describe, with comment about no longer needing one of the files etc.

 

I'll get around to it when I see any indication that the maintainer is even trying. Other people on the forums seem to have an extremely low opinion of him, so I decided it's better to direct my efforts toward a fixed ebuild people can use *now*.

 *Quote:*   

> The shutdown (sv -w) one looks like it's installing its own file too; is there good reason for that, or would upstream's be better?

 

The 1 & 3 files are Gentoo-specific, or more accurately, OpenRC-specific. We can't get rid of those, upstream has versions for a few different distros but not ours.

----------

## depontius

 *Ant P. wrote:*   

>  *steveL wrote:*   522204 (login shells are broken - patch obsoleted by above fix)
> 
> You should point that out on the bug then, by obsoleting that patch in favour of a minimal patch to the ebuild, as you describe, with comment about no longer needing one of the files etc. 
> 
> I'll get around to it when I see any indication that the maintainer is even trying. Other people on the forums seem to have an extremely low opinion of him, so I decided it's better to direct my efforts toward a fixed ebuild people can use *now*.
> ...

 

There has been any amount of discussion about forking Gentoo.  But an incremental point would be forking OpenRC, but that would also mean someone signing up to maintain the fork, and presumably an overlay to hold it.  At the moment I don't have the time to do that, so I'm not going to presume to sign someone else up, either.  I suspect I also don't currently have the proper skills refined, but that's a correctable situation.

----------

## steveL

 *Ant P. wrote:*   

> I'll get around to it when I see any indication that the maintainer is even trying. Other people on the forums seem to have an extremely low opinion of him, so I decided it's better to direct my efforts toward a fixed ebuild people can use *now*.

 

Hmm; can't say I agree with this approach. A distro is bigger than any one person, and where are those people supposed to find the ebuild, when they search bugzilla for this problem?

Just ignore the person, and speak to your fellow users, including over bugzie: Gentoo is first and foremost about its users, whatever anyone might try to claim. So is every other distro out there.

 *Quote:*   

> The 1 & 3 files are Gentoo-specific, or more accurately, OpenRC-specific. We can't get rid of those, upstream has versions for a few different distros but not ours.

 

Fair enough; we can do initscripts. I don't like the names though. Too easy for a typoed script to overwrite.

----------

## steveL

 *depontius wrote:*   

> There has been any amount of discussion about forking Gentoo.  But an incremental point would be forking OpenRC, but that would also mean someone signing up to maintain the fork, and presumably an overlay to hold it.  At the moment I don't have the time to do that, so I'm not going to presume to sign someone else up, either.  I suspect I also don't currently have the proper skills refined, but that's a correctable situation.

 

Please don't sign up in the hope that you can "refine" your skillset on the job. We've already had one incompetent mangle the project.

----------

## depontius

 *steveL wrote:*   

>  *depontius wrote:*   There has been any amount of discussion about forking Gentoo.  But an incremental point would be forking OpenRC, but that would also mean someone signing up to maintain the fork, and presumably an overlay to hold it.  At the moment I don't have the time to do that, so I'm not going to presume to sign someone else up, either.  I suspect I also don't currently have the proper skills refined, but that's a correctable situation. 
> 
> Please don't sign up in the hope that you can "refine" your skillset on the job. We've already had one incompetent mangle the project.

 

I know better than to even try such a thing.  That's right up there with delivering something on the Friday before you leave for a 2-week vacation.  If I were so inclined I'd be refining my applicable skillset before even thinking of taking on the job.

I don't mean to sign you up, but you've been the one talking about forking Gentoo.  Gentoo doesn't need forking, OpenRC does.  Given the situation where I work, I know benign and/or non-benign neglect when I see it.  The lead point of weakness in the systemd advance on Gentoo is OpenRC, which you appear to have said is in a state of under or negative development.  Perhaps you're a natural focal point on a fork of OpenRC, at the very least a more modest project than forking all of Gentoo.  Incidentally, I'm running "ebuild install" against openrc-0.13.1 now, just to see what is presumably in the future.  The Changelog wasn't very informative, something about a regression and something about "removal".  It sits beside the sysvinit that I've done the same with, and occasionally peruse the source for.

EDIT - I'll test and give feedback.

----------

## steveL

 *depontius wrote:*   

> I know better than to even try such a thing.  That's right up there with delivering something on the Friday before you leave for a 2-week vacation.  If I were so inclined I'd be refining my applicable skillset before even thinking of taking on the job.

 

Phew; glad you're as sane as I thought.

 *Quote:*   

> I don't mean to sign you up, but you've been the one talking about forking Gentoo.

 

Oh blimey, I have always argued against any such attempt, and have consistently said there's no point forking Gentoo ("try an overlay"). As the whole point of Gentoo is that everything is from source, and I've never met anyone who wanted to do more than patch a small corner. Claims to want to do much more, sure, but never the reality, imo.

Just to get that straight; yes I mentioned the idea, but only to dismiss it immediately. I most definitely don't want to fork Gentoo.

 *Quote:*   

> Gentoo doesn't need forking, OpenRC does.  Given the situation where I work, I know benign and/or non-benign neglect when I see it.  The lead point of weakness in the systemd advance on Gentoo is OpenRC, which you appear to have said is in a state of under or negative development.  Perhaps you're a natural focal point on a fork of OpenRC, at the very least a more modest project than forking all of Gentoo.  Incidentally, I'm running "ebuild install" against openrc-0.13.1 now, just to see what is presumably in the future.  The Changelog wasn't very informative, something about a regression and something about "removal".  It sits beside the sysvinit that I've done the same with, and occasionally peruse the source for.
> 
> EDIT - I'll test and give feedback.

 

Hmm well I'd be up for going back and filtering out the crappy shell that's infected the project since Roy left; I find it quite offensive.

blueness's work on eudev (filtering commits from udev for sanity) showed the way. So that'd be going back and reviewing each commit, rewriting it to do whatever the patch was supposed to do, without losing robust POSIX sh. So tedious, but not intellectually hard, just time-consuming.

If you really want to fork properly, I'd have to get help on the C side from my boss, most likely. We've discussed it before now, in the context of how little progress had happened with the actual code (the only significant progress made has been by users, who've not been encouraged from what I've seen.) While he'd be into helping, I'm sure, he also wants to sort out the mactab idea in-kernel, and he's not a kernel coder, bless ;) so we'd need a couple of more people at least, I think.

And someone like you would have to drive the project; the lead is often more of a QA admin than deep-tech, ime, since herding cats is a different skillset to holding a program in your head. It's very rare for the two sets of abilities to combine (zmedico is the only one I've met personally, apart from people in previous generations), but it's important that both sides know their limitations, or you end up with bitchy fallouts and no progress.

I know for sure I'd be a terrible lead; I can help on the technical side, but I simply don't have the demeanour, nor the tact, required for that.

I won't be able to even look at it for at least three weeks, realistically; my free-code time is going on update atm, and then the installer, which hopefully won't require much to do what it did before. That'll be useful for testing kde-lean, which is also waiting on me to finish update --toolchain. Surgery was necessitated by issues brought up by perl, ie a toolchain package in /etc/warning, which could be, and was ofc, blocked. From a user pov it's still at the same state (in git) wrt perl; in the background lots of changes have happened, which will be used by the next part I'm working on now.

Let me know how you get on with the newer version of openrc, and how you feel after you've poked through the codebase. (Reviewing the history with gitview is very revealing.)

----------

## depontius

 *steveL wrote:*   

> 
> 
> Let me know how you get on with the newer version of openrc, and how you feel after you've poked through the codebase. (Reviewing the history with gitview is very revealing.)

 

I haven't actually installed the new OpenRC - I did an "ebuild install", so it's sitting out in /var/tmp/portage, along with sysvinit and the stable openrc.  It's a good way to peruse the code - the source is there as well as an installation image.  You can see both gozintas and gozoutas for the build process.

Thanks for the pointer to gitview - I see now that I have it installed on one system.  I've been trying to learn more about Android so I can help out with my languishing phone, and don't know much about git.  Sometimes the problem with learning curves is getting a good handhold on the bottom of the ladder.

----------

## steveL

 *depontius wrote:*   

> Thanks for the pointer to gitview - I see now that I have it installed on one system.

 

Yeah it comes with git on Gentoo systems; to get the git-gui and gitk you need the tk USE-flag on the git package.

I just found that out the other day; until then I'd been wondering what had happened to gitk (and why i didn't have the git-gui command) for a few years lul. I just kept using gitview, and now I'm settled in with it as an overview on history. Oh, gitview --all is likely better for reviewing a repo.

 *Quote:*   

> I've been trying to learn more about Android so I can help out with my languishing phone, and don't know much about git.  Sometimes the problem with learning curves is getting a good handhold on the bottom of the ladder.

 

Yeah; git is quite complex as well. Try this url; I wish I'd seen it when I was starting out.

The main thing to remember is that the "index" is better thought of as the "stage"; it's what you've staged to make a commit (and you can add to it, or back things out with reset -p, all without committing). The other important thing is that a commit is only ever local; nothing gets sent until you push, typically several commits together. So you can always correct yourself, up until the time you git push.

I'd advise you not to use git-rebase, ever, except for a branch that you're rebasing onto master/upstream (don't worry about it til you need it.) The best command I've found is git add -p somefile, which allows you to select which bits of the file will go into the commit. (ie the staged index.) That covers the main usage people have for rebase -i, at least in pragmatic terms: being able to break up a commit into logical parts, and then push several commits all at once, each one being distinct, but all of them being required for the thing to be considered working. (which is where the distinction between commit and push is vital.)

But you keep the forward sense of progress. That's what always trips me up with rebase (the feeling of going backwards), and rebase can bork your repo, so I just avoid it now.

Check this post out too; it's my config and aliases for git, which should give you an idea of how i use it day-to-day; not very expertly compared to many but I've come to vcs very late in my life, and I try to keep the workflow simple, so I have enough headspace for coding. (Hence the insistence on fast-forward merges, unless I explicitly ask for a fork.)

Whenever I'm stuck or a bit unsure despite the manpages, I just /join #git -- they're brilliant :-)

Although they do have a tendency to make everything about the git model, and explaining all the concepts ;)

I really do recommend the kdiff3 setup (or some other difftool) as per the linked post(vdiff somefile), as well. It's so nice being able to view the patches in the context of the whole file, from time to time; again an alternative view that helps keep perspective on the overall process.

I use it when I'm coming back to work, to get my head back into the code, and quite often before I commit; it makes for much better self-review, ime, simply because you have the whole file to look at, and can move through it very quickly to where the changes are, but still consider other options based on everything else it fits into.

gdiff/git diff (the standard util ofc) is more useful pre-commit when I'm already in the flow, since I'm just reviewing the detail; but having the option to step back with kdiff3 really helps, ime.

Oh and don't forget git diff --staged (staged/vstaged aliases) to review what you're about to commit. You can always commit --amend it after as well, so don't be shy; it's not til you git push that anything leaves your machine, and you can always reset HEAD^ to go back a step (add --soft to keep your staged changes in the index).

Like I said though, the tias link is what you need, since it gets you playing locally, and takes the fear out of the process; at this point I'd advise you to play with branches and checkouts as well, in a throwaway repo (vbranch/git branch -lvv to see what's going on.) In this instance a push simply goes to the other local repo, so again you can't harm anything, since it's just another throwaway directory.

I'm sure you've been swamped with other links already; if not feel free to /msg friendlyToaster git on freenode.

And /join #git if you want advice; we're in #friendly-coders btw (i'm 'igli'), in case it all gets too much ;-)

----------

## bnolsen

Just FYI "socklog" is designed with runit in mind.  It might make sense to see how that runs compared to metalog.

Here's a page for socklog:

http://smarden.org/socklog/

and the configuration:

http://smarden.org/socklog/configuration.html

----------

## Ant P.

Okay, one of my above posts turned out to be incorrect. runsvdir -P doesn't exactly do the same thing as chpst -P, and I ended up with a few wedged boxes today that I couldn't log into after a reboot, because mingetty wouldn't start at all. Whoops.

(I guess I don't have to update that bug after all   :Laughing:  )

----------

## steveL

 *Ant P. wrote:*   

> Whoops.
> 
> (I guess I don't have to update that bug after all  :lol: )

 

lul :)

----------

## tclover

 *Ant P. wrote:*   

> I wrote this to make sysv's poweroff/reboot programs work. You can just run it directly.
> 
> ```
> #!/usr/bin/perl -T
> 
> ...

 

Just quoting this script for the absence of any COPYRIGHT NOTICE NOR AUTHORSHIP before angry John add *any* later as proof of his nonsense.

----------

## EmaRsk

From wikipedia:

"In all countries where the Berne Convention standards apply, copyright is automatic […]."

"Once an idea has been reduced to tangible form, for example by securing it in a fixed medium (such as a drawing, sheet music, photograph, a videotape, or a computer file), the copyright holder is entitled to enforce his or her exclusive rights."

"In 1989 the United States enacted the Berne Convention Implementation Act, amending the 1976 Copyright Act to conform to most of the provisions of the Berne Convention. As a result, the use of copyright notices has become optional to claim copyright, because the Berne Convention makes copyright automatic."

In free software, licensing is used to ALLOW the distribution of the software, not to prevent it. The fact that we usually don't care to enforce the copyright for our shared scripts doesn't mean that we don't have the right to do that.

----------

## szatox

This code clearly was ment to be published and used, so where's the problem?

----------

## Ant P.

 *szatox wrote:*   

> This code clearly was ment to be published and used, so where's the problem?

 

The problem is when someone comes along and slaps their own license restrictions on it, passing it off as their own work with a tiny ex post facto footnote buried somewhere giving the original author vague credit for something unspecified.

This childish reaction to being caught doing precisely that further confirms that there was no good intention behind it.

----------

## schorsch_76

My experiments with runit: 

I completly replaced openrc with runit. It boots insanely fast. The longest thing is decompressing the initrd, followed by the udev trigger. In nearly no time i am on X and i can work.

Got the getties fixed. Mount and my network setup are via services. Slim is scheduled to start when home is mounted and the udev-trigger is done.

There is one issue i am not yet understand ....

One time initializations like net.eth0 which run ifconfig/route dont need a process to supervise. 

```
cat net.eth0/run 

#!/bin/sh

ifconfig eth0 192.168.178.19/24 || exit 1

route add default gw 192.168.178.1 || exit 1

exec pause

```

The "pause" at the exec is a script which runs an endles loop sleep 1. 

```
cat /usr/local/bin/pause 

#!/bin/sh

while true ; do

   sleep 1

done

```

When i dont have this process, it tells me the service is not up. I know of the check script at the service directory. If it exits 0 it tells runit that the service is ok but sv /service/* tells me it is not up without that "pause". Any hints welcome ...

----------

## schorsch_76

To answer my own question: Yes it is needed. See voidlinux

https://github.com/voidlinux/void-runit

See the core-services and pause.c

----------

## Ant P.

Here's a trick you can use to avoid burning exec() calls on `sleep` processes:

```
chpst -L service.lock chpst -l service.lock true
```

----------

## mi_unixbird

Just posting here to make it known that I've at least currently transitioned from sysvinit/OpenRC to runit/OpenRC. Boot times seems to have improved by a fraction, at least the stage where runit calls openrc seems to be have been noticeably shorter.

My setup involves "/etc/runit/1" calling rc sysinit and rc boot. the  former "default" runlevel has been completely ported to runit as for now for me.

Some caveats I encountered (the package is horribly, horribly broken):

- a tty would not show up initially. The ./run scripts in /etc/service/getty{1..6}/run contained /usr/sbin/chpst. Turns out the executable is simply in /bin/chpst. Changing it to either "chpst" or "/bin/chpst" does it for me.

- Same issue as noted above with the tty's than be fixed by using the -P flag on runsvdir within /etc/runit/2

- the ebuild puts SVDIR=/etc/service in /etc/env.d, this is good because putting services in /service is inane. But bad and solves very little because sv expects a /service directory to exists. I symlinked it to /etc/service for now to fix a couple of annoying bugs with sv.

If anyone knows how to port rc sysinit and rc default to runit and do away with openrc completely. I'd love to now.

----------

## mi_unixbird

So an update on a while later of using runit and runsvdir.

I don't want to do away with OpenRC any more for system boot. OpenRC is better at bringing the system online than a simple random shell script which is what you're going to get with Runit, dependencies and parallel boot do matter. So I'm keeping sysinit and boot from OpenRC.

Couple of things which are in my opinion insane defaults on runit but which can be fixed.

/service, /etc/service, /var/service, they're all horrible choices for that directory that contains the "current" runlevel. On my system it's /run/svon, it needs to be in /run in my opinion. That's what /run is for. /etc is insane because a process really should not write to /etc under normal operation to store its state and hat's what it does. /var is okay I guess but still not as appropriate as /run

in general, runit loves to write to /etc. I I am philosophically opposed to this so I fixed this by creating symlinks, /etc/runit/{stopit,runit} are linked to /run/runit/{stopit,runit}, it can write in /run all day long. Also, the ./supervise directories in /etc/sv/$service are linked to /run/sv/supervise/$service here because again, stop writing in /etc you bastard

I created the sv group and made the permissions of the supervise files as such that members of the sv group can ask for the status of services without having to use root. I think it's silly you need to be root to ask for a status.

since this is reset at boot because /run is on a tmpfs. I actually created a service that actually monitors these permissions and if they are off it resets it back to the right ones, yeh...

I think storing runlevels in /etc/runit/runsvdir is dumb, runlevels are not part of runit, as far as I'm concerned the runit init system and the "sv" set of process supervision tools are unrelated but some-how packaged together, they do not need each other. So runlevels are stored in /etc/svlevels now with /run/svon always being a symlink to the current level.

"sv" was too limited, so I made a wrapper around it which can for instance do "svctl status \*" to get the status of all services in /run/svon, it basically performs globbing and does a bunch of other nice things like automatically managing symlinks, changing runlevels, listing available services etc.

Maybe this code will give you people some ideas:

```
#!/bin/bash

# system one time tasks

set -u

. /etc/runit/functions

PATH=/sbin:/usr/sbin:/bin:/usr/bin:/lib/rc/bin

einfo 'Initializing system with OpenRC'

eindent

   einfo 'sysinit level'

   openrc sysinit || {

      eerror 'OpenRC failed the sysinit level' ;

      sulogin ;

      exit 100 ;

      }

      

   slowdown

   

   einfo 'boot level'

   openrc boot || {

      eerror 'OpenRC failed the boot level' ;

      sulogin ;

      exit 100 ;

      }

eoutdent

eend 0

slowdown

ebegin 'Creating stopit and reboot files'

mkdir -p /run/runit &&

touch /run/runit/stopit &&

chmod 0 /run/runit/stopit &&

touch /run/runit/reboot &&

chmod 0 /run/runit/reboot

eend $? || { sulogin ; exit 100 ; }

slowdown
```

```
#!/bin/bash

set -u

. /etc/runit/functions

export PATH=/command:/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:/lib/rc/bin

export SVDIR=/run/svon

svlevels=/etc/svlevels

runlevel=$svlevels/default

supervisedir=/run/sv/supervise

if get_cmdline single; then

   einfo "Dropping to single user mode"

   sulogin

   exit 0

fi

# check for any other runlevel

if get_cmdline softlevel; then

   level=$cmdline_value

   ebegin "Using runlevel: $level"

   levelpath=$svlevels/$level

   if [[ -d "$levelpath" ]] ; then

      runlevel=$levelpath

      ewend 0

   else

      ewend "$levelpath does not exist"

      einfo "Using $runlevel"

   fi

fi

slowdown

ebegin "Linking $runlevel to $SVDIR"

if [[ -L "$SVDIR" ]] ; then

   eindent

      ewarn "$SVDIR already exists and is a symlink"

      ebegin "Unlinking $SVDIR"

      unlink "$SVDIR"

      eend $?

   outdent

fi

ln -s "$runlevel" "$SVDIR"

eend $? || { sulogin ; exit 0 ; }

slowdown 2

ebegin "Creating $supervisedir"

mkdir -p "$supervisedir"

eend $? || { sulogin ; exit 0 ; }

slowdown 2

openrc runsvdir

einfo "Starting runsvdir with: $SVDIR"

exec runsvdir -P "$SVDIR" &> /dev/null

eerror "Exec some-how failed horribly"

sulogin

exit 0

```

```
#!/bin/bash

set -u

. /etc/runit/functions

exec >/dev/console 2>&1

export PATH=/sbin:/usr/sbin:/bin:/usr/bin:/lib/rc/bin

export SVDIR=${SVDIR-/run/svon}

if [[ -d "$SVDIR" && "$(ls "$SVDIR")" ]]; then

   ebegin "Waiting for services to stop"

   sv force-stop "$SVDIR"/*

   sv exit "$SVDIR"/*

   eend $?

else

   einfo "No services to stop"

fi

slowdown 2

ebegin "Obtain all users in users group"

IFS=, users=( $(awk -F: '/^users/ {print $4;}' /etc/group ) )

eend $?

einfo "Sending a TERM signal to all processes by users in users group"

for user in "${users[@]}"; do 

   pkill -TERM -u "$user"

done

slowdown 2

patience=5

eindent

   ebegin "Waiting $patience seconds for processes to die"

   now=$(date +%s)

   timeout=$((now+$patience))

   while true ; do

      remaining_count=0

      for user in "${users[@]}" ; do

         usercount=$(ps -u "$user" -o pid= | wc -w)

         ((remaining_count+=usercount))

      done

      if [[ $remaining_count = 0 || $(date +%s) -gt timeout ]]; then

         break

      fi

   done

   if [[ $remaining_count -gt 0 ]]; then

      ewend 1 "$remaining_count processes remaining"

      einfo "Sending KILL signal to remaining processes"

      for user in "${users[@]}"; do

         pkill -KILL -u "$user"

      done

   else

      eend 0

   fi

eoutdent

slowdown 2

if [[ -x /etc/runit/reboot ]] ; then

   ebegin "Rebooting system with OpenRC"

   eindent

      openrc reboot

      exit=$?

   eoutdent

   eend $exit

else

   ebegin "Shutting down system with OpenRC"

   eindent

      openrc shutdown

      exit=$?

   eoutdent

   eend $exit

fi

slowdown

```

```
#!/bin/bash

get_cmdline () {

   cmdline_value=''

   [[ -r /proc/cmdline ]] || return 1

   local name=$1

   local arg

   local preflen=$((1+${#name}))

   local retval=1 #this is done this way to ensure that later cmdline flags overwrite earlier ones

   for arg in $(cat /proc/cmdline); do

      case "$arg" in

         "$name="?*)

            cmdline_value="${arg:$preflen}"

            retval=0

            ;;

         "$name")

            retval=0

            ;;

      esac

   done

   return $retval

}

slowdown () {

   local default=${1-2}

   if get_cmdline slow ; then

      sleep "${cmdline_value-$default}"

   fi

}

```

```
#!/bin/bash

set -u

opts=()

cmds=()

servs=()

svdir=${SVDIR-/run/svon}

rldir=${SVRLDIR-/etc/svlevels}

dedir=/etc/sv

if [[ ! -e $svdir ]]; then

   echo "SVDIR $svdir does not exist" 1>&2

   exit 2

fi

   

svdir=$(realpath -s -- "$svdir")

argv=( "$0" "$@" )

i=1

while [[ $i -lt ${#argv[@]} ]]; do

   arg=${argv[i]}

   case "$arg" in

      -w)  opts=( -w "${argv[$((i+1))]}" ); ((i++));;

      -?*) opts+=("$arg");;

      *)  cmds=("$arg"); args=( "${argv[@]:$((i+1))}" ); break;;

   esac

   ((i++))

done

enabledservs=()

definedservs=()

runlevels=()

for arg in "${args[@]:+${args[@]}}"; do

   if [[ $arg != */* ]]; then

      IFS=$'\n'

      globs=( $(find "$svdir/" -maxdepth 1 ! -path "$svdir/" -name "$arg" -printf '%f\n') )

      if [[ "${#globs[@]}" -gt 0 ]]; then

         for glob in "${globs[@]}"; do

            enabledservs+=( "$svdir/$glob" )

         done

      else

         enabledservs+=( "$svdir/$arg" )

      fi

      

      globs=( $(find "$dedir/" -maxdepth 1 ! -path "$dedir/" -name "$arg" -printf '%f\n') )

      if [[ "${#globs[@]}" -gt 0 ]]; then

         for glob in "${globs[@]}"; do

            definedservs+=( "$dedir/$glob" )

         done

      else

         definedservs+=( "$dedir/$arg" )

      fi

      

      globs=( $(find "$rldir/" -maxdepth 1 ! -path "$rldir/" -name "$arg" -printf '%f\n') )

      if [[ "${#globs[@]}" -gt 0 ]]; then

         for glob in "${globs[@]}"; do

            runlevels+=( "$rldir/$glob" )

         done

      else

         runlevels+=( "$rldir/$arg" )

      fi

      

   else

      enabledservs+=( "$arg" )

      definedservs+=( "$arg" )

      runlevels+=( "$arg" )

   fi

done

case "${cmds[@]-}" in

   enable)

      for i in "${!definedservs[@]}"; do

         ln -s -- "${definedservs[i]}" "$SVDIR"

      done

      ;;

   disable)

      for i in "${!enabledservs[@]}"; do

         unlink "${enabledservs[i]}"

      done

      ;;

   list)

      ls "${args[@]:+${args[@]}}" -- "$dedir/"

      ;;

   list-enabled)

      ls "${args[@]:+${args[@]}}" -- "$svdir/"

      ;;

   switch)

      ln -sfT -- "${runlevels[0]}"  "$SVDIR"

      ;;

   *)

      sv "${opts[@]:+${opts[@]}}" "${cmds[@]:+${cmds[@]}}" "${enabledservs[@]:+${enabledservs[@]}}"

      ;;

esac
```

----------

## Ant P.

Didn't expect this topic to pick up again...

 *mi_unixbird wrote:*   

> Some caveats I encountered (the package is horribly, horribly broken):

 

No surprise there, WilliamH is the maintainer. Seems to be real popular among users of other init packages for the same reason.

 *Quote:*   

> - a tty would not show up initially. The ./run scripts in /etc/service/getty{1..6}/run contained /usr/sbin/chpst. Turns out the executable is simply in /bin/chpst. Changing it to either "chpst" or "/bin/chpst" does it for me.

 

That one might actually be *my* fault. As in, I submitted a bunch of bugs against the in-tree ebuild to make it less insane a long time ago and the changes therein were only partially applied (and nobody responsible actually bothered to read them, see above).

/bin/chpst is the correct location for that binary because it doesn't need root privileges to work sensibly, nor does it need /usr mounted to function.

 *Quote:*   

> - Same issue as noted above with the tty's than be fixed by using the -P flag on runsvdir within /etc/runit/2

 

FWIW, vanilla runit provides a correct /etc/runit/2 and I filed a bug for that too. Gentoo insists on shipping a broken one.

 *Quote:*   

> - the ebuild puts SVDIR=/etc/service in /etc/env.d, this is good because putting services in /service is inane. But bad and solves very little because sv expects a /service directory to exists. I symlinked it to /etc/service for now to fix a couple of annoying bugs with sv.

 

Upstream said something a while ago (in geological timescales) about fixing the hardcoded /service thing, there hasn't been a release since then though.

 *Quote:*   

> If anyone knows how to port rc sysinit and rc default to runit and do away with openrc completely. I'd love to now.

 

rc default is really easy to replace (see link in sig, it's almost all one-liners), I don't feel confident enough to do away with rc sysinit though. Too much obfuscated magic going on there.

 *Quote:*   

> /service, /etc/service, /var/service, they're all horrible choices for that directory that contains the "current" runlevel. [...] a process really should not write to /etc under normal operation

 

I agree wholeheartedly with this point, but I've been too indifferent to do anything about it. Might give it another try.

To be fair runit itself isn't braindead; its own FAQ says you can symlink all the */supervise/ directories to a tmpfs if you really want and it supports running like this.

 *Quote:*   

> I created the sv group and made the permissions of the supervise files as such that members of the sv group can ask for the status of services without having to use root. I think it's silly you need to be root to ask for a status. 
> 
> since this is reset at boot because /run is on a tmpfs. I actually created a service that actually monitors these permissions and if they are off it resets it back to the right ones, yeh... 

 

That's weird and it sounds like Gentoo-specific screwery. There's no reason for those directories to not be world-readable.

----------

## mi_unixbird

It is weird and not necessary, but I find it paranoid to need root to _read_ the status of services. OpenRC and sysvrc allow status to be seen without even being in a specific group as far as I know. Needing to be in the sv group is more than enough.

That said, I've been hacking a bit more. I've modified the sysvinit package to install the init binary in /sbin/sysvinit instead of /sbin/init and made init a symlink which can be changed with eselect from runit-init to sysvinit.

I've partially implemented a system now as well where the eselect link also changes shutdown reboot poweroff and halt links to sysv{shutdown,reboot,poweroff,halt} and runit-{shutdown,reboot,poweroff,halt}. I've implemented a simple script that copies some of the functions that we know and love from those binaries and relays them to runit to get the behaviour back.

An "eselect init" system should probably exist, so here's my take:

```
DESCRIPTION="Manage /sbin/init implementations"

VERSION="0.1"

SYMLINK_PATH=/sbin/init

SYMLINK_TARGETS=( sysvinit runit-init )

SYMLINK_DESCRIPTION='init process'

SYMLINK_CRUCIAL=1

inherit bin-symlink

do_set () {

   local base=${1%init}

   local files="init halt reboot poweroff shutdown"

   local link

   local target

   # we first check the sanity of everything before we actually make any modiications

   for link in $files; do

      target=$EROOT/sbin/$base$link

      link=$EROOT/sbin/$link

      [[ -e $link && ! -L $link ]] && die -q "$link exists but is not a symlink"

   done

   

   for link in $files; do

      target=$EROOT/sbin/$base$link

      link=$EROOT/sbin/$link

      ln -sf "$target" "$link" || die -q "Could not not link $link to $target"

   done

}

```

----------

## mi_unixbird

Right, so this is my implementation right now for anyone who's interested:

/etc/runit/1:

```
#!/bin/sh

# system one time tasks

set -u

PATH=/sbin:/usr/sbin:/bin:/usr/bin:/lib/rc/bin

openrc sysinit || {

   sulogin

   exit 100

   }

openrc boot || {

   sulogin

   exit 100

   }

for arg in $(cat /proc/cmdline); do

   case "$arg" in single)

      sulogin

      exit 100

      ;;

   esac

done

openrc runsvdir || {

   sulogin

   exit 100

   }
```

The obviously relevant part is the "runsvdir" runlevel. This contains three scripts:

/etc/runlevels/runsvdir/runit-files:

```
#!/sbin/openrc-run

description="make the files runit needs to shutdown and reboot the system"

start () {

   ebegin 'Creating stopit and reboot files'

   mkdir -p /run/runit &&

   touch /run/runit/stopit &&

   chmod 0 /run/runit/stopit &&

   touch /run/runit/reboot &&

   chmod 0 /run/runit/reboot

   eend $?

}
```

This one is used to create the stopit and reboot files, note that they live in "/run/runit" instead of "/etc/runit" and symlinked from there. This is to allow "/etc" to be on a read-only  filesystem.

/etc/runlevels/runsvdir/runsvdir-runsupervise

```
#!/sbin/openrc-run

svcontainer=${svcontainer-/etc/sv}

runsupervise=${runsupervise-/run/sv/supervise}

depend () {

   keyword -shutdown

}

start() {

   ebegin "Making supervision directory $runsupervise"

   mkdir -p "$runsupervise"

   eend $?

}

stop() {

   ebegin "Removing supervision directory $runsupervise"

   rm -r "$runsupervise"

   eend $?

}
```

This simply creates a directory "/run/sv/supervise", this is because all the "/etc/sv/*/supervise" directories are symlinked to that directory, again, to keep /etc read-only if possible so /etc/sv/sshd/supervise is a symlink to /run/sv/supervise/sshd

And finally

/etc/runlevels/runsvdir/runsvdir-runlevel:

```
#!/sbin/openrc-run

description="set the runlevel runsvdir needs from the kernel command line"

SVDIR="${svdir-/run/svon}"

SVLEVELDIR=/etc/svlevels

start () {

   local level_ level=default arg

   einfo "Setting runsvdir runlevel based on kernel command line"

   for arg in $(cat /proc/cmdline); do

      case "$arg" in softlevel=?*)

         level="${arg#softlevel=}"

         if [ -d "$SVLEVELDIR/$level_" ]; then

            einfo "Setting runsvdir runlevel to $level_"

            level="$level_"

         else

            ewarn "runlevel $level_ does not exist"

         fi

         ;;

      esac

   done

   ln -s "$SVLEVELDIR/$level" "$SVDIR"

   return $?

}
```

Sets the runlevel which on my system lives in /etc/svlevels based on the kernel command line and defaults to /etc/svlevels/default if non provided. As you can see I do not use the common /var/service but /run/svon for a symlink to the currently active runlevel because I feel it should be in /run

/etc/runit/2 is simply a simple "exec runsvdir -P /run/svon"

I've currently completely embraced OpenRC for system one time tasks opposed to trying to run runit without it. runit-init is used as pid1. runsvdir for process supervision and OpenRC for booting the system.

----------

## kurac51

I see some people used runit on gentoo as there only init how was it i was thinking about it but i don't know how well does it work is it perfect like SystemD or OpenRC?

----------

## Ant P.

Runit will not handhold you unlike those other systems. You're required to know how to think and do things for yourself if you go down this path; there are no prewritten init scripts. In a sense it's like Gentoo itself, a meta-init.

If you're asking if it's maintained to the same standards as the other two: williamh "owns" the ebuilds for all three in the main tree, so yes. Don't use that one unless you like reporting bugs and not getting replies for months.

----------

