s6-rc: services

The basic building block

Table of contents

Service types

In all genericity, a service is a basic unit that can undergo a transition; but not all services can be handled the same way. Services are divided into several categories, which we call types; these are the following.

  1. Longrun.

    A longrun is the "traditional" definition of a service, implemented by a long-lived process, a.k.a. a daemon. As a first approximation, it means that when the daemon is alive, the service is up, and when the daemon is not present, the service is down. Longruns are the most common type of service, and the main reason why it's a good thing for a service manager to work in tandem with a process supervisor: the details of keeping the daemon alive, surveying its readiness, etc. are delegated to the process supervisor, which abstracts some complexity away from the service manager.

  2. Oneshot.

    A oneshot is a service that represents a state change in the machine, but that does not need a daemon because the state is maintained by the kernel. For instance, "mounting a filesystem" and "setting a sysctl" are oneshots: the service is considered up when the filesystem is mounted or the sysctl has been performed, and down when the filesystem is unmounted or the sysctl has its default value. Note that it's generally meaningless to revert a sysctl (and in most cases it's also a bad idea to try and unmount filesystems before the very end of a shutdown procedure), so it is quite common for the down transition of a oneshot to be a nop: after the first time the service has been brought up, the state basically never changes.

    Longruns and oneshots are collectively called atomic services. They are the core service types, the ones that actually do the work. Other service types are just convenience tools around them.

  3. External.

    An external is a service that is not handled by the service manager itself, but by a system that is external to it. It is a way for the service manager to delegate complex subsystems to other programs such as a network manager. The service manager does not know how to perform transitions for an external, it does not know anything but its name.

    It is impossible to set the wanted state of an external: such a service has to be triggered entirely outside of the service manager. All the service manager does is receive events that inform it of the external's current state.

    Consequently, an external does have any dependencies. It is, however, possible for a service to depend on an external — that is their intended use, gating the transition of another service to the reception of an external event.

  4. Bundle.

    A bundle is a pseudo-service representing a set of services: it is used to implement service conjunction (AND). when a bundle is wanted up, it means that all the services it contains are wanted up. A bundle's current state is up if all the services it contains are up, and it is down otherwise.

    However, when a bundle is wanted down, it also means that all (and not just one!) of the services it contains are wanted down, so take care when explicitly bringing down bundles.

  5. Virtual.

    A virtual is a pseudo-service representing a set of services, but used for disjunction (OR) instead: instead of meaning "all the services in the set", it means "one of the services in the set". A virtual's current state is up if at least one of the services it represents is up, and down otherwise.

    1. Dynamic instantiation

      In all genericity, a service is a basic unit that can undergo a transition; but not all services can be handled the same way. Services are divided into several categories, which we call types; these are the following.

    2. Dynamically instantiated longrun.

      A dynamically instantiated longrun, or DIL, is a template for an indeterminate amount of longruns that all follow the same model, and that differ by one parameter, the instance name. They are used to implement sets of similar services that the user will want to start on demand: for instance, a set of gettys. A DIL is identified by a @ at the end of the service name; anything that follows the @ is the instance parameter. For instance, getty@ can be the name of the DIL spawning the gettys, and getty@tty2 can be a dynamic instance of getty@ with tty2 as the instance parameter.

      (It is possible to define a regular, static (as opposed to dynamically instantiated), getty@tty1 service even if the getty@ DIL exists: in that case, getty@tty1 will always refer to the static service and it will be impossible to spawn a getty@ instance with tty1 as an instance parameter. This can be a good way to ensure that specific "instances" are special-cased.)

      However, DILs have a strong limitation: only dynamically instantiated services can depend on them, and only with the same instance parameter. In other words: B cannot depend on A@, only B@ can depend on A@, and that means that for any x, B@x depends on A@x.