Summary

Upstart currently allows jobs to define a list of events that can cause it to be started and stopped, where any single one of these events changes the goal. This specification proposes the replacement of this simple behaviour with the ability to construct more complex expressions for the set of events that start the job and set that stop it, and the job changes necessary as a result of this.

Rationale

A simple list of possible events is sufficient for starting tasks that deal with just that event, however it is not sufficient for more complicated scenarios where multiple events may be required.

Use cases

A job needs to be started when two different events both occur, in any order.

A job needs to be stopped when either of two different events occur.

Scope

This scope of this specification is limited to changes to job handling to allow for event expressions to start and stop the job, rather than a list of any given events. Since this changes the fundamental assertion that there is only one "cause" of a goal change, the scope includes the changes necessary to allow for multiple "cause"s.

Complex periods of events are not defined by this specification, instead this specification allows a job to define one single period between expressions. The ability to define and use multiple periods will be defined by the States specification.

Design

Syntax

Both start on and stop on are changed to accept an event expression, rather than a single event.

Only one event expression is permitted for each of start on and stop on, repeating the stanza will override the previously defined expression.

An expression is an infix boolean combination of events, parsed into an operator tree for evaluation.

The permitted operators are as follows, in order of precedence.

or, and: unary boolean operators

(...): sub-expression grouping

Non-operator tokens, including the quoting of operators, are treated as event names or arguments to events.

start on started dbus and started gdm
stop on stopping dbus or stopping gdm

start on (started dbus and started g-p-m) or started ctld

Within parentheses, newlines are treated as whitespace (Python-esque).

start on ((started dbus and started g-p-m)
or started ctld)

The expression, or sub-expressions within parentheses, must begin and end with an event, it is a syntax error for it to begin or end with a unary operator.

Parentheses may only follow an operator, it is a syntax error for them to follow an event.

Behaviour

Each node in the parsed tree represents an event operator (or or and) or an event match.

Each node has a boolean value that is initially set to FALSE.

The value of an event match node is set to TRUE if a matching event occurs.

The value of an event operator node is set according to the values of its children.

When an event occurs, each node in the tree is checked, and the value changed if the event matches. Each parent node is then updated to the new value.

The goal of the job is only changed if the event matched and the value root node of the tree is now TRUE.

The start on expression sets the goal to start.

The stop on expression sets the goal to stop.

The stop expression tree is updated before the start expression tree, so that the same event appearing in both trees will cause a restart, rather than strange behaviour.

The event that was matched is stored in the event match node, and referenced by it; they are also initially blocked. This replaces the previous cause member.

When a job reaches the expected state (defined by being a task or a service), the start expression tree is unblocked, but the events remain referenced to supply environment.

When a job reaches the stopped/waiting state, both the start and stop expression trees are unblocked and reset so that no references or blockers are held on the events.

Spawning a new instance copies the start expression tree to the instance, including the information about matched events along with references and blocks to that event. The master instance is then reset, so that the expression of events must happen again for a new instance to be spawned.

If a job fails to reach its goal, all appropriate blocked events are marked as failed. Nodes with a FALSE value, and children of those nodes, are not considered since they have not caused the job to fail.

Job Environment

Job processes will no longer receive the UPSTART_EVENT environment variable, since there are now multiple likely events.

Job processes will receive the environment variables of all appropriate matched events in the start expression tree, in tree iteration order. Nodes with a FALSE value, and children of those nodes, are not considered since they have not caused the job to start.

Job script processes will no longer receive the positional arguments of the events, since there is no particularly logical order or construction for them.

Job script processes will instead receive the names of the events in the UPSTART_EVENTS environment variable. Jobs can then know what to expect in their environment.

Notification

Subscriptions apply to all appropriate blocked events. Nodes with a FALSE value, and children of those nodes, are not considered since they have not caused the job to change goal so are not relevant to subscription.

Implementation

Code

Data preservation and migration

These changes are not entirely backwards compatible with the previous behaviour, since multiple start on and stop on stanzas have previously appended to the list of events and must now be changed to a single stanza with repeated use of the or operator.

At this phase of Upstart development, it is acceptable for job authors to be required to change their jobs.

Unresolved issues

Should we use different stanza names?

from started dbus and started gdm
until stopping dbus or stopping gdm

The events in the stop on tree should be able to reference environment variables from the events in the start on tree; this will likely be added by another spec.

start on tty-added
stop on tty-removed $TTY

Event positional arguments are expected to be deprecated entirely. Another spec?