sections in this module City College of San Francisco - CS260A
Unix/Linux System Administration

Module: StartupShutdown I
module list

upstart

upstart is replaced in RedHat7 with systemd. upstart is quite confusing, and since it's tenure is limited, read this section lightly. For those of you who use Ubuntu, however, upstart is currently heavily used there. Even this will be changing - Ubuntu, upstart's maintainer, will switch to systemd with Ubuntu 14.10, due to come out in October 2014.

You will not be tested on upstart, nor will it be covered in class.

upstart is the replacement for System5 init that is installed on Redhat 6. We have already discussed System5 init and how it uses the rc scripts and subsystems to initialize the system. There are several shortcomings with this scheme that upstart addresses:

upstart deals with these issues by emitting and responding to events. These events are configured to control jobs it manages. Each event has a name. Some also have parameters.

We will categorize events as three types for our discussion

The emission of any event may trigger actions in other jobs if they are so configured. If multiple jobs are configured to start on an event, upstart is free to start the jobs in any order, possibly in parallel.

Job definitions include the events they respond to such as which events start or stop the job and the code executed at different stages of the job's life-cycle.

A detailed discussion of upstart and how it works is beyond this course. We will, however, introduce the startup event and a few job-specific events and see how upstart manages jobs in response to these events. We will also see how jobs can enforce dependencies using these events.

The startup event

As indicated previously, the startup event is issued by upstart when it is ready to start jobs. It then starts all jobs (see below) that are configured to start on the startup event. These jobs tend to be the master jobs that are responsible for getting the initialization ball rolling. The jobs started due to the startup event [tell upstart to] emit events that are used to start other jobs.

upstart jobs

upstart jobs are kept in the /etc/init directory. The filename indicates the name of the job, with the extension .conf They are not shell scripts, so they do not have to be executable.

Each job contains

Dry run

Here is the sequence that occurs when upstart starts job Y:

start on starting X

upstart will wait until Y is stopped (i.e., the job cycle for Y is complete) before running X's main code. (Without the task directive, X's main code would be run as soon as Y is started.) You would think that the presence of task would have an effect on the timing of Y's started event as well, but it doesn't. The job cycle of Y thus continues to the post-start code (below)

task is thus used to insert Y before X by starting [task] Y on X's starting event. To accomplish the same ordering of Y before X you could alternately not start X until Y is finished by starting X on Y's stopped event.
If the pre-start or post-start code fails, the job goal changes to stop.

Here is the sequence that occurs when upstart stops job Y:

The runlevel event

The runlevel event is used to control legacy System5 subsystems, in effect, simulating runlevels. When telinit is invoked to change the runlevel, it emits the runlevel event and records the current and previous runlevel in the login accounting log /var/log/wtmp so that it is available to runlevel and who -r

You should only change the runlevel by invoking telinit. Although initctl could be used to emit the runlevel event, doing this will not record the runlevel correctly in /var/log/wtmp. This will cause confusion in the rc scripts and inconsistency in the output of runlevel and who -r

A "simple" example

This is a lot to soak in and I know it doesn't make much sense. So let's look at a contrived simple example. Here is our job (the contents of the file /etc/init/xxx.conf, which I created)

start on foobar
pre-start exec echo "xxx pre-start" >> /tmp/xxx.out
post-start exec echo "xxx post-start" >> /tmp/xxx.out
pre-stop exec echo "xxx pre-stop" >> /tmp/xxx.out
post-stop exec echo "xxx post-stop" >> /tmp/xxx.out

script
echo "xxx main code started" >> /tmp/xxx.out
sleep 10
echo "xxx main code finished" >> /tmp/xxx.out
end script

As you can see, we have defined our own event, foobar. (This makes our script only run when we say so.) When we emit the event using upstart:

# initctl emit foobar

initctl returns immediately. If we examine the file /tmp/xxx.out we find

# cat /tmp/xxx.out
xxx pre-start
xxx main code started
xxx post-start

10 seconds or so later:

# cat /tmp/xxx.out
xxx pre-start
xxx main code started
xxx post-start
xxx main code finished
xxx post-stop

Let's add a second job, /etc/init/yyy.conf:

# cat yyy.conf
start on starting xxx
script
sleep 1
echo "yyy ran" >> /tmp/xxx.out
end script

Our new job, yyy, starts when upstart emits the starting event for xxx. Now when we emit foobar (and wait 11 seconds) (although initctl still returns immediately)

# initctl emit foobar

# cat /tmp/xxx.out
xxx pre-start
xxx main code started
xxx post-start
yyy ran
xxx main code finished
xxx post-stop

It is not obvious that xxx's main code and yyy's code ran in parallel. You can see this, however, if we add the task directive to yyy, forcing yyy to run to completion before xxx is allowed to continue.

# cat yyy.conf
start on starting xxx
task
script
sleep 1
echo "yyy ran" >> /tmp/xxx.out
end script

And, again, emit foobar and wait 11 seconds:

# cat /tmp/xxx.out
yyy ran  # <-- note: this was run due to the starting event emitted for xxx
xxx pre-start
xxx main code started
xxx post-start
xxx main code finished
xxx post-stop

You can see that yyy was inserted and ran to completion before xxx's main (or pre-start) code was run.

Notice that there is no pre-stop output from xxx, since xxx was not explicitly stopped. If we emit foobar and immediately issue a stop directive for xxx (before the 11 seconds have elapsed):

