s6-rc: the s6-rc-update program
Keywords: s6-rc command rc update s6-rc-update init dependency state management services

s6-rc
Software
skarnet.org

# The s6-rc-update program

s6-rc-update is an online service database switcher: it will replace your compiled service database with another one, and adjust the live state accordingly. This allows you to change your set of services without having to reboot.

Live upgrading a service database is not easy, and no fully automated system can get it right in all cases. s6-rc-update will do its best on its own, but it lets the user give it instructions to handle difficult cases; and rather than implement doubtful heuristics, it will fail with an error message in situations it cannot solve.

## Interface

         s6-rc-update [ -n ] [ -v verbosity ] [ -t timeout ] [ -l live ] [ -f convfile ] [ -b ] [ -E | -e ] newdb

- s6-rc-update analyzes the current live state, the current compiled service database, and the compiled service database contained at newdb (which must be an absolute path).
- Additionally, it can process an optional conversion file containing instructions.
- It computes the necessary service transitions to safely update the live database.
- It shuts down the necessary services.
- It updates the live directory so that newdb becomes the live compiled service database.
- It starts up the necessary services.

## Exit codes

- 0: success
- 1: failure to perform some state transitions, but the database was switched to newdb
- 2: timed out, but the database was switched to newdb
- 3: unknown service name in the conversion file
- 4: invalid service database
- 5: wrong service type in the conversion file
- 6: duplicate service in the conversion file
- 9: failure to perform some state transitions, and the database was not switched
- 10: timed out, and the database was not switched
- 100: wrong usage
- 111: system call failed
- 126: cannot exec into s6-rc for the final "up" transition (error other than ENOENT)
- 127: cannot exec into s6-rc for the final "up" transition (executable not found)

## Options

-n, --dry-run
  Dry run. s6-rc-update will compute the service transitions, and print the s6-rc command lines it would execute to perform those transitions. It will not actually run them or modify the live database.
-v verbosity, --verbosity=verbosity
  Be more or less verbose. Default is 1: warning and error messages will be printed to stderr. 0 silences warnings. 2 adds a bit more information about what s6-rc-update is doing. 3 or more is heavy debug output.
-t timeout, --timeout=timeout
  If s6-rc-update cannot perform its job within timeout milliseconds, it will exit. The default is 0, meaning infinite (no timeout). Be aware that timing out and exiting may leave the live database in an inconsistent state, so use of this option is not recommended.
-l live, --livedir=live
  Look for the live state in live. It must be an absolute path. Default is `/run/s6-rc`. The default can be changed at compile-time by giving the `--livedir=live` option to `./configure`.
-f convfile, --conversion-file=convfile
  Use the conversion file located at convfile. Default is `/dev/null`, meaning no special instructions.
-b, --block
  Blocking lock. If the database is currently being used by another program, s6-rc-update will wait until that other program has released its lock on the database, then proceed. By default, s6-rc-update fails with an error message if the database is currently in use.
-E, --no-force-essentials
  If essential services are scheduled to be stopped for the transition, ignore them, i.e. keep them alive. This is the default.
-e, --force-essentials
  If essential services are scheduled to be stopped for the transition, stop them just like any other service.

## Transition details

s6-rc-update's job is to ensure consistency of the live state across a change of compiled service databases. To do so, it must make sure that the services that are up at the time of its invocation are still up after its invocation; but service definitions in the new compiled may be different from those in the old one - in particular, dependencies may change, or a service can change types: a oneshot can become a longrun and vice-versa, and an atomic service can even become a bundle.

### Service identity

s6-rc-update examines atomic services, as defined in the old compiled, that are up at invocation time, and computes what is necessary for the "same" services, as defined in the new compiled, to be up. Barring instructions from the conversion file, the service is "the same" if it has the same name in the new compiled, no matter its type.

So, if there is an up oneshot named `sshd` in the old compiled, and there is a longrun named `sshd` in the new compiled, then s6-rc-update will decide that the new `sshd` longrun will replace the old `sshd` oneshot. If the new compiled defines a `sshd` bundle instead, then s6-rc-update will decide that the old `sshd` oneshot will be replaced with the contents of the `sshd` bundle.

