In the previous posts we’ve looked at how Upstart’s events can be used as signals and as methods, in this post we’ll look at the third way in which they can be used: as hooks.
It’s worth a recap to fully explain how this works. Recall that signals are notifications that something happened on the system or changed about it; and that the most important thing about signals is that they have no duration and no follow-up. The emitter doesn’t care whether the signal is handled, or how long it takes to handle.
A signal event is emitted with the –no-wait parameter to initctl emit:
initctl emit --no-wait control-alt-delete
And a signal event is matched with start on or stop on, since the emitter isn’t waiting, it doesn’t make any difference whether a task uses the task keyword or not.
start on control-alt-delete exec shutdown -r now
In contrast, when events are used as methods, the result is important since the emitter is creating the event in order for a change to happen. The job is expected to perform that change, and initctl emit waits for it to complete before returning to the shell.
initctl emit runlevel RUNLEVEL=2
Because something is waiting, it’s more important that we use the task keyword if initctl should wait for the job to complete. Of course, it’s perfectly legal for a method event to be used to start or stop a service as well, in which case it wouldn’t be used.
start on runlevel task exec /etc/init.d/rc $RUNLEVEL
So now we look at hooks. A hook is somewhere between a signal and a method. It’s a notification that something changed on the system, but unlike a signal, the emitter waits for it to complete before carrying on.
The purpose of a hook is to allow other tasks, services (or packages) to hook into processing to perform its own actions before or after some change has taken place.
Both initctl emit and the start on or stop on keywords look the same as if the event as being used as a method, the difference is that the caller isn’t expecting something to happen as a result of the event, but is allowing the system to perform any processing before the caller itself makes something happen.
Things are never clear without examples, let’s use the process of unmounting filesystems on shutdown; that’s a popular one right now. How would a hook help? Let’s place a call to initctl emit before the call to umount -a:
initctl emit unmounting-filesystems umount -a
Since we didn’t use –no-wait, the initctl emit call will block while Upstart works and any services are started or stopped and any tasks run. Now we can use this hook to ensure things are done before the filesystem is unmounted, for example we can stop services:
stop on unmounting-filesystems
And we can even run tasks which must run and complete before the filesystem is unmounted:
start on unmounting-filesystems task script # last-minute-cleanup end script
There are plenty of hook events in any Upstart system, beginning with the starting and stopping events emitted by Upstart itself and including events such as mounting and mounted emitted during initial filesystem construction. Take a look at the manual pages for those events, section 7 if you recall from earlier.
So as you can hopefully see, Upstart’s events can be used to fulfill all three common programming patterns, simply by slight variation in the command used to emit them.