# initctl emit foobar
# initctl stop xxx
xxx stop/waiting
# cat /tmp/xxx.out
yyy ran
xxx pre-start
xxx main code started
xxx post-start
xxx pre-stop
xxx post-stop

We can now see how upstart jobs use of events can impose order without having to rely on some ad-hoc filename order, as are used in subsystems. This allows jobs that can be run in parallel, to be run in parallel. Anyone who has compared the speed of booting Redhat systems pre- and post- version 6 sees some difference. And in Ubuntu, which has moved a considerable number of subsystems to upstart jobs, booting is very fast indeed.

A few real jobs

At the time of this writing, our systems had very little placed under the control of upstart, but there are two sample jobs that perform a function we are acquainted with:

The first is the job rcS.conf.

It is one of the few jobs under control of upstart that responds to the startup event. The line that configures this is

start on startup

This says that rcS.conf will be started (the job goal event 'start' will be processed) by upstart when it initializes the system. When the job is started, upstart emits the starting event. Since there is no pre-start code, it runs the exec or script section, which in this case is

exec /etc/rc.d/rc.sysinit

This, of course, is our system initialization script. The rcS.conf file contains a task directive, so upstart will not continue with the initialization sequence until rc.sysinit is stopped. When rc.sysinit exits, upstart stops the job, (changing the job goal to stop), eventually emitting the stopped event for rcS and allowing upstart to continue with the initialization. rcS.conf also has a script to run when it is stopped, which says

if I was started by upstart with the startup event

if I was just booted with instructions to go to single-user mode, use S as the runlevel
else
extract the runlevel from /etc/inittab

run telinit with the new runlevel.

telinit then has upstart emit the runlevel event.

The second job uses the runlevel event. It is the job rc.conf. Here is a copy of that job so we can analyze it:

start on runlevel [0123456]

stop on runlevel [!$RUNLEVEL]

task

export RUNLEVEL
console output
exec /etc/rc.d/rc $RUNLEVEL

This job starts on a runlevel event. When started, it simply starts the rc script, giving it the new runlevel.

The stop on directive is curious. It is there to cause upstart to stop rc if it is currently running when the runlevel changes again! ($RUNLEVEL contains the current runlevel. The argument to the runlevel event is the new runlevel.)

We will investigate a few more jobs in the Exercises. You should peruse the /etc/init directory and see if you can figure out how they interact. The important jobs are rc, rcS, start-ttys, tty, init-system-dbus and prefdm.

A little more verbosity

Here is some log information output by upstart showing how the job rc progresses through the various states in upstart. I have annotated it to show where the sections of code in the job are executed and when the actual events are emitted:

 event_new: Pending runlevel event
 Handling runlevel event
 job_register: Registered instance /com/ubuntu/Upstart/jobs/rc/_
 event_pending_handle_jobs: New instance rc
 rc goal changed from stop to start             # here the goal is start
 rc state changed from waiting to starting      # first event 'starting'
 rc state changed from starting to pre-start    # the pre-start code is run
 rc state changed from pre-start to spawned
 rc main process (2707)                         # here's the main code
 rc state changed from spawned to post-start    # next, post-start code is run
 rc state changed from post-start to running    # started event emitted.
 rc main process (2707) exited normally
 rc goal changed from start to stop             # job goal changed to stop
 rc state changed from running to stopping      # first stop event: 'stopping'
 rc state changed from stopping to killed       #
 rc state changed from killed to post-stop      # post-stop code is run
 rc state changed from post-stop to waiting
 event_new: Pending stopped event               # finally, stopped event
 job_change_state: Destroyed inactive instance rc

Controlling jobs

As we have seen, you can control jobs yourself by using initctl. There are a lot of commands, but here are the introductory ones:

the first batch of initctl commands are similar to those of service (except the arguments are annoyingly reversed):

initctl start job

initctl stop job

initctl status job

initctl restart job

the next batch are a bit different:

initctl emit event

initctl reload job - send the job a HUP signal

initctl list - list all jobs and their status

The X window manager

The prefdm.conf initctl job on Redhat controls the X window manager. If you need to stop or restart it you should use the appropriate initctl command. Remember, this will kill all GUI applications and logout all users logged in under the GUI. It will return to a new login screen. If someone leaves the console logged in, however, this will log them (and anyone else who is logged in under the GUI) out.

Users of previous Redhat and other linux systems may recall being able to use the keystroke control-alt-backspace (CNTL-ALT-BS) to kill the window manager. This is still available as an option, but it is configurable on a user-by-user basis and it is somewhat difficult to find: it is in System->Preferences->Keyboard->Layouts->Layout Options

Since this is configurable on a user-by-user basis, it will only be available if the currently logged-in user so configures it! This makes it virtually unusable as a forced-logout technique for a user who leaves themself logged in. The only real solution is:

Note that you will be switched back to the GUI console, but your virtual console will still be logged in as root! Make sure you switch back to the virtual console and log yourself out. To avoid this, you may want to add ;exit to the end of your initctl command: 

Preview question: Suppose you wanted to add your own sequence of code to be executed when the system boots. Can you think of ways to configure this? 

NOTE: the Next link in this section proceeds to the next module, StartupShutdown Part 2

Prev This page was made entirely with free software on linux:  
the Mozilla Project
and Openoffice.org      
Next

Copyright 2014 Greg Boyd - All Rights Reserved.