s6-rc
Software
skarnet.org

s6-rc: FAQ

The s6-rc-compile source format

The source format for s6-rc-compile is not very convenient. Why not put all the information for a service in a single file?

Because parsing sucks. Writing parsers is an annoying, ungrateful task; and automatic parser generators produce big and inefficient code. For security, efficiency and maintainability reasons, I prefer to focus my efforts on code that actually does stuff, not parsing code.

Using the filesystem as a key-value store is a good technique to avoid parsing, and skarnet.org packages do it everywhere: for instance, look at s6-envdir. The s6-rc-compile source format is just another instance of this technique.

This format generally plays well with automated tools, be it for reading, as s6-rc-compile does, as for writing. I fully expect the s6-rc-compile source format to be used as the input (resp. the output) of some automated tools that would convert service definitions to (resp. from) another format, such as systemd unit files, sysv-rc scripts or OpenRC scripts; at least the s6-rc source format will make it easy on those tools.

And if you love configuration files, don't mind writing a parser (which is indubitably easier to do in other languages than C), and want to write a program that takes a text file, parses it and outputs a service definition directory, it should also be rather easy.

There are no "Provides:", no virtual services. What do I do if I have several implementations for a service?

Use bundles. Bundles are awesome.

Let's say you want to provide a ssh daemon, and have two possible implementations, opensshd and dropbear, but you want to provide a virtual service named sshd.

Define your two longruns, opensshd and dropbear; then define a bundle named sshd that only contains your default implementation, opensshd. Use the name sshd in your dependencies. When you run s6-rc-compile, all the dependencies will resolve to opensshd, and the compiled service database will consider opensshd to be the "real" service; but users will still be able to run s6-rc commands involving sshd. And if users want to change the default to dropbear, just change the sshd/contents file to dropbear, recompile the database, and run s6-rc-update.

The advantage of proceeding this way is that online service dependencies are kept very simple: dependencies are a directed acyclic graph, which is easy to handle - that is the reason why the compiled database is small, and why the s6-rc program is so small and fast. There are "AND" dependencies, but no "OR" dependencies, which would introduce great complexity both in data structures and in the dependency resolution engine. s6-rc handles this complexity offline.

You can use bundles to represent any collection of services, and write all your dependencies using only bundle names if you want. Bundles have multiple uses, and virtual services are definitely one of them.

Switching from another service manager

I have a collection of init scripts in another format, but don't want to wait until the whole collection is converted before switching to s6-rc. Is there a smooth way in?

Yes.

If you are using a service manager such as sysv-rc or OpenRC, you have a collection of init scripts that can be called with at least start and stop arguments. You also know dependencies between those scripts, or at least a reasonable ordering.

You can automatically generate a source directory for s6-rc-compile. For every init script /etc/init.d/foo that you have, create a service definition directory named foo:

You can now run compile your s6-rc service database, and use the s6-rc engine as your service manager. Transitions will use your original init scripts, and the supervision features of s6 will not be used, but you will get proper dependency tracking and easy state changes.

Then, you can improve the database by changing services one by one, turning them into longruns so daemons get supervised when applicable, rewriting them into bundles calling more atomic services if needed, etc. That can be done at your own pace, one service at a time, while still getting some benefits from s6-rc; and if an iteration doesn't work, you can always roll back while you fix it.

There are no runlevels in s6-rc. I like runlevels.

You have better than runlevels. You have bundles.

When writing your service database in source format, take note of the common sets of services that you like to run together, what other init systems sometimes call runlevels. For each of those sets, define a bundle containing all those services. For instance, you could define a runlevel-1 bundle that contains only a single getty, a runlevel-2 bundle that contains only your local services and no network, a runlevel-3 bundle that contains runlevel-2 as well as network services, and a runlevel-5 bundle that contains runlevel-3 and your desktop. You can even create a runlevel-0 bundle that contains nothing at all!

In your boot script (/etc/rc.init, for instance, if you're using s6-linux-init), after invoking s6-rc-init, just ask s6-rc to start the set of services you want up by default: s6-rc change runlevel-5.

If you later want to change your current set of services, you can then tell s6-rc to switch, using the -p option to make sure to stop services you don't want up anymore: s6-rc -p change runlevel-2.

Bundles are easy to use, they're flexible, and they're powerful. They give you the same level of functionality as runlevels would, and more. You can even add bundles to compiled service databases - including the live one - or remove bundles from them without having to recompile them: that's what the s6-rc-bundle utility is for.

When in doubt, use bundles.