In the previous posts, I’ve covered the various features that make Upstart a good service manager, but these are things you’ll find in most others as well. It’s now time to cover that which is singularly unique to Upstart, Events.
Start and Stop
You’ve already seen the
stop commands, which do somewhat unsurprising things to jobs. The important thing to remember about these is that they are not events. I just wanted to clear that up before we start, since it’s often been a source of confusion not helped by the design of some earlier versions of Upstart.
stop operate directly on jobs, and the command will not normally return until the operation is complete or otherwise interrupted. Services are considered complete when they are running, Tasks are considered complete when they have stopped again; in both cases the stop command is complete when the service or task has actually stopped.
This is important since it provides a common-sense behaviour, ensuring that the following operation is not a race condition:
# start apache apache running (start), process 3591 # wget http://localhost/
Solving race conditions is one key part of Upstart’s purpose.
Both commands may also set environment variables, those set by the start command form part of the environment of the job itself and those set by the stop command are available to the
# cat /etc/init/jobs.d/getty instance $TTY env SPEED=38400 exec /sbin/getty $SPEED $TTY # start getty TTY=tty1 getty (tty1) running (start), process 4152
As described above, the start and stop commands are admin instructions that act directly on named jobs. Events have many similar properties: they carry environment variables that end up in the environment of jobs they start, and they are not complete until the jobs that they affected have been started or stopped as appropriate.
The difference is that the start and stop commands are targeted at specific jobs, whereas events have no such targetting and instead it is jobs that specify which events they are interested in.
In the Upstart world events serve three general purposes: they act as signals of state changes that jobs can react to (e.g. hardware going away), as method calls to automatically start or stop jobs (e.g. shutdown) and as a way of passing information between jobs.
Events are identified by their name and have a different namespace to that of jobs. They are emitted by a D-Bus call or by using
emit on the command-line, naming the event and providing any associated environment variables you wish:
# emit interface-up IFACE=eth0 ADDRFAM=Ethernet ADDRESS=01:23:45:67:89:0a
Jobs may match them on this name and any number of their environment variables, specifying whether the event would automatically start or stop the Job.
start on interface-up IFACE=eth* ADDRFAM=Ethernet
As a short-hand, where the order of the variables for an event is fixed, the names may be omitted:
start on interface-up wlan*
When a job is started by an event, the environment for that event forms part of the environment for the job and may be used when matching events that can automatically stop the job. Harking back to our
getty job from previous posts, we can bind this to the lifetime of the underlying device.
start on tty-added stop on tty-removed TTY=$TTY instance TTY exec /sbin/getty 38400 $TTY
We can also match multiple events, either requiring that both occur or either using unsurprising operators:
start on a-up and b-up stop on a-down or b-down
In these situations, once stopped, both the
b-up events must happen again for the job to be restarted.
Upstart itself only emits a few events, leaving the rest up to application authors to define. The
startup event is the most interesting of these, and is ultimately what nearly all jobs get chained from.
As jobs are started and stopped, Upstart emits events on their behalf for four key points in their lifecyle.
- starting is emitted when the job is first starting, and the job will not actually be started until this event completes.
- started is emitted once the job is fully running.
- stopping is emitted when the job is stopping (after the pre-stop has completed), the job will not actually be stopped until this event completes.
- stopped is emitted once the job is fully stopped.
All of the events have the name of the job in the first variable,
JOB and the instance of the job (if applicable) in the second variable,
INSTANCE. The stopping and stopped events then have a series of variables indicating the reason for the job stopping:
RESULT indicates whether it was a normal stop or a failure then if it failed,
PROCESS will say what failed and
EXIT_STATUS will contain the terminating signal or exit code.
For example, we can take action to backup a database if the server crashes:
start on stopping hersql RESULT=failed EXIT_SIGNAL=SEGV task exec hersql-backup
Jobs can also export variables from their own environment to others through these events by using the
start on interface-up stop on interface-down $IFACE instance $IFACE export IFACE exec ...
Another job may then be started along with this one, and know what interface it’s bound to:
start on started JOBNAME stop on stopping JOBNAME instance $IFACE
We’ll look at the various powerful forms of dependency that these events allow us to express in the next post.