### Restarts

s6-rc-update tries to avoid needlessly restarting services. For instance, running it with a new compiled that is an exact copy of the old compiled should not cause any restarts. s6-rc-update will flag a service to be restarted in the following cases:

- The service has disappeared in the new compiled. In this case, the old service will simply be stopped.
- The service has changed types: a oneshot becomes a longrun, a longrun becomes a oneshot, or an atomic service becomes a bundle. In this case, the old service will be stopped, then the new service will be started.
- The service has a dependency to a service that must restart, or to an old service that must stop, or to a new service that did not previously exist or that was previously down.
- There is an instruction to restart the service in the conversion file.

### Steps

After it has decided what services it should restart, s6-rc-update will:

- Invoke s6-rc to stop old services.
- Update the live directory with the data from the new compiled. This is the critical part; s6-rc-update should not be interrupted at this point. It does its best to avoid risking leaving behind an inconsistent state, but a 100% atomicity guarantee is impossible to achieve.
- Adjust pipe names for the existing pipelines, if needed.
- Exec into s6-rc to start new services.

## The conversion file

The conversion file is used to give s6-rc-update instructions when the change of databases is not entirely straightforward. Currently, it supports the following features:

- changing the name of a service
- forcing a restart on a service

### Format

The conversion file is a sequence of lines; each line is parsed independently, there's no carrying of information from one line to the next.

A line is lexed into words by the execlineb lexer, which means that words are normally separated by whitespace, but can be quoted, that `#` comments are recognized, etc.

The first word in a line must be the name of an "old" atomic service, i.e. an atomic service contained in the current live database. The remaining words in the line are instructions telling s6-rc-update how to convert that service.

#### Renaming

If the second word in the line is `->`, then the third word in the line must be the new name of the service in the new database: s6-rc-update will then rename it. It is possible to rename an atomic service to another atomic service or a bundle, but no matter whether a service is renamed or not, changing its type will force a restart.

#### Restarting

If the word following either the old name, or a renaming instruction, is the word `restart`, then the service will forced to restart.

### Examples

Consider the following conversion file:

    # Simple conversion file
    mount-var -> mount-rwfs
    httpd restart
    sqld -> mysqld restart

- It will rename `mount-var` to `mount-rwfs`, not restarting it if `mount-var` in the old database and `mount-rwfs` in the new database have the same type and do not depend on services that would force a restart.
- It will restart `httpd`
- It will rename `sqld` to `mysqld` and make it restart.

## Safely managing and updating compiled databases

The sequence of operations needed to update a database from source to live can be a little tricky. It involves:

- Compiling the new database without overwriting the live one
- Calling s6-rc-update to safely replace the live database
- Making sure the new database will be correctly used at boot time

This FAQ entry gives a step-by-step explanation on how to proceed.

## Notes

- The live argument to the `-l` option, if present, should be the same argument that was given to the s6-rc-init invocation when the system was booted. In other words: it should be the name of the symbolic link pointing to the real directory containing the live information, not the name of the real directory.
- If a longrun service is renamed from oldname to newname, but not restarted, the s6-supervise process in charge of it will still show up in the process list as `s6-supervise oldname`. This is purely cosmetic and will have no impact on the service; nevertheless, if you wish to avoid that, simply force a restart on every longrun you rename.
- After an s6-rc-update invocation, the old compiled service database is left unchanged where it was, and the new compiled service database is used in-place. If the machine is rebooted, the s6-rc-init invocation will still boot on the old compiled unless more work is performed. It is recommended to keep a `current` symbolic link to the current compiled, to always s6-rc-init on `current`, and to make `current` a link to the new compiled right after an s6-rc-update invocation.
- s6-rc-update is the only way to "free" the old compiled database for displacement or deletion. A live compiled database must not be tampered with. After an s6-rc-update invocation, the old database isn't live anymore and can be moved or deleted; instead, newdb is live.