Please check the status of this specification in Launchpad before editing it. If it is Approved, contact the Assignee or another knowledgeable person before making changes.
Launchpad entry: https://blueprints.launchpad.net/upstart/+spec/states
This specification proposes the introduction of a new first class object into the Upstart core, that of a State. Simply defined, a State is said to be true when a certain set of conditions are true and a counter-set are false.
The pure event-driven model works extremely well for tasks, however it has notable limitations in the definition of services. Services tend to be defined not by the events that cause them to start or stop, but the system state in which they should be running.
It is true that the definition of the system state can be made entirely with reference to events, and more complex operators than just "or"; this was tackled by the ComplexEventConfig specification. However we found that with the introduction of the temporal "until" operator, and the need for state definitions to be flexible to system configuration (which set of path needs to be mounted for the filesystem to be considered ready?), the definition requirements for a service author were needlessly high.
Some of this was alleviated by the JobsAsStates specification, however this brought exactly the same issues into play when trying to combine states and did not offer the necessary flexibility to achieve some of the use cases outlined below.
By defining states themselves as first-class objects, with a prescribed behaviour that can differ from the default behaviour of jobs, we can implement the desired use cases without any particularly large burden on implementation, configuration or memory usage.
- The DBUS daemon should be running whenever the standard FHS filesystem is available; the definition of this state depends on the system configuration, since it requires that the set of paths that have been mounted is a superset of the paths that we expect to have been mounted given the system configuration.
- A daemon that provides a service to the network, for example a web server, should be running while at least one non-local network interface is up and the standard FHS filesystem is available. This requires that we maintain discreet instances of the "network interface is up" state for each network interface.
A getty should be run on each available system console, this should be configured as an instance job and tied to a state defined as the period between the tty-added and tty-removed events for a given TTY. For each instance of this state, an instance of the getty job should be spawned, and given information about which TTY it should use.
- Daemons may offer the ability to be run multiple times, provided that a discreet configuration file is available for each instance. The job should be able to be defined once, taking the name of the configuration file from a state defined by events received from inotify, and an instance of that job spawned for each configuration file that exists.
Many daemons such as HAL rely on the DBUS daemon, they should be able to define the state in which they are running as the state in which the DBUS daemon is running. This should have the same semantic as using the started and stopping events, the DBUS daemon should be blocked from stopping while there are jobs running in a state defined based on that event.
This scope of this specification is limited to the introduction of States as a first-class object, and permitting the definition of these states as entities in their own right or states attached to job definitions.
It makes no changes to the behaviour of Events or Jobs, those will be tackled, if necessary, by further specifications.
- State is introduced as a new first-class object.
- The first part of the definition of a state is a boolean combination of events that define the beginning of the state. Events may be matched in the usual way.
from (tty-added or cua-added) and foo
- The permitted Event operators are as follows, in order of precedence.
or, and: unary boolean operators
(...): sub-expression grouping
- When a state begins, an instance of that state is activated and holds the environment of each event that caused it to begin. This combination of environment from the events constitutes the environment of the state.
- The events that begin the state must happen again for a new instance to be activated.
- Multiple instances may be active at any one time, and these instances may be identical. Emitting an event twice could cause two duplicate states to exist, however since these states are identical and will be stopped by the same single event, this is not considered to be a problem.
- The second part of the definition of a state is a boolean combination of events that define the end of the state. Events may be matched in the usual way, and may make references to variables within the environment of the state.
until (tty-removed $TTY or cua-removed $CUA) and bar
- Instances are destroyed when the state that was begun by them ends.
- The third part of the definition of a state is a boolean combination of other states that define when the state can be TRUE. This combination does not affect whether instances of the state are spawned, however no instance can be TRUE while this combination of other states is FALSE.
while network-up and fhs-filesystem-mounted
- A state is considered to be TRUE if the state combination is TRUE and there are active instances.
- A state is considered to be FALSE if the state combination is FALSE or there are no active instances.
- States may be matched directly by name, and may also refer to environment of the state in a similar manner to events. This will be TRUE if the state is TRUE and any active instance has the specified environment.
- Multiple matches on the same State are permitted.
interface-up IFACE=eth0 and interface-up IFACE=eth1
- The rvalue of the match may be an expression that returns multiple answers; this will be TRUE if the state is TRUE and there is an active instance matching each of the returned answers.
path-mounted PATH=$(shell getmntent -fD)
- The permitted State operators are as follows, in order of precedence.
or, and: unary boolean operators
not: binary boolean operator
(...): sub-expression grouping
- States may be directly defined by a configuration file, in which case they have their own name in a unique namespace.
- Job definitions include the definition of a state, which is coupled to the goal of the job. This state does not have a name, and is considered part of the job definition.
A special running state is defined, this has one instance for each running job with the job name and id available in the environment.
while running apache and running dbus
Data preservation and migration
- Do states need to be able to block other states? Consider the following:
state dbus from started dbus until stopping dbus end state job foo while dbus end job
If the dbus state is not blocked by the foo job, then the dbus job can stop without waiting for foo to stop. OTOH, if we consider the opposite:
job foo while networking and filesystem end job
If the foo job does block these states, then effectively there's never any difference, so any job with just while networking would have to wait for filesystem since that state is blocked! There's also the issue of what a blocked state means. Jobs are affected by events the moment that they are issued, the fact an event is blocked merely affects when the emitter is let go. Jobs are blocked from changing state while their events are blocked because they are the emitter. So would blocking a state behave like a job (state prevented from becoming true), or is it merely a method of deciding when to unblock events. The latter case would solve the "networking and filesystem" issue from the state point of view, but would mean that whatever events were involved in networking and filesystem would be blocked (and thus their emitter blocked) until all services that need network are started. The direct method of referencing these events has the same effect.
- The difference between named and positional arguments, and the problems that have occurred, applies equally to States and Events. Since States have been defined from the beginning as having only named arguments, it might be interesting to see where States and Events meet in the middle. One can forsee a state definition such as:
state interface-up from interface-up until interface-down $IFACE args IFACE end stateAnd an event definition that looked like:
# initctl emit interface-up IFACE=eth0 ...Which provides nameless matching of selected arguments.
The definition of things like $(shell ...) is not done anywhere, and isn't fixed in stone yet.