- Writing an Upstart job
- Merging a job into the debian package
upstart starts and stops tasks and daemons according to event rules. It has optional integration with udev, dbus, and the file system to add a number of flexible events (e.g. creation and modification of files or addition or removal of devices).
Installation is quite simple. The only package necessary is upstart. The upstart-monitor package, which allows one to view the events that occur on their system (e.g. when they plug in their camera or printer), is very useful. The Upstart package currently conflicts with sysvinit, because it replaces /sbin/init (among other things).
There are very few Upstart jobs available in Debian at the moment, however pulling them from Ubuntu packages is quite simple. Additionally, I have a git repo with many jobs written for use on Debian systems here.
Use alongside systemd
If you would like to keep systemd installed, but use Upstart as the system init, make sure your GRUB kernel command line is free of any init= parameters, or init= points to /sbin/init. Also uninstall systemd-sysv. If you would like to use systemd as the system init, but keep Upstart installed and use it on occasion, edit your GRUB command line to have init=/lib/systemd/systemd.
Writing an Upstart job
First, check to see if there is one in the Ubuntu package or in the aforementioned repository of jobs. If there is, try to use that one first and modify it according to your needs. If you hit any issues, notify the source of the job what your problem and solution (if applicable) is.
If there is not one available, writing one is a three step endeavor:
- Identify the start on and stop on conditions.
- Determine the number of forks (if any).
- Add prep and post work to the job (e.g. creating the run directory).
If you would prefer (and I would recommend), please file an enhancement bug here and I can work with you to develop the Upstart job.
Identifying start/stop events
Most jobs will have a corresponding init script or systemd service file to reference. If so, then components from that file can be used to determine the job's start on. Non-Upstart well known events/dependencies will be italicized in this section, while Upstart's well known events will be bolded. Before continuing, please read the Upstart cookbook's information on event types as well as the upstart-events(7) page.
systemd services which do not have a DefaultDependencies=no line need the filesystem event in their start on, or a start on event that provides for the filesystem event (e.g. started dbus).
$remote_fs should convert to filesystem, not remote-filesystems. Unlike with sysvinit, mountall (a component Upstart systems use to mount the filesystems at boot) will emit the remote-filesystems event before emitting local-filesystems. $remote_fs is used by init scripts to depend on both remote and local filesystems, and the filesystem event signals that both of those have been satisfied. If the init script has a $local_fs in addition to a $remote_fs, filesystem is still the correct event.
init scripts with a $local_fs dependency or systemd services with a local-fs.target dependency, but no $remote_fs dependency are most likely network related services, which are used to mount the remote filesystems. The Upstart job should start on local-filesystems to avoid a dependency loop between remote filesystems being mounted and the jobs that allow the mounting of remote filesystems being started.
If the service needs to access /usr, /var, or any other filesystem that can feasibly be separately mounted, then that service should use filesystem. If the only filesystem that the service needs is a virtual filesystem (/sys, /run, etc.) then the virtual-filesystems event is necessary.
$network or network.target should be converted to the static-network-up event, or net-device-up INTERFACE=lo in some circumstances. This is only for the start on. The stop on is different.
If the init script has a stop dependency on $network, then it should stop on deconfiguring-networking. Daemons such as Transmission need this so that they can notify peers or clients of information on quit. Sometimes you can omit this if the init script does not have a stop dependency on $network, however that is on a per-service basis and usually not the case.
systemd hard codes a special case into the stanza After=network.target. The results are that any service with an After=network.target line is stopped before the network goes down as well as started after it is up. If you are converting a systemd service like this to an Upstart job, deconfiguring-networking should be used as a stop on event.
Note that stopping networking is not a replacement for deconfiguring-networking, because the latter is emitted by more than just the networking job, and is therefore more complete in preventing the downing of the network before the job in question is stopped.
Other job dependencies
If the init script has a start dependency on another service, then the job being written needs a start on started $JOB, where JOB is the service that needs to start first.
If the job needs to stop before another job has stopped (e.g. network-manager needs to stop before dbus has stopped), then a stop on stopping $JOB is necessary.
X-Start-Before and Before= sections should be converted to a starting $JOB event in the start on. Before=network-manager.service in a systemd unit should be converted to start on starting network-manager.
D-Bus and Socket Activation
Upstart only has single stream socket activation, and no activation for datagram sockets, sequential packet sockets, fifos, POSIX message queues, or dbus interfaces. It does have IPv6 support, though.
No apparent stop on event
If the job does not have any of the above stop events, it should use runlevel , which will stop the job when the system is shutdown, rebooted, or switched to runlevel 1.
Determining fork count
Before continuing, it is advisable to read the Upstart cookbook's documentation on the expect stanza here Three methods, in order of decreasing preference, to determine the correct expect stanza are:
- Using strace
- Reading the source code
- Guess and check
This method is mentioned in the Upstart cookbook, but I will add a few notes about it here, because it can be quite a hassle to get correct.
Single process daemons
The directions in the Upstart cookbook suffice for this situation.
First, stop any running instances of the daemon. Then, start the daemon with a command similar to the following:
sudo strace -o /tmp/strace.log -fFv /usr/sbin/daemon --arg foo --arg2 bar --arg3 &
where everything after -fFv is the command one would use to start the daemon (make sure it includes any --daemon args, if applicable).
Next, use this command to identify the main daemon process, where daemon is the daemon in question:
- pgrep daemon
The lowest PID should be the main process. Check a PID file, if one exists, if you are unsure of the main process.
This final command will identify all returned PIDs of clone calls (which includes forks):
- sudo grep -m 2 "clone" /tmp/strace.log
Match the main PID determined above to the return of the clone calls. If the main PID is returned from the first clone call, use expect fork. If the main PID is returned from the second clone call, use expect daemon. If the main PID does not match any of the printed PIDs, then the process forks more than twice. The daemon (or possibly the daemon starter, if applicable) will need to be modified. You may file an enhancement bug here if you would like help. For a quick fix, try using any --foreground or --nofork options and no expect stanza.
Reading the source code
This is pretty self explanatory. One thing to note, make sure you read both the actual daemon or any "daemonctl" components (for example, memcached forks once, but its starter script, in /usr/share/memcached/scripts/start-memcached, also forks twice, which causes issues for Upstart).
Guess and check
Use expect daemon first. If the initctl start command hangs, type control-c, kill the process given by initctl status $daemon, edit the job to use expect fork, and try to start the process again. If neither works, you are out of luck.
Adding preparatory and post work to the job
Use Upstart stanzas like limit, nice, and umask, or the pre-start, post-start, pre-stop, and post-stop sections to add anything that the job needs or should have for its startup. Commonly, the daemon will need it run directory created for it and possibly chown'ed to the user that the daemon runs as. Referring to the init script or systemd service is usually useful here, however not everything from the init script may need to be provided for in the Upstart job.
Merging a job into the debian package
Any jobs should go by the same identifier as the init script or systemd service, with simply the suffic changed from .service or .init to .upstart (in the debian directory of the source tree).
Please add the usertag upstart-job with the user email@example.com to keep these bugs trackable.