3

I just can't understand how Upstart's rc job definition in Natty 11.04 works. To illustrate the problem, here is the definition (empty lines and comments are left out):

start on runlevel [0123456]
stop on runlevel [!$RUNLEVEL]
export RUNLEVEL
export PREVLEVEL
console output
env INIT_VERBOSE
task
exec /etc/init.d/rc $RUNLEVEL

Let's suppose we currently are in runlevel 2 and the rc job is stopped (that is exactly the situation after booting my box and logging in via SSH). Now, let's assume that the system switches to runlevel 3, for example due to a command like "telinit 3" given by root. What will happen to the rc job?

Obviously, the rc job will be started since it is currently stopped and the event runlevel 3 is matching the start events. But from now on, things are unclear to me: According to the manual $RUNLEVEL evaluates to the new runlevel when the job is started (that means 3 in our example).

Therefore, the next stanza "stop on runlevel [!$RUNLEVEL]" translates to "stop on runlevel [!3]"; that means we have a first stanza which will trigger the job, but the second stanza will never stop the job and seems to be useless.

Since I know that the Ubuntu / Upstart people won't do useless things, I must be heavily misunderstanding something. I would be grateful for any explanation.

While trying to understand this, an additional question came to my mind. If I had contradicting start and stop triggers, for example

start on foo
stop on foo

what would happen? I swear I never will do that, but I am nevertheless very interested in how Upstart handles that on the theoretical level.

Thank you very much!


Editing the question as a reaction on geekosaur's first answer:

I can see the parallelism, but it is not that easy (at least, not to me).

Let's assume the job aurrently is still running, and a new runlevel event comes in (of course, the new runlevel is different from the current one). Then, the following should happen:

1) The job is single instance. That means that "start on ..." won't be triggered since the job is currently running; $RUNLEVEL is not touched.

2) "stop on ..." will be triggered since the new runlevel is different from $RUNLEVEL, so the job will be aborted.

3) Now, the job is stopped and waiting. I can't see how it is restarted with the new runlevel. AFAIK, initctl emits events only once, so "start on ..." won't be triggered and the new runlevel won't be entered.

I know that I still misunderstanding something, and I am grateful for explanations.

Thank you very much!

1
  • I'd suggest that the first part be dropped since it is completely duplicated in askubuntu.com/questions/30691/… , while the second part is a great question on its own.
    – SpamapS
    Mar 29, 2011 at 17:06

4 Answers 4

3

The order of operations is very carefully maintained in upstart when processing events. Stops caused by an event are always done before starts. While linux is multi-process, upstart's event engine is not.

So, the runlevel command, which emits the 'runlevel' event, is received by the upstart state engine, and then processed by first doing all transitions from start to stop completely. This will block until the first rc is killed and dead. Then the transitions from stop to start are done, and the new rc job is started.

This is actually documented in the upstart source code:

    /* We stop first so that if an event is listed both as a
     * stop and start event, it causes an active running process
     * to be killed, the stop script then the start script to be
     * run.  In any other state, it has no special effect.
     *
     * (The other way around would be just strange, it'd cause
     * a process's start and stop scripts to be run without the
     * actual process).
     */

This probably belongs in the upstart cookbook, so I've opened a wishlist bug against it here:

https://bugs.launchpad.net/upstart-cookbook/+bug/745096

3

We have now documented (hopefully) clearly how the "rc" job works in Ubuntu in the Upstart Cookbook. It even has its own section:

http://upstart.ubuntu.com/cookbook/#the-rc-job

However, for the full context, I suggest you read from here:

http://upstart.ubuntu.com/cookbook/#really-understanding-start-on-and-stop-on

3

The remainder of your question should now be answered by the following sections in the Upstart Cookbook:

1

Did you see my answer to Sense of "stop on..." stanza when job is a task? Same answer applies to the first part of your question, it's used to abort the runlevel startup if the user switches to another runlevel before it's finished.

I have no idea about the second part, though.

6
  • I have written the second question at the same time as you have written the answer to the first one... I see the parallelism, but it is not that easy: The stop stanza is only triggered if runlevel is not equal to $RUNLEVEL. That means that the job always has the same $RUNLEVEL during it's runtime. Thus, I can see how the job is stopped / aborted, but not how it is restarted with the new runlevel. AFAIK, initctl emits events exactly once. The runlevel switch event would be consumed for stopping the old job, and the new job would not be started because the event won't be emitted again?
    – Binarus
    Mar 16, 2011 at 23:19
  • It is only emitted once, but multiple jobs can be waiting for its emission and the same job can trigger multiple times via multiple start on/stop on stanzas or comma-separated clauses in a single stanza. That one would trigger twice: once in start on to start the new instance, then again for the stop on to kill the old. If you read the details in init(5) and upstart(8) carefully, you'll note that upstart knows to match the stop on to the existing job because $RUNLEVEL is exported to upstart as a job parameter.
    – geekosaur
    Mar 16, 2011 at 23:26
  • 1
    To clarify a little more (was a bit close to the comment length limit), because $RUNLEVEL is exported, upstart remembers the $RUNLEVEL associated with each start of the rc job. When the stop on is triggered, upstart looks for instances of rc which have a $RUNLEVEL that matches the pattern [!$RUNLEVEL] and stops only those. The new instance obviously doesn't match [!$RUNLEVEL] because it is $RUNLEVEL, so is left alone. (Er, still a bit confusing. The pattern it uses for matching is the current $RUNLEVEL, being matched against the saved $RUNLEVEL for each job.)
    – geekosaur
    Mar 16, 2011 at 23:34
  • Hmmm, more of parallel posting :-) OK then, I could not find an instance stanza in the job's definition, so I thought the rc job is single instance. But according to what you say, rc must be multi-instance. Now I am worried...
    – Binarus
    Mar 16, 2011 at 23:40
  • As I understand it, there are two kinds of "instances". One is having the same job name but different export variable values; the other is having them the same, but having different instance names to distinguish them. So there can only be one instance of a "fully qualified job name" at a time, and export variables and instances are two ways of changing components of those "fully qualified" names. That said, it might be that upstart is smart enough to trigger the stop on first, so that when the start on is evaluated there won't be any other rc jobs active.
    – geekosaur
    Mar 16, 2011 at 23:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .