Upstart helpers (abstract jobs and event aliases) for Oneiric

James Hunt james.hunt at ubuntu.com
Tue Jun 7 14:00:03 UTC 2011


Hi All,

This mail turned into rather an epic in the writing. The summary is I'm
interested in thoughts on the two implementation options for "Proposal 2".

Regards,

James.


= Overview =

For a while Clint and I have thought it would make sense to introduce
some "helper" events and jobs. The idea here being to:

- Simplify existing .conf files.
- Make it easier / speed up writing new .conf files.
- Aid in understanding .conf files.

The idea was discussed at UDS-O ([1]+[2]) and there were no fundamental issues
raised so the plan is to introduce them for Oneiric.

This mail explains the problem we're trying to address along with 2
possible implementations.

Let's start by describing the problem...

= Problem Examples =

== Example 1: plymouth.conf ==

The first example is taken from /etc/init/plymouth.conf:

  start on (starting mountall
            or (runlevel [016]
                and (stopped gdm
                     or stopped kdm
                     or stopped xdm
                     or stopped lxdm
                     or stopped lightdm
                     or stopped uxlaunch)))

=== Observations ===

1) Overly complex "start on" condition.
   - Indicative of refactoring requirement.
     (since a rule of thumb is that most jobs should only
      require ~3-4 events).
   - Difficult to understand.
   - Difficult to modify without breaking behaviour.

2) Cryptic runlevel condition.

3) Hard-coded list of Display Managers.

   Bad since:

   - Results in inability to handle new Display Managers efficiently.
   - Promotes "cut-and-paste hell" (where the same bugs are pasted into
     other .conf files and the problem is propagated, thus making it
     more time-consuming to fix).

== Example 2: gdm.conf ==

Here is the upcoming /etc/init/gdm.conf:

  start on ((filesystem
              and (runlevel [!06]
              and (started dbus
              and (drm-device-added card0 PRIMARY_DEVICE_FOR_DISPLAY=1
              or  (graphics-device-added PRIMARY_DEVICE_FOR_DISPLAY=1
              or   stopped udevtrigger)))))
              or  runlevel PREVLEVEL=S)

=== Observations ===

1) WT^H^H Errr... what?

   You may be forgiven for dropping your Rich Tea biscuit into your cuppa
   on seeing this for the first time (YMMV of course :)

2) Why are there two lines which appear to represent the same thing
   (graphics device)?

3) Thought: surely there must be a simpler way to represent this condition?

4) Fragile.

= The Problem(s) defined =

- Too many job configuration files hard-code required *applications* for
  which there are multiple alternatives.

  What if you don't have that application installed? The Ubuntu archive
  is huge and there are often many alternatives for system components.
  So, by hard-coding applications, you risk breaking users who deviate
  from the defaults, resulting potentially in a bad experience.

  A level of abstraction would protect us from this problem.

  Job Configuration Files should instead specify the *services* they
  require.

- Quite a few core Job Configuration Files have overly complex
  conditions which can be abstracted.

- Cut-and-pasting of complex conditions has led to problems.
  We need to provide a way to avoid this sort of problem proliferating.

= Proposals =

== Proposal 1: Provide Event Aliases for Common Scenarios ==

Create event aliases as shown below:

|----------------+------------------|
| Existing Event | Event Alias      |
|----------------+------------------|
| runlevel 0     | halt             |
| runlevel 1     | single-user-mode |
| runlevel S     | single-user-mode |
| runlevel 2     | multi-user-mode  |
| runlevel 6     | reboot           |
| runlevel [016] | shutdown         |
|----------------+------------------|

== Proposal 2: Provide Abstract Jobs for Common Services ==

|-----------------+-------------------------------------------------------------------|
| Abstract Job    | Description                                                       |
|-----------------+-------------------------------------------------------------------|
| display-manager | gdm, kdm, lightm, etc.                                            |
| network-manager | NetworkManager, wicd, connman, etc                                |
| firewall        | ufw alias.                                                        |
| network         | started when *all* configured network interfaces and bridges "up" |
| graphics-card   | starts when first graphics card added to system.                  |
|-----------------+-------------------------------------------------------------------|

=== Implementation ===

There are two simple methods here we're considering.

==== Option 1 ====

Update every package that provides a service such that its Job
Configuration File sets and exports a "well-known" variable. The
proposed list of environment variables which represent these services
is:

  DISPLAY_MANAGER
  FIREWALL
  GRAPHICS_CARD
  NETWORK
  NETWORK_MANAGER

For example, each display manager package would be updated such that its
.conf file specified:

  env    DISPLAY_MANAGER=y
  export DISPLAY_MANAGER

Then, any job that requires a display manager could say:

  start on starting DISPLAY_MANAGER=y

===== Observations =====

- This might *appear* to be inefficient in that Upstart must check every
  time *any* job starts to determine if DISPLAY_MANAGER=y is set in that
  jobs environment. However, there is no additional overhead beyond how
  Upstart currently works. Consider that...

    start on starting gdm

  ... is actually an alias for:

    start on stating JOB=gdm

- This would require *every* package that provides a service to be
  updated. Admittedly this would only need to be done once though.

- Might be confusing since users you *must* specify "=y" exactly
  (dropping the "=y" won't work, and nor will specifying "=Y" (or "=1")).

==== Option 2 ====

Create a Job Configuration File that hard-codes the list of known
service providers. For example for "display-manger", we could have:

  start on (starting gdm
        or (starting kdm
        or (starting lightdm
        or (starting lxdm
        or (starting slim
        or (starting wdm
        or  starting xdm))))))

  stop on (stopping gdm
        or (stopping kdm
        or (stopping lightdm
        or (stopping lxdm
        or (stopping slim
        or (stopping wdm
        or  stopping xdm))))))

  env    ABSTRACT_JOB=y
  export ABSTRACT_JOB

===== Observations =====

- Since this is an Abstract Job, it will have no PID, but this job will
  "run" for the duration of the first display manager to be invoked.

- We appear to have simply "moved the problem" since although we are no
  longer hard-coding the list of display managers in "plymouth.conf", we
  are hard-coding the list now in "display-manager.conf". However, we
  have gained by doing this since:

  - We have still created the level of abstraction desired.
  - We have contained the problem into a single file: we define the list
    of display managers *once*.
  - We don't need to update every Ubuntu (and potentially every Debian)
    package as would be required for "Option 1".
  - We *could* conceivably auto-generate "display-manager.conf" from the
    archive with a simple script which munged the output of:

    apt-cache search x-display-manager|awk '{print $1}'|sort

- The "ABSTRACT_JOB" variable would allow other jobs to detect that a
  job was abstract (they shouldn't need to care, but just in case...)
  This also avoids the unwieldliness of "abstract-display-manager" (or
  even "abstract/display-manager" (were we to put the abstract jobs in
  /etc/init/abstract/ say).

==== Personal Preference ====

Although it looks rather ugly, my preference is currently for Option 2
primarily since it would be quick and easy to modify the single source
for each service. However, I could be persuaded otherwise :)

= Rationale =

By introducing Abstract Jobs and Event Alias "helpers", we can simplify
the existing Job Configuration Files and make them more understandable.
For example, the "start on" condition for "plymouth.conf" could become:

  start on (starting mountall
        or (shutdown and stopped display-manager))

Similarly, "gdm.conf" could become the much simpler / easier to comprehend:

  start on (filesystem and (started dbus and graphics-device-available)
  stop on shutdown

These changes may also incidentally minimise changes required by Ubuntu
derivatives once the helpers become pervasive.

= Documentation =

We will of course ensure that all these helpers are documented appropriately and we plan to make
maximum use of them to ease the work required for [2].

-----

[1] - https://blueprints.launchpad.net/ubuntu/+spec/foundations-o-upstart-convert-main-initd-to-jobs
[2] - http://summit.ubuntu.com/uds-o/meeting/foundations-o-upstart-convert-main-initd-to-jobs/


--
James Hunt



More information about the ubuntu-devel mailing list