Merge lp:~jamesodhunt/upstart/serialise-remaining-objects into lp:upstart

Proposed by James Hunt
Status: Merged
Merged at revision: 1479
Proposed branch: lp:~jamesodhunt/upstart/serialise-remaining-objects
Merge into: lp:upstart
Diff against target: 58491 lines (+56824/-375)
26 files modified
ChangeLog (+274/-0)
init/Makefile.am (+1/-1)
init/conf.c (+651/-11)
init/conf.h (+57/-1)
init/control.h (+25/-25)
init/event.c (+14/-4)
init/event_operator.c (+311/-3)
init/event_operator.h (+26/-1)
init/job.c (+56/-46)
init/job.h (+2/-2)
init/job_class.c (+288/-131)
init/job_class.h (+9/-3)
init/main.c (+4/-0)
init/paths.h (+9/-0)
init/state.c (+87/-30)
init/state.h (+5/-8)
init/tests/data/upstart-1.8+apparmor.json (+7962/-0)
init/tests/data/upstart-1.8+full_serialisation+apparmor.json (+18886/-0)
init/tests/data/upstart-1.8+full_serialisation-apparmor.json (+18201/-0)
init/tests/data/upstart-1.8.json (+8651/-0)
init/tests/test_conf.c (+306/-3)
init/tests/test_event_operator.c (+25/-5)
init/tests/test_job.c (+31/-10)
init/tests/test_state.c (+845/-90)
init/tests/test_util.c (+85/-0)
init/tests/test_util.h (+13/-1)
To merge this branch: bzr merge lp:~jamesodhunt/upstart/serialise-remaining-objects
Reviewer Review Type Date Requested Status
James Hunt Needs Resubmitting
Steve Langasek Needs Fixing
Review via email: mp+163151@code.launchpad.net

Description of the change

This monster MP includes fixes for two bugs:

= bug 1124384 =

This bug has seemingly existed for a long time and only came to light when users of cloud-init attempted to overcome the inotify limitations of overlayfs (see bug 882147) by invoking 'initctl reload-configuration' in early boot to make Upstart consider newly-created jobs.

The issue has nothing to do with early boot though - potentially, whenever 'initctl reload-configuration' gets called and events are "in flight", the bug could be seen. However, since most filesystems have sane inotify semantics (thus negating the need for ever calling 'initctl reload-configuration), it is unsurprising this problem has only come to light recently.

The fix involved deferring the destruction of the original ConfFile object coupled with code to allow the newly-created (post-reload) JobClass to reference any relevant events before the original ConfFile and JobClass are destroyed. This allows the event reference count to stay positive across a reload.

= bug 1103881 =

This is an issue with stateful re-exec: the original approach was to serialise the start/stop on conditions as a logically identical representation of the original string from the .conf file. However, this meant that required EventOperator data was not available on deserialisation. This resulted in the possibility that events would be destroyed too early since their reference counts could drop to zero as the EventOperator references to those evens would not be re-instated post-re-exec.

The new approach is to:

1) Serialise the start/stop on conditions as full EventOperator trees.
2) Serialise all objects, regardless of whether they are currently being referenced by running jobs.

New objects being serialised are:

- ConfSource
- ConfFile
- EventOperator

Since all objects are now being serialised, chroot sessions can now be supported. These changes would also support upstart-in-initramfs (although further work is required for that - see init/state.h).

To post a comment you must log in.
1481. By James Hunt

* Merge of lp:upstart.

1482. By James Hunt

Revert to not supporting deserialisation of JobClasses with associated
user/chroot sessions to avoid behavioural change for now.

* init/job_class.c:
  - job_class_deserialise(): Revert to failing if associated session is
    non-NULL.
  - job_class_deserialise_all(): Revert to ignoring failure to
    deserialise a JobClass iff it has a non-NULL associated session.
* init/state.c:
  - state_to_string(): Provide some diagnostics if serialisation fails.
  - state_from_string(): Provide some diagnostics if deserialisation fails.
  - state_deserialise_resolve_deps(): Ignore failure to lookup JobClass
    iff it has an associated user/chroot session.
  - state_deserialise_blocking(): Revert to ignoring failure to
    deserialise a Blocked object that is associated with a Job whose
    JobClass has a non-NULL session.
* init/tests/test_state.c: test_blocking(): Revert test to assert that
  blocked job with non-NULL session is ignored.

1483. By James Hunt

* init/state.c: stateful_reexec(): Specify all values for array
  initialiser.

Revision history for this message
Stéphane Graber (stgraber) wrote :

Something looks wrong with this branch, commit 1481 should probably have been a rebase instead of merging trunk into a branch that you then want to merge into trunk. BZR won't fail to merge this but the history will look a bit weird.

You appear to be using "iff" in quite a few places (including some other occurrences in current lp:upstart) are those actually meant as "if and only if" or are those typos (or a mix of the two)?

Besides those two small details, nothing jumped at me during a quick read through of the diff. However this is a massive change so I won't pretend to have done a careful review of it.
I mostly rechecked the concept based on the very detailed comments you left in the code and checked that the serialization/deserialization bits make sense and match.

Revision history for this message
Steve Langasek (vorlon) wrote :

After the merge of lp:~mdeslaur/upstart/apparmor-support, this no longer merges cleanly into trunk; and the conflicts in init/tests/test_state.c are non-trivial. James, can you please resolve these conflicts on your branch and resubmit?

review: Needs Fixing
Revision history for this message
Steve Langasek (vorlon) wrote :

On Mon, May 27, 2013 at 06:02:26PM -0000, Stéphane Graber wrote:
> Something looks wrong with this branch, commit 1481 should probably have
> been a rebase instead of merging trunk into a branch that you then want to
> merge into trunk. BZR won't fail to merge this but the history will look
> a bit weird.

No, merging trunk to the branch is the expected action here.

1484. By James Hunt

* Sync with lp:upstart.
* init/tests/test_state.c: test_upstart_pre_security_upgrade(): Tweaks
  to work with new TestDataFile structure and requirement that no
  conf_sources exist just prior to deserialisation.

Revision history for this message
James Hunt (jamesodhunt) wrote :

Re-synced with lp:upstart.

review: Needs Resubmitting
Revision history for this message
Steve Langasek (vorlon) wrote :

--- init/event_operator.h 2013-02-27 11:46:04 +0000
+++ init/event_operator.h 2013-05-30 17:07:37 +0000
[...]
+event_operator_deserialise_all (void *parent, json_object *json)
+ __attribute__ ((warn_unused_result));
+
+NIH_END_EXTERN
+
 char *event_operator_collapse (EventOperator *condition)
- __attribute__ ((warn_unused_result));
-
-NIH_END_EXTERN
+ __attribute__ ((warn_unused_result, unused));

This moves the event_operator_collapse() definition outside the extern "C" {} block, which seems like a mistake?

--- init/job_class.c 2013-05-15 13:21:54 +0000
+++ init/job_class.c 2013-05-30 17:07:37 +0000
@@ -1910,17 +2015,17 @@

                json_class = job_class_serialise (class);

- /* No object returned means the class doesn't need to be
- * serialised. Even if this is a real failure, it's always
- * better to serialise as much of the state as possible.
- */
                if (! json_class)
- continue;
+ goto error;

                json_object_array_add (json, json_class);

I think the reasoning in that comment still holds (that it's better to serialize as much of the state as possible). But this is consistent with the behavior in the rest of the code, so I suppose that shouldn't be a blocker.

--- init/state.c 2013-02-28 16:40:36 +0000
+++ init/state.c 2013-05-30 17:07:37 +0000
[...]
+ /* Again, we cannot error here since older JSON state data did
+ * not encode ConfSource or ConfFile objects.
+ */
+ if (conf_source_deserialise_all (json) < 0)
+ nih_warn ("%s", _("No ConfSources present in state data"));

Wouldn't it make sense to have a check on the format version, and make this an error for new formats vs. a warning for the old format?

So given that there are version checks now in the deserializer, and there is another change to the serialization format landing upstream at the same time: how can we backport this change to raring in an SRU? Should we consider the format with apparmor_switch to be version 4, and treat the confsource serialization without apparmor_switch to be version 3, then include tests for each (i.e., change
test_upstart_pre_security_upgrade() to use v3)?

Finally, it appears that there is no test json for the *current* serialization format. This should definitely be included as its own test, and added to the tree now - not be left until the next time someone changes the serialization format (or accidentally changes the deserializer!).

Other than that, this looks very good. If you can fix up the above issues, I'll approve this.

1485. By James Hunt

* init/event.c: event_deserialise(): Revert to checking JSON for
  blockers to avoid reliance on JSON serialisation data format version.
* init/event_operator.h: Fix misplacement of NIH_END_EXTERN.
* init/state.c: Remove serialisation version code since the autoconf
  approach of detecting the format of the JSON is safer.
* init/state.h: Remove STATE_VERSION.

1486. By James Hunt

* init/tests/data/upstart-1.8+apparmor.json: New test data file.
* init/tests/test_state.c:
  - test_upgrade(): Re-initialise lists and hashes as a convenience to
    the tests.
  - test_upstart_with_apparmor_upgrade(): New test to ensure Upstart can
    parse the current 1.8 format JSON with the addition of the AppArmor
    serialisation (but crucially *without* the full serialisation data
    (EventOperator, etc).
  - test_upstart_full_serialise_without_apparmor_upgrade(): New test to
    ensure Upstart can parse the current 1.8 format JSON with the
    addition of the full serialisation data (EventOperator, etc),
    but _without_ the AppArmor serialisation.

Revision history for this message
James Hunt (jamesodhunt) wrote :

The plan now is to remove the STATE_VERSION code and revert to the autoconf approach of detecting the format of the serialisation data by inspection (this also allows us to work around the backport issue without having to invent mythical format version numbers or other such games).

What we do need to do is add explicit tests for the serialisation formats shown below to allow full serialisation to be backported into Ubuntu _without_ the AppArmor branch (since the former is a fix whilst the latter is a new feature):

1) Current serialisation format with the addition of apparmor data, but without the full serialisation provided by this branch.
2) Current serialisation format with the addition of apparmor data, but without the full serialisation provided by this branch.

1487. By James Hunt

* init/tests/data/upstart-1.8+full_serialisation+apparmor.json: New test
  data file.
* init/tests/test_state.c:
  - test_upstart_full_serialise_with_apparmor_upgrade(): New test to ensure
    Upstart can parse the current 1.8 format JSON with the addition of the full
    serialisation data (EventOperator, etc), and the AppArmor serialisation.

1488. By James Hunt

* init/state.c: state_from_string(): Check if ConfSources
  exist in the serialisation data to allow distinction between
  ConfSources not present and failing to deserialise them.

1489. By James Hunt

* Sync with lp:upstart.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2013-05-31 15:57:47 +0000
3+++ ChangeLog 2013-06-05 16:21:24 +0000
4@@ -1,3 +1,38 @@
5+2013-06-05 James Hunt <james.hunt@ubuntu.com>
6+
7+ * init/tests/data/upstart-1.8+apparmor.json: New test data file.
8+ * init/tests/data/upstart-1.8+full_serialisation-apparmor.json:
9+ New test data file.
10+ * init/tests/data/upstart-1.8+full_serialisation+apparmor.json:
11+ New test data file.
12+ * init/tests/test_state.c:
13+ - test_upgrade(): Re-initialise lists and hashes as a convenience to
14+ the tests.
15+ - test_upstart_with_apparmor_upgrade(): New test to ensure Upstart can
16+ parse the current 1.8 format JSON with the addition of the AppArmor
17+ serialisation (but crucially *without* the full serialisation data
18+ (EventOperator, etc).
19+ - test_upstart_full_serialise_without_apparmor_upgrade(): New test to
20+ ensure Upstart can parse the current 1.8 format JSON with the
21+ addition of the full serialisation data (EventOperator, etc),
22+ but _without_ the AppArmor serialisation.
23+ - test_upstart_full_serialise_with_apparmor_upgrade(): New test to
24+ ensure Upstart can parse the current 1.8 format JSON with the
25+ addition of the full serialisation data (EventOperator, etc),
26+ and the AppArmor serialisation.
27+ * init/state.c: state_from_string(): Check if ConfSources
28+ exist in the serialisation data to allow distinction between
29+ ConfSources not present and failing to deserialise them.
30+
31+2013-06-04 James Hunt <james.hunt@ubuntu.com>
32+
33+ * init/event.c: event_deserialise(): Revert to checking JSON for
34+ blockers to avoid reliance on JSON serialisation data format version.
35+ * init/event_operator.h: Fix misplacement of NIH_END_EXTERN.
36+ * init/state.c: Remove serialisation version code since the autoconf
37+ approach of detecting the format of the JSON is safer.
38+ * init/state.h: Remove STATE_VERSION.
39+
40 2013-05-31 James Hunt <james.hunt@ubuntu.com>
41
42 [ Eric S. Raymond <esr@thyrsus.com> ]
43@@ -63,11 +98,250 @@
44 * init/tests/test_state.c: Test apparmor_switch and
45 PROCESS_SECURITY.
46
47+2013-05-13 James Hunt <james.hunt@ubuntu.com>
48+
49+ * init/state.c: stateful_reexec(): Specify all values for array
50+ initialiser.
51+
52+2013-05-10 James Hunt <james.hunt@ubuntu.com>
53+
54+ Revert to not supporting deserialisation of JobClasses with associated
55+ user/chroot sessions to avoid behavioural change for now.
56+
57+ * init/job_class.c:
58+ - job_class_deserialise(): Revert to failing if associated session is
59+ non-NULL.
60+ - job_class_deserialise_all(): Revert to ignoring failure to
61+ deserialise a JobClass iff it has a non-NULL associated session.
62+ * init/state.c:
63+ - state_to_string(): Provide some diagnostics if serialisation fails.
64+ - state_from_string(): Provide some diagnostics if deserialisation fails.
65+ - state_deserialise_resolve_deps(): Ignore failure to lookup JobClass
66+ iff it has an associated user/chroot session.
67+ - state_deserialise_blocking(): Revert to ignoring failure to
68+ deserialise a Blocked object that is associated with a Job whose
69+ JobClass has a non-NULL session.
70+ * init/tests/test_state.c: test_blocking(): Revert test to assert that
71+ blocked job with non-NULL session is ignored.
72+
73+2013-05-09 James Hunt <james.hunt@ubuntu.com>
74+
75+ * init/job.c: job_serialise(): Corrected logic for handling 'stop on'
76+ condition.
77+ * init/job_class.c: job_class_deserialise(): Only check type after JSON
78+ queried (thanks Coverity).
79+
80+2013-05-08 James Hunt <james.hunt@ubuntu.com>
81+
82+ * init/Makefile.am: Link test_util to test_event_operator.
83+ * init/conf.c: conf_source_deserialise_all(): Assert conf_sources is
84+ empty.
85+ * init/control.h: control_get_job(): Use job_class_get_registered()
86+ rather than redundant job_class_find().
87+ * init/event_operator.c:
88+ - Typos and formatting.
89+ - event_operator_serialise(): Conditionally encode name and env.
90+ - event_operator_deserialise(): Treat env as a string array, not an
91+ environ array to ensure values are not discarded unless they contain '='.
92+ * init/job.c:
93+ - job_deserialise(): Check JSON type for 'stop on' prior to attempting value extraction.
94+ - job_find():
95+ - Make job_class const.
96+ - Use job_class_get_registered() rather than job_class_find().
97+ * init/job_class.c:
98+ - job_class_get_registered(): Rework.
99+ - job_class_add_safe(): Re-add since required to support old
100+ serialisation formats that do not encode ConfSource and ConfFile
101+ objects.
102+ - job_class_deserialise():
103+ - Take care to only associate ConfFile with JobClass if JSON encodes
104+ ConfFile objects.
105+ - Check JSON type for 'start/stop on' prior to attempting value extraction.
106+ - job_class_find(): Remove (redundant due to job_class_get_registered()).
107+ * init/state.c:
108+ - Set serialisation_version to an invalid value to detect if it was
109+ never set (to handle old serialisation formats).
110+ - state_from_string():
111+ - Make failure to read header and ConfSources warnings only, to support old
112+ serialisation formats that do not encode this information.
113+ - state_deserialise_blocked(): Fix bug where event blocker was being
114+ incremented twice. Now, just assert it is set.
115+ - state_deserialise_blocking(): Failure to deserialise a Blocked object
116+ is no longer ignored since sessions are supported.
117+ * init/state.h: STATE_VERSION history and notes.
118+ * init/tests/test_conf.c: Replace calls to job_class_find() with
119+ job_class_get_registered().
120+ * init/tests/test_event_operator.c:
121+ - test_operator_serialisation(): Check both the original string
122+ serialisation format and the new full EventOperator serialisation.
123+ type.
124+ * init/tests/test_job.c:
125+ - test_deserialise_ptrace():
126+ - Comments and more checks.
127+ - Create ConfSource, ConfFile and JobClass manually.
128+ - Call job_class_consider() as we now have a backing ConfFile.
129+ - Wait for childs child.
130+ * init/tests/test_state.c:
131+ - Simplify TestDataFile.
132+ - Ensure files passed to conf_file_new() provide expected suffix.
133+ - job_class_diff(): Add event_operator_diff() call.
134+ - conf_source_diff(): New function.
135+ - conf_file_diff(): New function.
136+ - test_blocking():
137+ - Extra checks.
138+ - Expect ConfFiles to be recreated on re-exec.
139+ - test_upgrade(): Check environment is clean before running the next test.
140+ - test_upstart1_8_upgrade(): New Upstart-1.8 serialisation format test.
141+ - conf_source_from_path(): New utility function.
142+ * init/tests/test_util.c:
143+ - event_operator_diff(): New function.
144+ - session_from_chroot(): New function.
145+ * init/tests/data/upstart-1.8.json: New test file.
146+
147+2013-05-08 James Hunt <james.hunt@ubuntu.com>
148+
149+ * init/conf.c:
150+ - conf_source_serialise_all(): Make failures fatal.
151+ * init/event_operator.c: event_operator_collapse(): Unhide but mark as
152+ unused (it's still used by the tests).
153+ * init/job.c:
154+ - job_serialise(): Remove unused variable.
155+ - job_deserialise(): Check the type of the start/stop on condition in
156+ the json, not the json passed to the function.
157+ * init/job_class.c: job_class_deserialise(): Check the type of the
158+ start/stop on condition in the json, not the json passed to the
159+ function.
160+ * init/state.c:
161+ - Comments.
162+ - state_read_header(): Extract header from JSON before checking for
163+ serialisation version.
164+
165 2013-05-07 James Hunt <james.hunt@ubuntu.com>
166
167 * util/man/shutdown.8: Specify default action is to bring system
168 down to single-user mode (LP: #1065851).
169
170+2013-05-02 James Hunt <james.hunt@ubuntu.com>
171+
172+ * init/event.c: event_deserialise(): Only set blockers if EventOperators
173+ are serialised.
174+ * init/event_operator.c: event_operator_collapse(): Now only needed for
175+ DEBUG.
176+ * init/job.c:
177+ - job_deserialise(): Support old string format for 'stop_on'
178+ EventOperator as well as new array format.
179+ * init/job_class.c:
180+ * init/state.c: Create a meta-header containing a serialisation version
181+ number.
182+
183+2013-05-02 James Hunt <james.hunt@ubuntu.com>
184+
185+ * init/conf.c:
186+ - New serialisation and support functions:
187+ - conf_source_serialise()
188+ - conf_source_serialise_all()
189+ - conf_source_deserialise()
190+ - conf_source_deserialise_all()
191+ - conf_source_type_enum_to_str()
192+ - conf_source_type_str_to_enum()
193+ - conf_file_serialise()
194+ - conf_file_deserialise()
195+ - conf_file_deserialise_all()
196+ - conf_source_get_index()
197+ - conf_file_find()
198+ - debug_show_event_operator()
199+ - debug_show_event_operators()
200+ * init/event_operator.c:
201+ - event_deserialise():
202+ - Fix leaked env array.
203+ - Deserialise blockers count now EventOperators are also serialised.
204+ - event_operator_type_enum_to_str()
205+ - event_operator_type_str_to_enum()
206+ - event_operator_serialise()
207+ - event_operator_serialise_all()
208+ - event_operator_deserialise()
209+ - event_operator_deserialise_all()
210+ * init/job_class.c:
211+ - job_class_add_safe(): Remove as no longer required.
212+ - job_class_get(): Must call job_class_init().
213+ - job_class_serialise():
214+ - Remove error case if chroot session specified now that ConfSources
215+ are serialised.
216+ - Serialise start/stop on fully (as EventOperators) rather than just the original
217+ string condition (LP: #1103881).
218+ - job_class_deserialise():
219+ - Remove error case if chroot session specified now that ConfSources
220+ are serialised.
221+ - Associate the JobClass with its ConfFile.
222+ - job_class_serialise_all():
223+ - Comments.
224+ - Failure to serialise a JobClass is now a hard error since we now
225+ serialise all JobClasses.
226+ - job_class_deserialise():
227+ - Deserialise start/stop on conditions as EventOperators rather than as a
228+ string condition.
229+ - Since ConfFiles are now serialised, call job_class_consider()
230+ rather than job_class_add_safe().
231+ - job_class_deserialise_all():
232+ - Failure to deserialise a JobClass is now a hard error since we now
233+ serialise all JobClasses.
234+ - job_class_get_index(): New function.
235+ * init/state.c:
236+ - state_to_string(): Serialise ConfSources and ConfFiles.
237+ - state_from_string(): Deserialise ConfSources and ConfFiles.
238+ - state_deserialise_resolve_deps(): Allow chroot sessions to be
239+ handled now we serialise ConfSources and ConfFiles.
240+
241+2013-04-30 James Hunt <james.hunt@ubuntu.com>
242+
243+ * init/job.c: job_serialise_all(): Really serialise all JobClasses,
244+ * init/main.c: Add 'write-state-file' command-line option.
245+ regardless of whether they have associated Jobs.
246+ * init/paths.h: Added STATE_FILE_ENV.
247+ * init/state.c:
248+ - state_read_objects(): Write state file if 'write-state-file'
249+ specified or STATE_FILE_ENV set.
250+ - event_deserialise():
251+ - Preserve the pre-reexec blockers count until JobClasses
252+ are deserialised and reverse-dependencies resolved.
253+ - Fix incorrect calls for failed and blockers Event attributes.
254+ * init/state.h: Comments.
255+ * init/tests/test_state.c:
256+ - test_job_class_serialise(): Ensure that all JobClasses get
257+ serialised, regardless of whether they have any associated
258+ Jobs.
259+ - test_event_serialise(): New "with failed" test.
260+ * init/event.c: event_deserialise(): Don't deserialise Event->blockers -
261+ there is no point until we serialise EventOperators.
262+
263+2013-04-29 James Hunt <james.hunt@ubuntu.com>
264+
265+ * init/conf.c:
266+ - conf_reload(): Explanation of how reload works.
267+ - conf_reload_path(): Defer destruction of original ConfFile until
268+ newly-loaded .conf file has a chance to reference any events its
269+ 'start on' condition requires (LP: 1124384).
270+ - debug_show_jobs(): Return if no instances.
271+ - debug_show_events(): New function.
272+ * init/event.c: Comments.
273+ * init/job.c: Comment.
274+ * init/job_class.c:
275+ - job_class_get_registered(): New function resulting from refactor of
276+ job_class_consider() and job_class_reconsider().
277+ - job_class_consider():
278+ - Use job_class_get_registered().
279+ - Call job_class_event_block() to avoid premature event destruction.
280+ - job_class_reconsider(): Use job_class_get_registered().
281+ - job_class_event_block(): New function to increase reference count
282+ for events new JobClass specifies in its 'start on' condition that
283+ old JobClass has also referenced (second part of LP: #1124384).
284+ * init/tests/test_conf.c:
285+ - test_source_reload(): New tests:
286+ - "ensure reload does not destroy a blocked event used by another job"
287+ - "ensure .conf reload causes waiting job to run when 'start on' matches"
288+ * init/tests/test_conf.h: TEST_ENSURE_CLEAN_ENV(): Check
289+ nih_child_watches too.
290+
291 2013-04-22 James Hunt <james.hunt@ubuntu.com>
292
293 * Typo and doc changes.
294
295=== modified file 'init/Makefile.am'
296--- init/Makefile.am 2013-05-15 13:21:54 +0000
297+++ init/Makefile.am 2013-06-05 16:21:24 +0000
298@@ -280,7 +280,7 @@
299 $(JSON_LIBS) \
300 -lrt
301
302-test_event_operator_SOURCES = tests/test_event_operator.c
303+test_event_operator_SOURCES = tests/test_event_operator.c $(test_util_SOURCES)
304 test_event_operator_LDADD = \
305 system.o environ.o process.o \
306 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
307
308=== modified file 'init/conf.c'
309--- init/conf.c 2013-02-27 11:46:04 +0000
310+++ init/conf.c 2013-06-05 16:21:24 +0000
311@@ -2,7 +2,7 @@
312 *
313 * conf.c - configuration management
314 *
315- * Copyright © 2009,2010,2011,2012,2013 Canonical Ltd.
316+ * Copyright 2009,2010,2011,2012,2013 Canonical Ltd.
317 * Author: Scott James Remnant <scott@netsplit.com>.
318 *
319 * This program is free software; you can redistribute it and/or modify
320@@ -49,6 +49,7 @@
321 #include "conf.h"
322 #include "errors.h"
323 #include "paths.h"
324+#include "environ.h"
325
326 /* Prototypes for static functions */
327 static int conf_source_reload_file (ConfSource *source)
328@@ -103,6 +104,7 @@
329 **/
330 NihList *conf_sources = NULL;
331
332+extern json_object *json_conf_sources;
333
334 /**
335 * is_conf_file_std:
336@@ -447,12 +449,24 @@
337 /**
338 * conf_reload:
339 *
340- * Reloads all configuration sources.
341+ * Reloads configuration sources.
342 *
343 * Watches on new configuration sources are established so that future
344 * changes will be automatically detected with inotify. Then for both
345 * new and existing sources, the current state is parsed.
346 *
347+ * All ConfFiles are recreated as part of the reload. If the JobClass
348+ * associated with a ConfSource has no Job instances, the JobClass is
349+ * recreated and added to the job_classes hash.
350+ *
351+ * However, if a JobClass has running instances at reload time, although
352+ * a new ConfSource *and* a new JobClass are created, the new JobClass
353+ * (called the "best") will *NOT* be added to the job_classes hash until
354+ * the last running instance has finished. At this point, the "registered"
355+ * JobClass will be removed from the hash (when job_class_reconsider()
356+ * is called by job_change_state()) and replaced by the "best" (newest)
357+ * JobClass.
358+ *
359 * Any errors are logged through the usual mechanism, and not returned,
360 * since some configuration may have been parsed; and it's possible to
361 * parse no configuration without error.
362@@ -1039,6 +1053,7 @@
363 const char *override_path)
364 {
365 ConfFile *file = NULL;
366+ ConfFile *orig = NULL;
367 nih_local char *buf = NULL;
368 nih_local char *name = NULL;
369 size_t len, pos, lineno;
370@@ -1051,25 +1066,67 @@
371 path_to_load = (override_path ? override_path : path);
372
373 /* If there is no corresponding override file, look up the old
374- * conf file in memory, and then free it. In cases of failure,
375+ * conf file in memory, and then free it. In cases of failure,
376 * we discard it anyway, so there's no particular reason
377 * to keep it around anymore.
378 *
379- * Note: if @override_path has been specified, do not
380- * free the file if found, since we want to _update_ the
381- * existing entry.
382+ * Notes:
383+ *
384+ * - If @override_path has been specified, do not free the file
385+ * if found, since we want to _update_ the existing entry.
386+ * - Freeing a ConfFile does _not_ necessarily free its associated
387+ * JobClass.
388 */
389- file = (ConfFile *)nih_hash_lookup (source->files, path);
390- if (! override_path && file)
391- nih_unref (file, source);
392+ orig = (ConfFile *)nih_hash_lookup (source->files, path);
393+ if (! override_path && orig) {
394+ /* Found an existing ConfFile. We will free this, but
395+ * just not yet since iff that ConfFiles associated JobClass
396+ * does not have any running instances, freeing the
397+ * ConfFile will cause the original JobClass associated
398+ * with this ConfFile to be destroyed. But if the JobClass
399+ * had referenced any events via it's 'start on' EventOperator tree,
400+ * the JobClasses destruction could lead to the Events
401+ * being destroyed _before_ the about-to-be-created
402+ * replacement JobClass gets a chance to reference those
403+ * same events (assuming its 'start on' EventOperator tree
404+ * contains nodes specifying the same event names as
405+ * those in the original JobClasses).
406+ *
407+ * As such, we simply remove the ConfFile from its
408+ * parent ConfSources hash, create the new ConfFile and
409+ * JobClass, give the new JobClass a chance to be the
410+ * registered JobClass, and finally allow the original
411+ * ConfFile to be destroyed.
412+ *
413+ * If this is not done, reloading a configuration
414+ * mid-way through the boot sequence could lead to a
415+ * hung system as the new JobClasses will wait forever
416+ * for events to be emitted that have already been
417+ * destroyed.
418+ */
419+ nih_list_remove (&orig->entry);
420+ }
421
422 /* Read the file into memory for parsing, if this fails we don't
423 * bother creating a new ConfFile structure for it and bail out
424 * now.
425 */
426 buf = nih_file_read (NULL, path_to_load, &len);
427- if (! buf)
428+ if (! buf) {
429+ if (! override_path && orig) {
430+ /* Failed to reload the file from disk in all
431+ * likelihood because the configuration file was
432+ * deleted.
433+ *
434+ * Allow the ConfFile to be cleaned up taking
435+ * its JobClass (and possibly events that
436+ * JobClass was referencing) with it.
437+ */
438+ nih_unref (orig, source);
439+ }
440+
441 return -1;
442+ }
443
444 /* Create a new ConfFile structure (if no @override_path specified) */
445 file = (ConfFile *)nih_hash_lookup (source->files, path);
446@@ -1112,6 +1169,10 @@
447 file->job = parse_job (NULL, source->session, file->job,
448 name, buf, len, &pos, &lineno);
449
450+ /* Allow the original ConfFile which has now been replaced to be
451+ * destroyed which will also cause the original JobClass to be
452+ * freed.
453+ */
454 if (file->job) {
455 job_class_consider (file->job);
456 } else {
457@@ -1123,6 +1184,12 @@
458 nih_assert_not_reached ();
459 }
460
461+ /* Finally, allow the original ConfFile to be destroyed without
462+ * affecting the new JobClass.
463+ */
464+ if (! override_path && orig)
465+ nih_unref (orig, source);
466+
467 /* Deal with any parsing errors that occurred; we don't consider
468 * these to be hard failures, which means we can warn about them
469 * here and give the path and line number along with the warning.
470@@ -1253,8 +1320,568 @@
471 return NULL;
472 }
473
474+/**
475+ * conf_source_serialise:
476+ * @source: ConfSource to serialise.
477+ *
478+ * Convert @source into a JSON representation for serialisation.
479+ * Caller must free returned value using json_object_put().
480+ *
481+ * Returns: JSON-serialised ConfSource object, or NULL on error.
482+ **/
483+json_object *
484+conf_source_serialise (const ConfSource *source)
485+{
486+ json_object *json;
487+ json_object *json_files;
488+ int session_index;
489+
490+ nih_assert (source);
491+ nih_assert (conf_sources);
492+
493+ json = json_object_new_object ();
494+ if (! json)
495+ return NULL;
496+
497+ json_files = json_object_new_array ();
498+ if (! json_files)
499+ goto error;
500+
501+ session_index = session_get_index (source->session);
502+ if (session_index < 0)
503+ goto error;
504+
505+ if (! state_set_json_int_var (json, "session", session_index))
506+ goto error;
507+
508+ if (! state_set_json_string_var_from_obj (json, source, path))
509+ goto error;
510+
511+ if (! state_set_json_enum_var (json,
512+ conf_source_type_enum_to_str,
513+ "type", source->type))
514+ goto error;
515+
516+ /* 'watch' does not need to be serialised - it gets recreated
517+ * when conf_source_new() is called on deserialisation.
518+ */
519+
520+ if (! state_set_json_int_var_from_obj (json, source, flag))
521+ goto error;
522+
523+ /* Add array of ConfFile names to represent the files hash */
524+ NIH_HASH_FOREACH (source->files, file_iter) {
525+ ConfFile *file = (ConfFile *)file_iter;
526+ json_object *json_conf_file;
527+
528+ json_conf_file = conf_file_serialise (file);
529+ if (! json_conf_file)
530+ goto error;
531+
532+ json_object_array_add (json_files, json_conf_file);
533+ }
534+
535+ json_object_object_add (json, "conf_files", json_files);
536+
537+ return json;
538+
539+error:
540+ json_object_put (json);
541+ return NULL;
542+}
543+
544+/**
545+ * conf_source_serialise_all:
546+ *
547+ * Convert existing ConfSource objects to JSON representation.
548+ *
549+ * Returns: JSON object containing array of ConfSource objects,
550+ * or NULL on error.
551+ **/
552+json_object *
553+conf_source_serialise_all (void)
554+{
555+ json_object *json;
556+
557+ conf_init ();
558+
559+ json = json_object_new_array ();
560+ if (! json)
561+ return NULL;
562+
563+ NIH_LIST_FOREACH (conf_sources, iter) {
564+ json_object *json_source;
565+ ConfSource *source = (ConfSource *)iter;
566+
567+ json_source = conf_source_serialise (source);
568+
569+ if (! json_source)
570+ goto error;
571+
572+ json_object_array_add (json, json_source);
573+ }
574+
575+ return json;
576+
577+error:
578+ json_object_put (json);
579+ return NULL;
580+}
581+
582+/**
583+ * conf_source_deserialise:
584+ * @parent: parent,
585+ * @json: JSON-serialised ConfSource object to deserialise.
586+ *
587+ * Create ConfSource from provided JSON and add to the
588+ * conf sources list.
589+ *
590+ * Returns: ConfSource object, or NULL on error.
591+ **/
592+ConfSource *
593+conf_source_deserialise (void *parent, json_object *json)
594+{
595+ ConfSource *source = NULL;
596+ ConfSourceType type = -1;
597+ Session *session;
598+ int session_index = -1;
599+ nih_local char *path = NULL;
600+
601+ nih_assert (json);
602+
603+ if (! state_check_json_type (json, object))
604+ goto error;
605+
606+ if (! state_get_json_int_var (json, "session", session_index))
607+ goto error;
608+
609+ if (session_index < 0)
610+ goto error;
611+
612+ session = session_from_index (session_index);
613+
614+ if (! state_get_json_string_var_strict (json, "path", NULL, path))
615+ goto error;
616+
617+ if (! state_get_json_enum_var (json,
618+ conf_source_type_str_to_enum,
619+ "type", type))
620+ goto error;
621+
622+ source = conf_source_new (parent, path, type);
623+ if (! source)
624+ goto error;
625+
626+ source->session = session;
627+
628+ if (! state_get_json_int_var_to_obj (json, source, flag))
629+ goto error;
630+
631+ if (conf_file_deserialise_all (source, json) < 0)
632+ goto error;
633+
634+ return source;
635+
636+error:
637+ if (source)
638+ nih_free (source);
639+
640+ return NULL;
641+}
642+
643+/**
644+ * conf_source_deserialise_all:
645+ *
646+ * @json: root of JSON-serialised state.
647+ *
648+ * Convert JSON representation of ConfSources back into ConfSource objects.
649+ *
650+ * Returns: 0 on success, -1 on error.
651+ **/
652+int
653+conf_source_deserialise_all (json_object *json)
654+{
655+ ConfSource *source = NULL;
656+
657+ nih_assert (json);
658+
659+ conf_init ();
660+
661+ nih_assert (NIH_LIST_EMPTY (conf_sources));
662+
663+ json_conf_sources = json_object_object_get (json, "conf_sources");
664+
665+ if (! json_conf_sources)
666+ goto error;
667+
668+ if (! state_check_json_type (json_conf_sources, array))
669+ goto error;
670+
671+ for (int i = 0; i < json_object_array_length (json_conf_sources); i++) {
672+ json_object *json_source;
673+
674+ json_source = json_object_array_get_idx (json_conf_sources, i);
675+ if (! json_source)
676+ goto error;
677+
678+ if (! state_check_json_type (json_source, object))
679+ goto error;
680+
681+ source = conf_source_deserialise (NULL, json_source);
682+
683+ if (! source)
684+ continue;
685+ }
686+
687+ return 0;
688+
689+error:
690+ if (source)
691+ nih_free (source);
692+
693+ return -1;
694+}
695+
696+/**
697+ * conf_source_type_enum_to_str:
698+ *
699+ * @type: ConfSourceType.
700+ *
701+ * Convert ConfSourceType to a string representation.
702+ *
703+ * Returns: string representation of @type, or NULL if not known.
704+ **/
705+const char *
706+conf_source_type_enum_to_str (ConfSourceType type)
707+{
708+ state_enum_to_str (CONF_FILE, type);
709+ state_enum_to_str (CONF_DIR, type);
710+ state_enum_to_str (CONF_JOB_DIR, type);
711+
712+ return NULL;
713+}
714+
715+/**
716+ * conf_source_type_str_to_enum:
717+ *
718+ * @type: string ConfSourceType value.
719+ *
720+ * Convert @expect back into an enum value.
721+ *
722+ * Returns: ConfSourceType representing @type, or -1 if not known.
723+ **/
724+ConfSourceType
725+conf_source_type_str_to_enum (const char *type)
726+{
727+ nih_assert (type);
728+
729+ state_str_to_enum (CONF_FILE, type);
730+ state_str_to_enum (CONF_DIR, type);
731+ state_str_to_enum (CONF_JOB_DIR, type);
732+
733+ return -1;
734+}
735+
736+/**
737+ * conf_file_serialise:
738+ * @file: ConfFile to serialise.
739+ *
740+ * Convert @file into a JSON representation for serialisation.
741+ * Caller must free returned value using json_object_put().
742+ *
743+ * Returns: JSON-serialised ConfFile object, or NULL on error.
744+ **/
745+json_object *
746+conf_file_serialise (const ConfFile *file)
747+{
748+ json_object *json;
749+ json_object *json_job_class;
750+ JobClass *registered;
751+ int session_index;
752+ ssize_t conf_source_index;
753+
754+ nih_assert (file);
755+
756+ json = json_object_new_object ();
757+ if (! json)
758+ return NULL;
759+
760+ if (! state_set_json_string_var_from_obj (json, file, path))
761+ goto error;
762+
763+ conf_source_index = conf_source_get_index (file->source);
764+ if (conf_source_index < 0)
765+ goto error;
766+
767+ if (! state_set_json_int_var (json, "conf_source", conf_source_index))
768+ goto error;
769+
770+ if (! state_set_json_int_var_from_obj (json, file, flag))
771+ goto error;
772+
773+ /*
774+ * Ignore the "best" JobClass associated with this ConfFile
775+ * (file->job) since it won't be serialised.
776+ *
777+ * Instead look up the currently registered JobClass and
778+ * reference that. This ensures that best == registered for the
779+ * re-exec. This may change though immediately after re-exec
780+ * when conf_reload() gets called.
781+ *
782+ * See job_class_serialise_all() for further details.
783+ */
784+ registered = job_class_get_registered (file->job->name,
785+ file->job->session);
786+
787+ if (! registered)
788+ goto error;
789+
790+ /* Create a reference to the registered job class in the JSON by
791+ * encoding the name and session index. We do this rather than
792+ * simply encoding an index number for the JobClass since
793+ * job_classes is a hash and it is safer should a re-exec
794+ * result from an upgrade to NIH, say, where its hashing
795+ * algorithm changed meaning the index may be unreliable once
796+ * the job_classes hash was created post-re-exec.
797+ */
798+ json_job_class = json_object_new_object ();
799+ if (! json_job_class)
800+ goto error;
801+
802+ if (! state_set_json_string_var (json_job_class,
803+ "name",
804+ registered->name))
805+ goto error;
806+
807+ session_index = session_get_index (registered->session);
808+ if (session_index < 0)
809+ goto error;
810+
811+ if (! state_set_json_int_var (json_job_class,
812+ "session",
813+ session_index))
814+ goto error;
815+
816+ json_object_object_add (json, "job_class", json_job_class);
817+
818+ return json;
819+
820+error:
821+ json_object_put (json);
822+ return NULL;
823+}
824+
825+/**
826+ * conf_file_deserialise:
827+ * @source: ConfSource,
828+ * @json: JSON-serialised ConfFile object to deserialise.
829+ *
830+ * Create ConfFile from provided JSON and add to the
831+ * conf sources list.
832+ *
833+ * Returns: ConfFile object, or NULL on error.
834+ **/
835+ConfFile *
836+conf_file_deserialise (ConfSource *source, json_object *json)
837+{
838+ ConfFile *file = NULL;
839+ nih_local char *path = NULL;
840+
841+ nih_assert (json);
842+
843+ if (! state_check_json_type (json, object))
844+ goto error;
845+
846+ if (! state_get_json_string_var_strict (json, "path", NULL, path))
847+ goto error;
848+
849+ file = conf_file_new (source, path);
850+ if (! file)
851+ goto error;
852+
853+ /* Note that the associated JobClass is not handled at this
854+ * stage: it can't be the JobClasses haven't been deserialised
855+ * yet. As such, the ConfFile->JobClass link is dealt with by
856+ * job_class_deserialise_all().
857+ */
858+ file->job = NULL;
859+
860+ if (! state_get_json_int_var_to_obj (json, file, flag))
861+ goto error;
862+
863+ return file;
864+
865+error:
866+ if (file)
867+ nih_free (file);
868+
869+ return NULL;
870+}
871+
872+/**
873+ * conf_file_deserialise_all:
874+ *
875+ * @source: ConfSource,
876+ * @json: root of JSON-serialised state.
877+ *
878+ * Convert JSON representation of ConfFiles back into ConfFile objects.
879+ *
880+ * Returns: 0 on success, -1 on error.
881+ **/
882+int
883+conf_file_deserialise_all (ConfSource *source, json_object *json)
884+{
885+ json_object *json_conf_files;
886+ ConfFile *file = NULL;
887+
888+ nih_assert (source);
889+ nih_assert (json);
890+
891+ conf_init ();
892+
893+ json_conf_files = json_object_object_get (json, "conf_files");
894+
895+ if (! json_conf_files)
896+ goto error;
897+
898+ if (! state_check_json_type (json_conf_files, array))
899+ goto error;
900+
901+ for (int i = 0; i < json_object_array_length (json_conf_files); i++) {
902+ json_object *json_file;
903+
904+ json_file = json_object_array_get_idx (json_conf_files, i);
905+ if (! json_file)
906+ goto error;
907+
908+ if (! state_check_json_type (json_file, object))
909+ goto error;
910+
911+ file = conf_file_deserialise (source, json_file);
912+
913+ if (! file)
914+ goto error;
915+ }
916+
917+ return 0;
918+
919+error:
920+ if (file)
921+ nih_free (file);
922+
923+ return -1;
924+}
925+
926+/**
927+ * conf_source_get_index:
928+ *
929+ * @source: ConfSource to search for.
930+ *
931+ * Returns: index of @source in the conf sources list,
932+ * or -1 if not found.
933+ **/
934+ssize_t
935+conf_source_get_index (const ConfSource *source)
936+{
937+ ssize_t i = 0;
938+
939+ nih_assert (source);
940+
941+ conf_init ();
942+
943+ NIH_LIST_FOREACH (conf_sources, iter) {
944+ ConfSource *s = (ConfSource *)iter;
945+
946+ if (! strcmp (source->path, s->path)
947+ && source->session == s->session)
948+ return i;
949+ i++;
950+ }
951+
952+ return -1;
953+}
954+
955+/**
956+ * conf_file_find:
957+ *
958+ * @name: name of ConfFile (without dirname and extension),
959+ * @session: session ConfFile belongs to.
960+ *
961+ * Find the ConfFile with name @name in session @session.
962+ *
963+ * Returns: ConfFile or NULL if not found.
964+ **/
965+ConfFile *
966+conf_file_find (const char *name, const Session *session)
967+{
968+ nih_local char *basename = NULL;
969+
970+ nih_assert (name);
971+
972+ conf_init ();
973+
974+ /* There can only be one ConfFile per session with the same
975+ * basename.
976+ */
977+ basename = NIH_MUST (nih_sprintf (NULL, "/%s%s",
978+ name, CONF_EXT_STD));
979+
980+ NIH_LIST_FOREACH (conf_sources, iter) {
981+ ConfSource *source = (ConfSource *)iter;
982+
983+ if (source->session != session)
984+ continue;
985+
986+ NIH_HASH_FOREACH (source->files, file_iter) {
987+ ConfFile *file = (ConfFile *)file_iter;
988+ char *match;
989+ char *slash;
990+
991+ match = strstr (file->path, basename);
992+ slash = strrchr (file->path, '/');
993+
994+ if (match && match == slash)
995+ return file;
996+ }
997+ }
998+
999+ return NULL;
1000+}
1001+
1002 #ifdef DEBUG
1003
1004+void
1005+debug_show_event_operator (EventOperator *oper)
1006+{
1007+ nih_local char *env = NULL;
1008+
1009+ nih_assert (oper);
1010+
1011+ if (oper->env) env = state_collapse_env ((const char **)oper->env);
1012+
1013+ nih_debug ("EventOperator %p: type='%s', value=%d, name='%s', event='%s', env='%s'",
1014+ oper,
1015+ oper->type == EVENT_MATCH ? "EVENT_MATCH"
1016+ : oper->type == EVENT_AND ? "EVENT_AND"
1017+ : "EVENT_OR",
1018+ oper->value,
1019+ oper->name,
1020+ oper->event ? oper->event->name : "",
1021+ env ? env : "");
1022+}
1023+
1024+void
1025+debug_show_event_operators (EventOperator *root)
1026+{
1027+ nih_assert (root);
1028+
1029+ NIH_TREE_FOREACH_POST (&root->node, iter) {
1030+ EventOperator *oper = (EventOperator *)iter;
1031+
1032+ debug_show_event_operator (oper);
1033+ }
1034+}
1035+
1036 size_t
1037 debug_count_list_entries (const NihList *list)
1038 {
1039@@ -1383,7 +2010,8 @@
1040 void
1041 debug_show_jobs (const NihHash *instances)
1042 {
1043- nih_assert (instances);
1044+ if (! instances)
1045+ return;
1046
1047 nih_debug ("jobs:");
1048
1049@@ -1405,6 +2033,18 @@
1050 }
1051
1052 void
1053+debug_show_events (void)
1054+{
1055+ nih_assert (events);
1056+
1057+ NIH_LIST_FOREACH (events, iter) {
1058+ Event *event = (Event *)iter;
1059+
1060+ debug_show_event (event);
1061+ }
1062+}
1063+
1064+void
1065 debug_show_conf_file (const ConfFile *file)
1066 {
1067 nih_assert (file);
1068
1069=== modified file 'init/conf.h'
1070--- init/conf.h 2013-02-27 11:46:04 +0000
1071+++ init/conf.h 2013-06-05 16:21:24 +0000
1072@@ -1,6 +1,6 @@
1073 /* upstart
1074 *
1075- * Copyright © 2010,2011 Canonical Ltd.
1076+ * Copyright 2010,2011 Canonical Ltd.
1077 * Author: Scott James Remnant <scott@netsplit.com>.
1078 *
1079 * This program is free software; you can redistribute it and/or modify
1080@@ -128,6 +128,50 @@
1081
1082 JobClass * conf_select_job (const char *name, const Session *session);
1083
1084+const char *
1085+conf_source_type_enum_to_str (ConfSourceType type)
1086+ __attribute__ ((warn_unused_result));
1087+
1088+ConfSourceType
1089+conf_source_type_str_to_enum (const char *type)
1090+ __attribute__ ((warn_unused_result));
1091+
1092+json_object *
1093+conf_source_serialise (const ConfSource *source)
1094+ __attribute__ ((warn_unused_result));
1095+
1096+json_object *
1097+conf_source_serialise_all (void)
1098+ __attribute__ ((warn_unused_result));
1099+
1100+ConfSource *
1101+conf_source_deserialise (void *parent, json_object *json)
1102+ __attribute__ ((warn_unused_result));
1103+
1104+int
1105+conf_source_deserialise_all (json_object *json)
1106+ __attribute__ ((warn_unused_result));
1107+
1108+json_object *
1109+conf_file_serialise (const ConfFile *file)
1110+ __attribute__ ((warn_unused_result));
1111+
1112+ConfFile *
1113+conf_file_deserialise (ConfSource *source, json_object *json)
1114+ __attribute__ ((warn_unused_result));
1115+
1116+int
1117+conf_file_deserialise_all (ConfSource *source, json_object *json)
1118+ __attribute__ ((warn_unused_result));
1119+
1120+ssize_t
1121+conf_source_get_index (const ConfSource *source)
1122+ __attribute__ ((warn_unused_result));
1123+
1124+ConfFile *
1125+conf_file_find (const char *name, const Session *session)
1126+ __attribute__ ((warn_unused_result));
1127+
1128 #ifdef DEBUG
1129
1130 /* used for debugging only */
1131@@ -160,6 +204,10 @@
1132 __attribute__ ((unused));
1133
1134 void
1135+debug_show_events (void)
1136+ __attribute__ ((unused));
1137+
1138+void
1139 debug_show_conf_file(const ConfFile *file)
1140 __attribute__ ((unused));
1141
1142@@ -171,6 +219,14 @@
1143 debug_show_conf_sources(void)
1144 __attribute__ ((unused));
1145
1146+void
1147+debug_show_event_operator (EventOperator *oper)
1148+ __attribute__ ((unused));
1149+
1150+void
1151+debug_show_event_operators (EventOperator *root)
1152+ __attribute__ ((unused));
1153+
1154 #endif
1155
1156 NIH_END_EXTERN
1157
1158=== modified file 'init/control.h'
1159--- init/control.h 2013-02-08 16:15:23 +0000
1160+++ init/control.h 2013-06-05 16:21:24 +0000
1161@@ -58,31 +58,31 @@
1162 *
1163 * Returns: -1 on raised error, or nothing on success.
1164 **/
1165-#define control_get_job(session, job, job_name, instance) \
1166-{ \
1167- if (job_name != NULL ) { \
1168- JobClass *class; \
1169- \
1170- class = job_class_find (session, job_name); \
1171- if (! class) { \
1172- nih_dbus_error_raise_printf ( \
1173- DBUS_INTERFACE_UPSTART \
1174- ".Error.UnknownJob", \
1175- _("Unknown job: %s"), \
1176- job_name); \
1177- return -1; \
1178- } \
1179- \
1180- job = job_find (session, class, NULL, instance); \
1181- if (job == NULL) { \
1182- nih_dbus_error_raise_printf ( \
1183- DBUS_INTERFACE_UPSTART \
1184- ".Error.UnknownJobInstance", \
1185- _("Unknown instance: %s of job %s"), \
1186- instance, job_name); \
1187- return -1; \
1188- } \
1189- } \
1190+#define control_get_job(session, job, job_name, instance) \
1191+{ \
1192+ if (job_name != NULL ) { \
1193+ JobClass *class; \
1194+ \
1195+ class = job_class_get_registered (job_name, session); \
1196+ if (! class) { \
1197+ nih_dbus_error_raise_printf ( \
1198+ DBUS_INTERFACE_UPSTART \
1199+ ".Error.UnknownJob", \
1200+ _("Unknown job: %s"), \
1201+ job_name); \
1202+ return -1; \
1203+ } \
1204+ \
1205+ job = job_find (session, class, NULL, instance); \
1206+ if (job == NULL) { \
1207+ nih_dbus_error_raise_printf ( \
1208+ DBUS_INTERFACE_UPSTART \
1209+ ".Error.UnknownJobInstance", \
1210+ _("Unknown instance: %s of job %s"), \
1211+ instance, job_name); \
1212+ return -1; \
1213+ } \
1214+ } \
1215 }
1216
1217 NIH_BEGIN_EXTERN
1218
1219=== modified file 'init/event.c'
1220--- init/event.c 2012-12-14 23:47:51 +0000
1221+++ init/event.c 2013-06-05 16:21:24 +0000
1222@@ -315,8 +315,8 @@
1223
1224 /* We stop first so that if an event is listed both as a
1225 * stop and start event, it causes an active running process
1226- * to be killed, the stop script then the start script to be
1227- * run. In any other state, it has no special effect.
1228+ * to be killed, and then stop script then the start script
1229+ * to be run. In any other state, it has no special effect.
1230 *
1231 * (The other way around would be just strange, it'd cause
1232 * a process's start and stop scripts to be run without the
1233@@ -638,7 +638,7 @@
1234 json_object *json_env;
1235 Event *event = NULL;
1236 nih_local char *name = NULL;
1237- char **env = NULL;
1238+ nih_local char **env = NULL;
1239 int session_index = -1;
1240
1241 nih_assert (json);
1242@@ -675,9 +675,19 @@
1243 "progress", event->progress))
1244 goto error;
1245
1246- if (! state_set_json_int_var_from_obj (json, event, failed))
1247+ if (! state_get_json_int_var_to_obj (json, event, failed))
1248 goto error;
1249
1250+ /* We can only set the blockers count in the scenario that
1251+ * EventOperators are serialised (since without this, it is not
1252+ * possible to manually reconstruct the state of the
1253+ * EventOperators post-re-exec.
1254+ */
1255+ if (json_object_object_get (json, "blockers")) {
1256+ if (! state_get_json_int_var_to_obj (json, event, blockers))
1257+ goto error;
1258+ }
1259+
1260 return event;
1261
1262 error:
1263
1264=== modified file 'init/event_operator.c'
1265--- init/event_operator.c 2013-02-15 14:07:05 +0000
1266+++ init/event_operator.c 2013-06-05 16:21:24 +0000
1267@@ -59,7 +59,7 @@
1268 * array of environment variables in KEY=VALUE form. @env will be referenced
1269 * by the new event. After calling this function, you should never use
1270 * nih_free() to free @env and instead use nih_unref() or nih_discard() if
1271- * you longer need to use it.
1272+ * you no longer need to use it.
1273 *
1274 * If @parent is not NULL, it should be a pointer to another object which
1275 * will be used as a parent for the returned operator. When all parents
1276@@ -782,8 +782,7 @@
1277 env ? env : ""));
1278 nih_list_add_after (stack, &expr->entry);
1279 break;
1280- }
1281- else {
1282+ } else {
1283 /* We build the expression from visiting the logical
1284 * operators (and their children) only.
1285 */
1286@@ -864,3 +863,312 @@
1287
1288 return NIH_MUST (nih_strdup (NULL, latest->str));
1289 }
1290+
1291+/**
1292+ * event_operator_type_enum_to_str:
1293+ *
1294+ * @type: EventOperatorType.
1295+ *
1296+ * Convert EventOperatorType to a string representation.
1297+ *
1298+ * Returns: string representation of @type, or NULL if not known.
1299+ **/
1300+const char *
1301+event_operator_type_enum_to_str (EventOperatorType type)
1302+{
1303+ state_enum_to_str (EVENT_OR, type);
1304+ state_enum_to_str (EVENT_AND, type);
1305+ state_enum_to_str (EVENT_MATCH, type);
1306+
1307+ return NULL;
1308+}
1309+
1310+/**
1311+ * event_operator_type_str_to_enum:
1312+ *
1313+ * @type: string EventOperatorType value.
1314+ *
1315+ * Convert @expect back into an enum value.
1316+ *
1317+ * Returns: EventOperatorType representing @type, or -1 if not known.
1318+ **/
1319+EventOperatorType
1320+event_operator_type_str_to_enum (const char *type)
1321+{
1322+ nih_assert (type);
1323+
1324+ state_str_to_enum (EVENT_OR, type);
1325+ state_str_to_enum (EVENT_AND, type);
1326+ state_str_to_enum (EVENT_MATCH, type);
1327+
1328+ return -1;
1329+}
1330+
1331+/**
1332+ * event_operator_serialise:
1333+ * @oper: EventOperator to serialise.
1334+ *
1335+ * Convert @oper into a JSON representation for serialisation.
1336+ * Caller must free returned value using json_object_put().
1337+ *
1338+ * Returns: JSON-serialised EventOperator object, or NULL on error.
1339+ **/
1340+json_object *
1341+event_operator_serialise (const EventOperator *oper)
1342+{
1343+ json_object *json;
1344+ int event_index;
1345+
1346+ nih_assert (oper);
1347+
1348+ json = json_object_new_object ();
1349+ if (! json)
1350+ return NULL;
1351+
1352+ if (! state_set_json_enum_var (json,
1353+ event_operator_type_enum_to_str,
1354+ "type", oper->type))
1355+ goto error;
1356+
1357+ if (! state_set_json_int_var_from_obj (json, oper, value))
1358+ goto error;
1359+
1360+ if (oper->name) {
1361+ if (! state_set_json_string_var_from_obj (json, oper, name))
1362+ goto error;
1363+ }
1364+
1365+ if (oper->env) {
1366+ if (! state_set_json_str_array_from_obj (json, oper, env))
1367+ goto error;
1368+ }
1369+
1370+ if (oper->event) {
1371+ event_index = event_to_index (oper->event);
1372+ if (event_index < 0)
1373+ goto error;
1374+
1375+ if (! state_set_json_int_var (json, "event", event_index))
1376+ goto error;
1377+ }
1378+
1379+ return json;
1380+
1381+error:
1382+ json_object_put (json);
1383+ return NULL;
1384+
1385+}
1386+
1387+/**
1388+ * event_operator_serialise_all:
1389+ *
1390+ * @root: operator tree to serialise,
1391+ *
1392+ * Convert EventOperator tree to JSON representation.
1393+ *
1394+ * Returns: JSON object containing array of EventOperator nodes in post-order,
1395+ * or NULL on error.
1396+ */
1397+json_object *
1398+event_operator_serialise_all (EventOperator *root)
1399+{
1400+ json_object *json;
1401+ json_object *json_node;
1402+
1403+ nih_assert (root);
1404+
1405+ json = json_object_new_array ();
1406+ if (! json)
1407+ return NULL;
1408+
1409+ NIH_TREE_FOREACH_POST (&root->node, iter) {
1410+ EventOperator *oper = (EventOperator *)iter;
1411+
1412+ json_node = event_operator_serialise (oper);
1413+ if (! json_node)
1414+ goto error;
1415+
1416+ if (json_object_array_add (json, json_node) < 0)
1417+ goto error;
1418+ }
1419+
1420+ return json;
1421+
1422+error:
1423+ json_object_put (json);
1424+ return NULL;
1425+}
1426+
1427+/**
1428+ * event_operator_deserialise:
1429+ * @parent: parent,
1430+ * @json: JSON-serialised EventOperator object to deserialise.
1431+ *
1432+ * Create EventOperator from provided JSON.
1433+ *
1434+ * Returns: EventOperator object, or NULL on error.
1435+ **/
1436+EventOperator *
1437+event_operator_deserialise (void *parent, json_object *json)
1438+{
1439+ EventOperator *oper = NULL;
1440+ EventOperatorType type = -1;
1441+ nih_local char *name = NULL;
1442+ nih_local char **env = NULL;
1443+
1444+ nih_assert (json);
1445+
1446+ if (! state_check_json_type (json, object))
1447+ goto error;
1448+
1449+ if (json_object_object_get (json, "name")) {
1450+ if (! state_get_json_string_var_strict (json, "name", NULL, name))
1451+ goto error;
1452+ }
1453+
1454+ if (! state_get_json_enum_var (json,
1455+ event_operator_type_str_to_enum,
1456+ "type", type))
1457+ goto error;
1458+
1459+ if (json_object_object_get (json, "env")) {
1460+ json_object *json_env;
1461+ if (! state_get_json_var_full (json, "env", array, json_env))
1462+ goto error;
1463+
1464+ /* XXX: note that we have to treat the environment array
1465+ * as a plain string array (rather than an environ
1466+ * array) at this point since the values are not
1467+ * expanded (do not necessarily contain '='), and hence
1468+ * would be discarded by the environ-handling routines.
1469+ */
1470+ if (! state_deserialise_str_array (NULL, json_env, &env))
1471+ goto error;
1472+ }
1473+
1474+ oper = event_operator_new (parent, type, name, env);
1475+ if (! oper)
1476+ goto error;
1477+
1478+ if (! state_get_json_int_var_to_obj (json, oper, value))
1479+ goto error;
1480+
1481+ if (json_object_object_get (json, "event")) {
1482+ int event_index;
1483+
1484+ if (! state_get_json_int_var (json, "event", event_index))
1485+ goto error;
1486+
1487+ oper->event = event_from_index (event_index);
1488+ if (! oper->event)
1489+ goto error;
1490+ }
1491+
1492+ return oper;
1493+
1494+error:
1495+ if (oper)
1496+ nih_free (oper);
1497+
1498+ return NULL;
1499+}
1500+
1501+/**
1502+ * event_operator_deserialise_all:
1503+ *
1504+ * @parent: parent,
1505+ * @json: root of JSON-serialised state.
1506+ *
1507+ * Convert EventOperator tree to JSON representation.
1508+ *
1509+ * Returns: EventOperator tree root node on success, or NULL on error.
1510+ */
1511+EventOperator *
1512+event_operator_deserialise_all (void *parent, json_object *json)
1513+{
1514+ EventOperator *oper = NULL;
1515+ EventOperator *left_oper = NULL;
1516+ EventOperator *right_oper = NULL;
1517+ nih_local NihList *stack = NULL;
1518+ NihListEntry *item;
1519+
1520+ nih_assert (json);
1521+
1522+ stack = NIH_MUST (nih_list_new (NULL));
1523+
1524+ if (! state_check_json_type (json, array))
1525+ goto error;
1526+
1527+ for (int i = 0; i < json_object_array_length (json); i++) {
1528+ json_object *json_event_operator;
1529+ nih_local NihList *left = NULL;
1530+ nih_local NihList *right = NULL;
1531+
1532+ json_event_operator = json_object_array_get_idx (json, i);
1533+ if (! json_event_operator)
1534+ goto error;
1535+
1536+ if (! state_check_json_type (json_event_operator, object))
1537+ goto error;
1538+
1539+ oper = event_operator_deserialise (parent, json_event_operator);
1540+ if (! oper)
1541+ goto error;
1542+
1543+ item = nih_list_entry_new (stack);
1544+ if (! item)
1545+ goto error;
1546+
1547+ switch (oper->type) {
1548+ case EVENT_AND:
1549+ case EVENT_OR:
1550+ left = NIH_MUST (nih_list_new (NULL));
1551+ right = NIH_MUST (nih_list_new (NULL));
1552+
1553+ /* pop the top two stack elements */
1554+ nih_assert (! NIH_LIST_EMPTY (stack));
1555+ right = nih_list_add (right, stack->next);
1556+
1557+ nih_assert (! NIH_LIST_EMPTY (stack));
1558+ left = nih_list_add (left, stack->next);
1559+
1560+ left_oper = (EventOperator *)((NihListEntry *)left)->data;
1561+ right_oper = (EventOperator *)((NihListEntry *)right)->data;
1562+
1563+ nih_assert (left_oper);
1564+ nih_assert (right_oper);
1565+
1566+ /* Attach them as children of the new operator */
1567+ nih_tree_add (&oper->node, &left_oper->node, NIH_TREE_LEFT);
1568+ nih_tree_add (&oper->node, &right_oper->node, NIH_TREE_RIGHT);
1569+
1570+ /* FALL THROUGH:
1571+ *
1572+ * This will re-add the operator to the stack.
1573+ */
1574+
1575+ case EVENT_MATCH:
1576+ item->data = oper;
1577+ nih_list_add_after (stack, &item->entry);
1578+ break;
1579+ default:
1580+ nih_assert_not_reached ();
1581+ }
1582+ }
1583+
1584+ nih_assert (! NIH_LIST_EMPTY (stack));
1585+
1586+ oper = ((NihListEntry *)stack->next)->data;
1587+
1588+ nih_list_remove (stack->next);
1589+ nih_assert (NIH_LIST_EMPTY (stack));
1590+
1591+ return oper;
1592+
1593+error:
1594+ if (oper)
1595+ nih_free (oper);
1596+
1597+ return NULL;
1598+}
1599
1600=== modified file 'init/event_operator.h'
1601--- init/event_operator.h 2013-02-27 11:46:04 +0000
1602+++ init/event_operator.h 2013-06-05 16:21:24 +0000
1603@@ -108,8 +108,33 @@
1604
1605 void event_operator_reset (EventOperator *root);
1606
1607+const char *
1608+event_operator_type_enum_to_str (EventOperatorType type)
1609+ __attribute__ ((warn_unused_result));
1610+
1611+EventOperatorType
1612+event_operator_type_str_to_enum (const char *type)
1613+ __attribute__ ((warn_unused_result));
1614+
1615+json_object *
1616+event_operator_serialise (const EventOperator *oper)
1617+ __attribute__ ((warn_unused_result));
1618+
1619+json_object *
1620+event_operator_serialise_all (EventOperator *root)
1621+ __attribute__ ((warn_unused_result));
1622+
1623+EventOperator *
1624+event_operator_deserialise (void *parent, json_object *json)
1625+ __attribute__ ((warn_unused_result));
1626+
1627+EventOperator *
1628+event_operator_deserialise_all (void *parent, json_object *json)
1629+ __attribute__ ((warn_unused_result));
1630+
1631+
1632 char *event_operator_collapse (EventOperator *condition)
1633- __attribute__ ((warn_unused_result));
1634+ __attribute__ ((warn_unused_result, unused));
1635
1636 NIH_END_EXTERN
1637
1638
1639=== modified file 'init/job.c'
1640--- init/job.c 2013-05-27 14:29:22 +0000
1641+++ init/job.c 2013-06-05 16:21:24 +0000
1642@@ -570,6 +570,7 @@
1643 job->path));
1644 }
1645
1646+ /* Destroy the instance */
1647 nih_free (job);
1648 }
1649
1650@@ -1577,7 +1578,6 @@
1651 json_object *json_pid;
1652 json_object *json_fds;
1653 json_object *json_logs;
1654- nih_local char *stop_on = NULL;
1655
1656 nih_assert (job);
1657
1658@@ -1611,12 +1611,13 @@
1659 goto error;
1660
1661 if (job->stop_on) {
1662- stop_on = event_operator_collapse (job->stop_on);
1663- if (! stop_on)
1664- goto error;
1665-
1666- if (! state_set_json_string_var (json, "stop_on", stop_on))
1667- goto error;
1668+ json_object *json_stop_on;
1669+
1670+ json_stop_on = event_operator_serialise_all (job->stop_on);
1671+ if (! json_stop_on)
1672+ goto error;
1673+
1674+ json_object_object_add (json, "stop_on", json_stop_on);
1675 }
1676
1677 json_fds = state_serialise_int_array (int, job->fds, job->num_fds);
1678@@ -1739,7 +1740,6 @@
1679 json_object *
1680 job_serialise_all (const NihHash *jobs)
1681 {
1682- int count = 0;
1683 json_object *json;
1684
1685 nih_assert (jobs);
1686@@ -1752,7 +1752,6 @@
1687 json_object *json_job;
1688 Job *job = (Job *)iter;
1689
1690- count++;
1691 json_job = job_serialise (job);
1692
1693 if (! json_job)
1694@@ -1761,12 +1760,6 @@
1695 json_object_array_add (json, json_job);
1696 }
1697
1698- /* Raise an error to avoid serialising job classes with
1699- * no associated jobs.
1700- */
1701- if (! count)
1702- goto error;
1703-
1704 return json;
1705
1706 error:
1707@@ -1794,6 +1787,7 @@
1708 json_object *json_fds;
1709 json_object *json_pid;
1710 json_object *json_logs;
1711+ json_object *json_stop_on = NULL;
1712 size_t len;
1713 int ret;
1714
1715@@ -1833,37 +1827,53 @@
1716 if (! state_get_json_env_array_to_obj (json, job, stop_env))
1717 goto error;
1718
1719- if (json_object_object_get (json, "stop_on")) {
1720- nih_local char *stop_on = NULL;
1721-
1722- if (! state_get_json_string_var_strict (json, "stop_on", NULL, stop_on))
1723- goto error;
1724-
1725- if (*stop_on) {
1726- nih_local JobClass *tmp = NULL;
1727-
1728- tmp = NIH_MUST (job_class_new (NULL, "tmp", NULL));
1729-
1730- tmp->stop_on = parse_on_simple (tmp, "stop", stop_on);
1731- if (! tmp->stop_on) {
1732- NihError *err;
1733-
1734- err = nih_error_get ();
1735-
1736- nih_error ("%s %s: %s",
1737- _("BUG"),
1738- _("instance 'stop on' parse error"),
1739- err->message);
1740-
1741- nih_free (err);
1742-
1743- goto error;
1744- }
1745-
1746- nih_free (job->stop_on);
1747- job->stop_on = event_operator_copy (job, tmp->stop_on);
1748+ if (json_object_object_get_ex (json, "stop_on", &json_stop_on)) {
1749+
1750+ if (state_check_json_type (json_stop_on, array)) {
1751+
1752+ job->stop_on = event_operator_deserialise_all (job, json_stop_on);
1753 if (! job->stop_on)
1754 goto error;
1755+ } else {
1756+ nih_local char *stop_on = NULL;
1757+
1758+ /* Old format (string)
1759+ *
1760+ * Note that we re-search for the JSON key here
1761+ * (json, rather than json_stop_on) to allow
1762+ * the use of the convenience macro. This is
1763+ * of course slower, but its a legacy scenario.
1764+ */
1765+
1766+ if (! state_get_json_string_var_strict (json, "stop_on", NULL, stop_on))
1767+ goto error;
1768+
1769+ if (*stop_on) {
1770+ nih_local JobClass *tmp = NULL;
1771+
1772+ tmp = NIH_MUST (job_class_new (NULL, "tmp", NULL));
1773+
1774+ tmp->stop_on = parse_on_simple (tmp, "stop", stop_on);
1775+ if (! tmp->stop_on) {
1776+ NihError *err;
1777+
1778+ err = nih_error_get ();
1779+
1780+ nih_error ("%s %s: %s",
1781+ _("BUG"),
1782+ _("instance 'stop on' parse error"),
1783+ err->message);
1784+
1785+ nih_free (err);
1786+
1787+ goto error;
1788+ }
1789+
1790+ nih_free (job->stop_on);
1791+ job->stop_on = event_operator_copy (job, tmp->stop_on);
1792+ if (! job->stop_on)
1793+ goto error;
1794+ }
1795 }
1796 }
1797
1798@@ -2284,7 +2294,7 @@
1799 Job *
1800 job_find (const Session *session,
1801 JobClass *class,
1802- char *job_class,
1803+ const char *job_class,
1804 const char *job_name)
1805 {
1806 Job *job;
1807@@ -2293,7 +2303,7 @@
1808 nih_assert (job_classes);
1809
1810 if (! class)
1811- class = job_class_find (session, job_class);
1812+ class = job_class_get_registered (job_class, session);
1813
1814 if (! class)
1815 goto error;
1816
1817=== modified file 'init/job.h'
1818--- init/job.h 2013-05-15 13:21:54 +0000
1819+++ init/job.h 2013-06-05 16:21:24 +0000
1820@@ -222,8 +222,8 @@
1821
1822 Job * job_find (const Session *session,
1823 JobClass *class,
1824- char *job_class,
1825- const char *job_name)
1826+ const char *job_class,
1827+ const char *job_name)
1828 __attribute__ ((warn_unused_result));
1829
1830 NIH_END_EXTERN
1831
1832=== modified file 'init/job_class.c'
1833--- init/job_class.c 2013-05-15 13:21:54 +0000
1834+++ init/job_class.c 2013-06-05 16:21:24 +0000
1835@@ -2,7 +2,7 @@
1836 *
1837 * job_class.c - job class definition handling
1838 *
1839- * Copyright © 2011 Canonical Ltd.
1840+ * Copyright 2011 Canonical Ltd.
1841 * Author: Scott James Remnant <scott@netsplit.com>.
1842 *
1843 * This program is free software; you can redistribute it and/or modify
1844@@ -371,6 +371,35 @@
1845 return NULL;
1846 }
1847
1848+/**
1849+ * job_class_get_registered:
1850+ *
1851+ * @name: name of JobClass to search for,
1852+ * @session: Session of @class.
1853+ *
1854+ * Determine the currently registered JobClass with name @name for
1855+ * session @session.
1856+ *
1857+ * Returns: JobClass or NULL if no JobClass with name @name and
1858+ * session @session is registered.
1859+ **/
1860+JobClass *
1861+job_class_get_registered (const char *name, const Session *session)
1862+{
1863+ JobClass *registered = NULL;
1864+
1865+ nih_assert (name);
1866+
1867+ job_class_init ();
1868+
1869+ /* If we found an entry, ensure we only consider the appropriate session */
1870+ do {
1871+ registered = (JobClass *)nih_hash_search (job_classes,
1872+ name, registered ? &registered->entry : NULL);
1873+ } while (registered && registered->session != session);
1874+
1875+ return registered;
1876+}
1877
1878 /**
1879 * job_class_consider:
1880@@ -385,7 +414,8 @@
1881 int
1882 job_class_consider (JobClass *class)
1883 {
1884- JobClass *registered = NULL, *best = NULL;
1885+ JobClass *registered = NULL;
1886+ JobClass *best = NULL;
1887
1888 nih_assert (class != NULL);
1889
1890@@ -395,16 +425,19 @@
1891 nih_assert (best != NULL);
1892 nih_assert (best->session == class->session);
1893
1894- registered = (JobClass *)nih_hash_search (job_classes, class->name, NULL);
1895-
1896- /* If we found an entry, ensure we only consider the appropriate session */
1897- while (registered && registered->session != class->session)
1898- registered = (JobClass *)nih_hash_search (job_classes, class->name, &registered->entry);
1899+ registered = job_class_get_registered (class->name, class->session);
1900
1901 if (registered != best) {
1902- if (registered)
1903- if (! job_class_remove (registered, class->session))
1904+ if (registered) {
1905+ job_class_event_block (NULL, registered, best);
1906+
1907+ if (! job_class_remove (registered, class->session)) {
1908+ /* Couldn't deregister, so undo */
1909+ if (best->start_on)
1910+ event_operator_reset (best->start_on);
1911 return FALSE;
1912+ }
1913+ }
1914
1915 job_class_add (best);
1916 }
1917@@ -428,7 +461,8 @@
1918 int
1919 job_class_reconsider (JobClass *class)
1920 {
1921- JobClass *registered = NULL, *best = NULL;
1922+ JobClass *registered = NULL;
1923+ JobClass *best = NULL;
1924
1925 nih_assert (class != NULL);
1926
1927@@ -436,11 +470,7 @@
1928
1929 best = conf_select_job (class->name, class->session);
1930
1931- registered = (JobClass *)nih_hash_search (job_classes, class->name, NULL);
1932-
1933- /* If we found an entry, ensure we only consider the appropriate session */
1934- while (registered && registered->session != class->session)
1935- registered = (JobClass *)nih_hash_search (job_classes, class->name, &registered->entry);
1936+ registered = job_class_get_registered (class->name, class->session);
1937
1938 if (registered == class) {
1939 if (class != best) {
1940@@ -459,6 +489,80 @@
1941 }
1942
1943 /**
1944+ * job_class_event_block:
1945+ *
1946+ * @parent: parent object for list,
1947+ * @old: original JobClass currently registered in job_classes,
1948+ * @new: new "best" JobClass that is not yet present in job_classes.
1949+ *
1950+ * Compare @old and @new start on EventOperator trees looking for
1951+ * matching events that occur in both (_and_ which implicitly still exist
1952+ * in the global events list). Events that satisfy these criteria will have
1953+ * their reference count elevated to allow @new to replace @old in job_classes
1954+ * without the destruction of @old freeing the events in question.
1955+ *
1956+ * Note that the reference count never needs to be decremented back
1957+ * again since this function effectively passes "ownership" of the event
1958+ * block from @old to @new, since @old will be replaced by @new but @new
1959+ * should replicate the EventOperator state of @old.
1960+ **/
1961+void
1962+job_class_event_block (void *parent, JobClass *old, JobClass *new)
1963+{
1964+ EventOperator *old_root;
1965+ EventOperator *new_root;
1966+
1967+ if (! old || ! new)
1968+ return;
1969+
1970+ old_root = old->start_on;
1971+ new_root = new->start_on;
1972+
1973+ /* If either @old or @new are NULL, or have no start_on
1974+ * condition, there is no need to modify any events.
1975+ */
1976+ if (! old_root || ! new_root)
1977+ return;
1978+
1979+ /* The old JobClass has associated instances meaning it
1980+ * will not be possible for job_class_remove() to replace it, so
1981+ * we don't need to manipulate any event reference counts.
1982+ */
1983+ NIH_HASH_FOREACH (old->instances, iter)
1984+ return;
1985+
1986+ NIH_TREE_FOREACH_POST (&old_root->node, iter) {
1987+ EventOperator *old_oper = (EventOperator *)iter;
1988+ Event *event;
1989+
1990+ if (old_oper->type != EVENT_MATCH)
1991+ continue;
1992+
1993+ /* Ignore nodes that are not blocking events */
1994+ if (! old_oper->event)
1995+ continue;
1996+
1997+ /* Since the JobClass is blocking an event,
1998+ * that event must be valid.
1999+ */
2000+ event = old_oper->event;
2001+
2002+ NIH_TREE_FOREACH_POST (&new_root->node, niter) {
2003+ EventOperator *new_oper = (EventOperator *)niter;
2004+
2005+ if (new_oper->type != EVENT_MATCH)
2006+ continue;
2007+
2008+ /* ignore the return - we just want to ensure
2009+ * that any events in @new that match those in
2010+ * @old have identical nodes.
2011+ */
2012+ (void)event_operator_handle (new_oper, event, NULL);
2013+ }
2014+ }
2015+}
2016+
2017+/**
2018 * job_class_add:
2019 * @class: new class to select.
2020 *
2021@@ -493,24 +597,21 @@
2022 void
2023 job_class_add_safe (JobClass *class)
2024 {
2025- JobClass *existing = NULL;
2026+ JobClass *registered = NULL;
2027
2028 nih_assert (class);
2029 nih_assert (class->name);
2030
2031 control_init ();
2032
2033- /* Ensure no existing class exists for the same session */
2034- do {
2035- existing = (JobClass *)nih_hash_search (job_classes,
2036- class->name, existing ? &existing->entry : NULL);
2037- } while (existing && existing->session != class->session);
2038+ registered = job_class_get_registered (class->name, class->session);
2039
2040- nih_assert (! existing);
2041+ nih_assert (! registered);
2042
2043 job_class_add (class);
2044 }
2045
2046+
2047 /**
2048 * job_class_remove:
2049 * @class: class to remove,
2050@@ -1286,6 +1387,8 @@
2051
2052 nih_assert (name);
2053
2054+ job_class_init ();
2055+
2056 do {
2057 class = (JobClass *)nih_hash_search (job_classes, name, prev);
2058 if (! class)
2059@@ -1710,8 +1813,8 @@
2060 json_object *json_normalexit;
2061 json_object *json_limits;
2062 json_object *json_jobs;
2063- nih_local char *start_on = NULL;
2064- nih_local char *stop_on = NULL;
2065+ json_object *json_start_on;
2066+ json_object *json_stop_on;
2067 int session_index;
2068
2069 nih_assert (class);
2070@@ -1721,15 +1824,6 @@
2071 if (! json)
2072 return NULL;
2073
2074- /* XXX: chroot jobs are not currently supported
2075- * due to ConfSources not currently being serialised.
2076- */
2077- if (class->session) {
2078- nih_info ("WARNING: serialisation of chroot "
2079- "sessions not currently supported");
2080- goto error;
2081- }
2082-
2083 session_index = session_get_index (class->session);
2084 if (session_index < 0)
2085 goto error;
2086@@ -1774,21 +1868,19 @@
2087 json_object_object_add (json, "export", json_export);
2088
2089 if (class->start_on) {
2090- start_on = event_operator_collapse (class->start_on);
2091- if (! start_on)
2092+ json_start_on = event_operator_serialise_all (class->start_on);
2093+ if (! json_start_on)
2094 goto error;
2095
2096- if (! state_set_json_string_var (json, "start_on", start_on))
2097- goto error;
2098+ json_object_object_add (json, "start_on", json_start_on);
2099 }
2100
2101 if (class->stop_on) {
2102- stop_on = event_operator_collapse (class->stop_on);
2103- if (! stop_on)
2104+ json_stop_on = event_operator_serialise_all (class->stop_on);
2105+ if (! json_stop_on)
2106 goto error;
2107
2108- if (! state_set_json_string_var (json, "stop_on", stop_on))
2109- goto error;
2110+ json_object_object_add (json, "stop_on", json_stop_on);
2111 }
2112
2113 json_emits = class->emits
2114@@ -1888,7 +1980,20 @@
2115 /**
2116 * job_class_serialise_all:
2117 *
2118- * Convert existing JobClass objects to JSON representation.
2119+ * Convert existing JobClass objects in job classes hash to JSON
2120+ * representation.
2121+ *
2122+ * NOTE: despite its name, this function does not _necessarily_
2123+ * serialise all JobClasses - there may be "best" (ie newer) JobClasses
2124+ * associated with ConfFiles that have not yet replaced the existing
2125+ * entries in the job classes hash if the JobClass has running instances.
2126+ *
2127+ * However, this is academic since although such data is not serialised,
2128+ * after the re-exec conf_reload() is called to recreate these "best"
2129+ * JobClasses. This also has the nice side-effect of ensuring that
2130+ * should jobs get created in the window when Upstart is statefully
2131+ * re-exec'ing, it will always see the newest versions of on-disk files
2132+ * (which is what the user expects).
2133 *
2134 * Returns: JSON object containing array of JobClass objects,
2135 * or NULL on error.
2136@@ -1910,17 +2015,17 @@
2137
2138 json_class = job_class_serialise (class);
2139
2140- /* No object returned means the class doesn't need to be
2141- * serialised. Even if this is a real failure, it's always
2142- * better to serialise as much of the state as possible.
2143- */
2144 if (! json_class)
2145- continue;
2146+ goto error;
2147
2148 json_object_array_add (json, json_class);
2149 }
2150
2151 return json;
2152+
2153+error:
2154+ json_object_put (json);
2155+ return NULL;
2156 }
2157
2158 /**
2159@@ -1937,11 +2042,14 @@
2160 {
2161 json_object *json_normalexit;
2162 JobClass *class = NULL;
2163+ ConfFile *file = NULL;
2164 Session *session;
2165 int session_index = -1;
2166 int ret;
2167 nih_local char *name = NULL;
2168 nih_local char *path = NULL;
2169+ json_object *json_start_on = NULL;
2170+ json_object *json_stop_on = NULL;
2171
2172 nih_assert (json);
2173 nih_assert (job_classes);
2174@@ -1957,11 +2065,9 @@
2175
2176 session = session_from_index (session_index);
2177
2178- /* XXX: chroot jobs are not currently supported
2179- * due to ConfSources not currently being serialised.
2180- */
2181+ /* XXX: chroot and old user session jobs not currently supported */
2182 if (session) {
2183- nih_info ("WARNING: deserialisation of chroot "
2184+ nih_info ("WARNING: deserialisation of user/chroot "
2185 "sessions not currently supported");
2186 goto error;
2187 }
2188@@ -1969,10 +2075,20 @@
2189 if (! state_get_json_string_var_strict (json, "name", NULL, name))
2190 goto error;
2191
2192+ /* Create the class and associate it with the ConfFile */
2193 class = job_class_new (NULL, name, session);
2194 if (! class)
2195 goto error;
2196
2197+ /* Lookup the ConfFile associated with this class.
2198+ *
2199+ * Don't error if this fails since previous serialisation data
2200+ * formats did not encode ConfSources and ConfFiles.
2201+ */
2202+ file = conf_file_find (name, session);
2203+ if (file)
2204+ file->job = class;
2205+
2206 /* job_class_new() sets path */
2207 if (! state_get_json_string_var_strict (json, "path", NULL, path))
2208 goto error;
2209@@ -2003,52 +2119,77 @@
2210 goto error;
2211
2212 /* start and stop conditions are optional */
2213- if (json_object_object_get (json, "start_on")) {
2214- nih_local char *start_on = NULL;
2215-
2216- if (! state_get_json_string_var_strict (json, "start_on", NULL, start_on))
2217- goto error;
2218-
2219- if (*start_on) {
2220- class->start_on = parse_on_simple (class, "start", start_on);
2221- if (! class->start_on) {
2222- NihError *err;
2223-
2224- err = nih_error_get ();
2225-
2226- nih_error ("%s %s: %s",
2227- _("BUG"),
2228- _("'start on' parse error"),
2229- err->message);
2230-
2231- nih_free (err);
2232-
2233- goto error;
2234+ if (json_object_object_get_ex (json, "start_on", &json_start_on)) {
2235+
2236+ if (state_check_json_type (json_start_on, array)) {
2237+
2238+ class->start_on = event_operator_deserialise_all (class, json_start_on);
2239+ if (! class->start_on)
2240+ goto error;
2241+ } else {
2242+ nih_local char *start_on = NULL;
2243+
2244+ /* Old format (string).
2245+ *
2246+ * Note that we re-search for the JSON key here
2247+ * (json, rather than json_start_on) to allow
2248+ * the use of the convenience macro. This is
2249+ * of course slower, but its a legacy scenario.
2250+ */
2251+ if (! state_get_json_string_var_strict (json, "start_on", NULL, start_on))
2252+ goto error;
2253+
2254+ if (*start_on) {
2255+ class->start_on = parse_on_simple (class, "start", start_on);
2256+ if (! class->start_on) {
2257+ NihError *err;
2258+
2259+ err = nih_error_get ();
2260+
2261+ nih_error ("%s %s: %s",
2262+ _("BUG"),
2263+ _("'start on' parse error"),
2264+ err->message);
2265+
2266+ nih_free (err);
2267+
2268+ goto error;
2269+ }
2270 }
2271 }
2272 }
2273
2274- if (json_object_object_get (json, "stop_on")) {
2275- nih_local char *stop_on = NULL;
2276-
2277- if (! state_get_json_string_var_strict (json, "stop_on", NULL, stop_on))
2278- goto error;
2279-
2280- if (*stop_on) {
2281- class->stop_on = parse_on_simple (class, "stop", stop_on);
2282- if (! class->stop_on) {
2283- NihError *err;
2284-
2285- err = nih_error_get ();
2286-
2287- nih_error ("%s %s: %s",
2288- _("BUG"),
2289- _("'stop on' parse error"),
2290- err->message);
2291-
2292- nih_free (err);
2293-
2294- goto error;
2295+ if (json_object_object_get_ex (json, "stop_on", &json_stop_on)) {
2296+
2297+ if (state_check_json_type (json_stop_on, array)) {
2298+
2299+ class->stop_on = event_operator_deserialise_all (class, json_stop_on);
2300+ if (! class->stop_on)
2301+ goto error;
2302+ } else {
2303+ nih_local char *stop_on = NULL;
2304+
2305+ /* Old format (string) - re-search as above */
2306+
2307+ if (! state_get_json_string_var_strict (json, "stop_on", NULL, stop_on))
2308+ goto error;
2309+
2310+ if (*stop_on) {
2311+ class->stop_on = parse_on_simple (class, "stop", stop_on);
2312+ if (! class->stop_on) {
2313+ NihError *err;
2314+
2315+ err = nih_error_get ();
2316+
2317+ nih_error ("%s %s: %s",
2318+ _("BUG"),
2319+ _("'stop on' parse error"),
2320+ err->message);
2321+
2322+ nih_free (err);
2323+
2324+ goto error;
2325+ }
2326 }
2327 }
2328 }
2329@@ -2137,12 +2278,17 @@
2330 if (process_deserialise_all (json, class->process, class->process) < 0)
2331 goto error;
2332
2333- /* Force class to be known.
2334- *
2335- * We cannot use job_class_*consider() since the
2336- * JobClasses have no associated ConfFile.
2337- */
2338- job_class_add_safe (class);
2339+ if (file) {
2340+ /* Add the class to the job_classes hash if ConfFiles were
2341+ * available in the serialisation data.
2342+ */
2343+ job_class_consider (class);
2344+ } else {
2345+ /* No ConfSources and ConfFiles were available in the
2346+ * serialisation data, so special-case the insertion.
2347+ */
2348+ job_class_add_safe (class);
2349+ }
2350
2351 /* Any jobs must be added after the class is registered
2352 * (since you cannot add a job to a partially-created
2353@@ -2187,7 +2333,7 @@
2354 goto error;
2355
2356 for (int i = 0; i < json_object_array_length (json_classes); i++) {
2357- json_object *json_class;
2358+ json_object *json_class;
2359
2360 json_class = json_object_array_get_idx (json_classes, i);
2361 if (! json_class)
2362@@ -2198,11 +2344,23 @@
2363
2364 class = job_class_deserialise (json_class);
2365
2366- /* For parity with the serialisation code, don't treat
2367- * errors as fatal for the entire deserialisation.
2368+ /* Either memory is low or -- more likely -- a JobClass
2369+ * with a session was encountered, so keep going.
2370 */
2371- if (! class)
2372- continue;
2373+ if (! class) {
2374+ int session_index = -1;
2375+
2376+ if (state_get_json_int_var (json_class, "session", session_index)
2377+ && session_index > 0) {
2378+ /* Although ConfSources are now serialised, ignore
2379+ * JobClasses with associated user/chroot sessions to avoid
2380+ * behavioural changes for the time being.
2381+ */
2382+ continue;
2383+ } else {
2384+ goto error;
2385+ }
2386+ }
2387 }
2388
2389 return 0;
2390@@ -2358,33 +2516,6 @@
2391 }
2392
2393 /**
2394- * job_class_find:
2395- *
2396- * @session: session,
2397- * @name: name of JobClass.
2398- *
2399- * Lookup a JobClass by session and name.
2400- *
2401- * Returns: JobClass associated with @session, or NULL if not found.
2402- */
2403-JobClass *
2404-job_class_find (const Session *session,
2405- const char *name)
2406-{
2407- JobClass *class = NULL;
2408-
2409- nih_assert (name);
2410- nih_assert (job_classes);
2411-
2412- do {
2413- class = (JobClass *)nih_hash_search (job_classes,
2414- name, class ? &class->entry : NULL);
2415- } while (class && class->session != session);
2416-
2417- return class;
2418-}
2419-
2420-/**
2421 * job_class_max_kill_timeout:
2422 *
2423 * Determine maximum kill timeout for all running jobs.
2424@@ -2413,3 +2544,29 @@
2425
2426 return kill_timeout;
2427 }
2428+
2429+/**
2430+ * job_class_get_index:
2431+ * @class: JobClass to search for.
2432+ *
2433+ * Returns: index of @class in the job classes hash,
2434+ * or -1 if not found.
2435+ **/
2436+ssize_t
2437+job_class_get_index (const JobClass *class)
2438+{
2439+ ssize_t i = 0;
2440+
2441+ nih_assert (class);
2442+
2443+ NIH_HASH_FOREACH (job_classes, iter) {
2444+ JobClass *c = (JobClass *)iter;
2445+
2446+ if (! strcmp (c->name, class->name)
2447+ && c->session == class->session)
2448+ return i;
2449+ i++;
2450+ }
2451+
2452+ return -1;
2453+}
2454
2455=== modified file 'init/job_class.h'
2456--- init/job_class.h 2013-05-15 13:21:54 +0000
2457+++ init/job_class.h 2013-06-05 16:21:24 +0000
2458@@ -359,12 +359,18 @@
2459
2460 void job_class_prepare_reexec (void);
2461
2462-JobClass * job_class_find (const Session *session, const char *name)
2463- __attribute__ ((warn_unused_result));
2464-
2465 time_t job_class_max_kill_timeout (void)
2466 __attribute__ ((warn_unused_result));
2467
2468+JobClass *job_class_get_registered (const char *name, const Session *session)
2469+ __attribute__ ((warn_unused_result));
2470+
2471+void job_class_event_block (void *parent, JobClass *old, JobClass *new);
2472+
2473+ssize_t
2474+job_class_get_index (const JobClass *class)
2475+ __attribute__ ((warn_unused_result));
2476+
2477 NIH_END_EXTERN
2478
2479 #endif /* INIT_JOB_CLASS_H */
2480
2481=== modified file 'init/main.c'
2482--- init/main.c 2013-04-22 10:30:09 +0000
2483+++ init/main.c 2013-06-05 16:21:24 +0000
2484@@ -126,6 +126,7 @@
2485 extern int disable_job_logging;
2486 extern int use_session_bus;
2487 extern int default_console;
2488+extern int write_state_file;
2489 extern char *log_dir;
2490
2491
2492@@ -173,6 +174,9 @@
2493 { 0, "user", N_("start in user mode (as used for user sessions)"),
2494 NULL, NULL, &user_mode, NULL },
2495
2496+ { 0, "write-state-file", N_("attempt to write state file on every re-exec"),
2497+ NULL, NULL, &write_state_file, NULL },
2498+
2499 /* Ignore invalid options */
2500 { '-', "--", NULL, NULL, NULL, NULL, NULL },
2501
2502
2503=== modified file 'init/paths.h'
2504--- init/paths.h 2013-01-25 10:08:19 +0000
2505+++ init/paths.h 2013-06-05 16:21:24 +0000
2506@@ -97,6 +97,15 @@
2507 #endif
2508
2509 /**
2510+ * STATE_FILE_ENV:
2511+ *
2512+ * If set, write STATE_FILE on any re-exec.
2513+ **/
2514+#ifndef STATE_FILE_ENV
2515+#define STATE_FILE_ENV "UPSTART_WRITE_STATEFILE"
2516+#endif
2517+
2518+/**
2519 * INIT_XDG_SUBDIR:
2520 *
2521 * This is the name of the sub folder we will use when constructing
2522
2523=== modified file 'init/state.c'
2524--- init/state.c 2013-02-28 16:40:36 +0000
2525+++ init/state.c 2013-06-05 16:21:24 +0000
2526@@ -47,6 +47,7 @@
2527 json_object *json_sessions = NULL;
2528 json_object *json_events = NULL;
2529 json_object *json_classes = NULL;
2530+json_object *json_conf_sources = NULL;
2531
2532 extern char *log_dir;
2533
2534@@ -67,6 +68,13 @@
2535 **/
2536 int restart = FALSE;
2537
2538+/**
2539+ * write_state_file:
2540+ *
2541+ * If TRUE, write STATE_FILE on every re-exec.
2542+ **/
2543+int write_state_file = FALSE;
2544+
2545 /* Prototypes for static functions */
2546 static JobClass *
2547 state_index_to_job_class (int job_class_index)
2548@@ -233,6 +241,9 @@
2549 if (state_from_string (buffer->buf) < 0)
2550 goto error;
2551
2552+ if (write_state_file || getenv (STATE_FILE_ENV))
2553+ state_write_file (buffer);
2554+
2555 return 0;
2556
2557 error:
2558@@ -327,8 +338,8 @@
2559 int
2560 state_to_string (char **json_string, size_t *len)
2561 {
2562- json_object *json;
2563- const char *value;
2564+ json_object *json;
2565+ const char *value;
2566
2567 nih_assert (json_string);
2568 nih_assert (len);
2569@@ -339,24 +350,39 @@
2570 return -1;
2571
2572 json_sessions = session_serialise_all ();
2573- if (! json_sessions)
2574+ if (! json_sessions) {
2575+ nih_error ("%s Sessions", _("Failed to serialise"));
2576 goto error;
2577+ }
2578
2579 json_object_object_add (json, "sessions", json_sessions);
2580
2581 json_events = event_serialise_all ();
2582- if (! json_events)
2583+ if (! json_events) {
2584+ nih_error ("%s Events", _("Failed to serialise"));
2585 goto error;
2586+ }
2587
2588 json_object_object_add (json, "events", json_events);
2589
2590 json_classes = job_class_serialise_all ();
2591
2592- if (! json_classes)
2593+ if (! json_classes) {
2594+ nih_error ("%s JobClasses", _("Failed to serialise"));
2595 goto error;
2596+ }
2597
2598 json_object_object_add (json, "job_classes", json_classes);
2599
2600+ json_conf_sources = conf_source_serialise_all ();
2601+
2602+ if (! json_conf_sources) {
2603+ nih_error ("%s ConfSources", _("Failed to serialise"));
2604+ goto error;
2605+ }
2606+
2607+ json_object_object_add (json, "conf_sources", json_conf_sources);
2608+
2609 /* Note that the returned value is managed by json-c! */
2610 value = json_object_to_json_string (json);
2611 if (! value)
2612@@ -410,17 +436,37 @@
2613 if (! state_check_json_type (json, object))
2614 goto out;
2615
2616- if (session_deserialise_all (json) < 0)
2617- goto out;
2618-
2619- if (event_deserialise_all (json) < 0)
2620- goto out;
2621-
2622- if (job_class_deserialise_all (json) < 0)
2623- goto out;
2624-
2625- if (state_deserialise_resolve_deps (json) < 0)
2626- goto out;
2627+ if (session_deserialise_all (json) < 0) {
2628+ nih_error ("%s Sessions", _("Failed to deserialise"));
2629+ goto out;
2630+ }
2631+
2632+ if (event_deserialise_all (json) < 0) {
2633+ nih_error ("%s Events", _("Failed to deserialise"));
2634+ goto out;
2635+ }
2636+
2637+ /* Again, we cannot error here since older JSON state data did
2638+ * not encode ConfSource or ConfFile objects.
2639+ */
2640+ if (json_object_object_get (json, "conf_sources")) {
2641+ if (conf_source_deserialise_all (json) < 0) {
2642+ nih_error ("%s ConfSources", _("Failed to deserialise"));
2643+ goto out;
2644+ }
2645+ } else {
2646+ nih_warn ("%s", _("No ConfSources present in state data"));
2647+ }
2648+
2649+ if (job_class_deserialise_all (json) < 0) {
2650+ nih_error ("%s JobClasses", _("Failed to deserialise"));
2651+ goto out;
2652+ }
2653+
2654+ if (state_deserialise_resolve_deps (json) < 0) {
2655+ nih_error (_("Failed to resolve deserialisation dependencies"));
2656+ goto out;
2657+ }
2658
2659 ret = 0;
2660
2661@@ -1118,7 +1164,6 @@
2662 return json_type_null;
2663 }
2664
2665-
2666 /**
2667 * state_deserialise_resolve_deps:
2668 *
2669@@ -1135,8 +1180,9 @@
2670 {
2671 nih_assert (json);
2672
2673- /* XXX: Events, JobClasses, Jobs and DBusConnections must have
2674- * previously been deserialised before invoking this function.
2675+ /* XXX: Sessions, Events, JobClasses, Jobs and DBusConnections
2676+ * must have previously been deserialised before invoking
2677+ * this function.
2678 */
2679 nih_assert (json_sessions);
2680 nih_assert (json_events);
2681@@ -1175,14 +1221,21 @@
2682
2683 /* lookup class associated with JSON class index */
2684 class = state_index_to_job_class (i);
2685- if (! class)
2686- goto error;
2687-
2688- /* XXX: user and chroot jobs are not currently supported
2689- * due to ConfSources not currently being serialised.
2690- */
2691- if (class->session)
2692- continue;
2693+ if (! class) {
2694+ int session_index = -1;
2695+
2696+ if (state_get_json_int_var (json_class, "session", session_index)
2697+ && session_index > 0) {
2698+
2699+ /* Although ConfSources are now serialised, ignore
2700+ * JobClasses with associated user/chroot sessions to avoid
2701+ * behavioural changes for the time being.
2702+ */
2703+ continue;
2704+ } else {
2705+ goto error;
2706+ }
2707+ }
2708
2709 if (! state_get_json_var_full (json_class, "jobs", array, json_jobs))
2710 goto error;
2711@@ -1526,7 +1579,12 @@
2712
2713 blocked = NIH_MUST (blocked_new (parent, BLOCKED_EVENT, event));
2714 nih_list_add (list, &blocked->entry);
2715- event_block (blocked->event);
2716+
2717+ /* Event must already exist and should have
2718+ * blockers associated for it to have a blocked
2719+ * object pointing at it.
2720+ */
2721+ nih_assert (blocked->event->blockers);
2722 }
2723 break;
2724
2725@@ -1655,7 +1713,6 @@
2726 if (! json_blocked)
2727 goto error;
2728
2729-
2730 /* Don't error in this scenario to allow for possibility
2731 * that version of Upstart that performed the
2732 * serialisation did not correctly handle user and
2733@@ -1894,7 +1951,7 @@
2734 void
2735 stateful_reexec (void)
2736 {
2737- int fds[2] = { -1 };
2738+ int fds[2] = { -1, -1 };
2739 pid_t pid;
2740 sigset_t mask, oldmask;
2741 nih_local char *state_data = NULL;
2742
2743=== modified file 'init/state.h'
2744--- init/state.h 2013-02-27 11:46:04 +0000
2745+++ init/state.h 2013-06-05 16:21:24 +0000
2746@@ -4,7 +4,7 @@
2747 *
2748 * - XXX: Deferred work:
2749 * - handling of Upstart-in-initramfs - for this to work, it would be
2750- * necessary to serialise ConfSources along with the following:
2751+ * necessary to serialise ConfSources (done) along with the following:
2752 *
2753 * (1) inode number of source->path
2754 * (2) inode number of '/'
2755@@ -27,12 +27,6 @@
2756 * Note too that (2)+(3) are the only reliable method for Upstart to
2757 * detect that is *has* changed filesystem context.
2758 *
2759- * - Since ConfSources are NOT serialised, it is currently not possible
2760- * to support chroot jobs (because the only ConfSource
2761- * objects created are those at startup (for '/etc/init/'): any
2762- * pre-existing ConfSources with non-NULL Session objects will
2763- * be ignored).
2764- *
2765 * - parent/child timeout handling: we won't support down-grading initially.
2766 *
2767 * - dbus-connections: only be re-exec'ing post-boot so bridges won't
2768@@ -371,7 +365,7 @@
2769 *
2770 * Name of file that is written below the job log directory if the
2771 * newly re-exec'ed init instance failed to understand the JSON sent to
2772- * it by the old instance.
2773+ * it by the old instance (or if requested by STATE_FILE_ENV).
2774 *
2775 * This could happen for example if the old instance generated invalid
2776 * JSON, or JSON in an unexected format.
2777@@ -1226,6 +1220,9 @@
2778 struct rlimit *state_rlimit_deserialise (json_object *json)
2779 __attribute__ ((warn_unused_result));
2780
2781+int state_get_version (void)
2782+ __attribute__ ((warn_unused_result));
2783+
2784 extern char **args_copy;
2785 extern int restart;
2786
2787
2788=== added file 'init/tests/data/upstart-1.8+apparmor.json'
2789--- init/tests/data/upstart-1.8+apparmor.json 1970-01-01 00:00:00 +0000
2790+++ init/tests/data/upstart-1.8+apparmor.json 2013-06-05 16:21:24 +0000
2791@@ -0,0 +1,7962 @@
2792+{
2793+ "sessions" : [],
2794+ "job_classes" : [
2795+ {
2796+ "setuid" : null,
2797+ "jobs" : [
2798+ {
2799+ "trace_state" : "TRACE_NONE",
2800+ "failed" : 0,
2801+ "state" : "JOB_RUNNING",
2802+ "failed_process" : "PROCESS_INVALID",
2803+ "pid" : [
2804+ 669,
2805+ 0,
2806+ 0,
2807+ 0,
2808+ 0,
2809+ 0
2810+ ],
2811+ "start_env" : [],
2812+ "trace_forks" : 2,
2813+ "respawn_time" : 0,
2814+ "log" : [
2815+ {
2816+ "path" : null
2817+ },
2818+ {
2819+ "path" : null
2820+ },
2821+ {
2822+ "path" : null
2823+ },
2824+ {
2825+ "path" : null
2826+ },
2827+ {
2828+ "path" : null
2829+ },
2830+ {
2831+ "path" : null
2832+ }
2833+ ],
2834+ "respawn_count" : 0,
2835+ "kill_process" : "PROCESS_INVALID",
2836+ "stop_on" : "stopping dbus",
2837+ "env" : [
2838+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
2839+ "TERM=linux",
2840+ "JOB=dbus",
2841+ "INSTANCE=",
2842+ "UPSTART_EVENTS=filesystem started"
2843+ ],
2844+ "name" : "",
2845+ "path" : "/com/ubuntu/Upstart/jobs/avahi_2ddaemon/_",
2846+ "exit_status" : 0,
2847+ "goal" : "JOB_START",
2848+ "stop_env" : [],
2849+ "fds" : []
2850+ }
2851+ ],
2852+ "emits" : [],
2853+ "session" : 0,
2854+ "debug" : 0,
2855+ "export" : [],
2856+ "expect" : "EXPECT_DAEMON",
2857+ "stop_on" : "stopping dbus",
2858+ "chroot" : null,
2859+ "console" : "CONSOLE_LOG",
2860+ "kill_signal" : 15,
2861+ "name" : "avahi-daemon",
2862+ "instance" : "",
2863+ "description" : "mDNS/DNS-SD daemon",
2864+ "respawn_interval" : 5,
2865+ "process" : [
2866+ {
2867+ "script" : 1,
2868+ "command" : "opts=\"-D\"\n[ -e \"/etc/eucalyptus/avahi-daemon.conf\" ] && opts=\"${opts} -f /etc/eucalyptus/avahi-daemon.conf\"\nexec avahi-daemon ${opts}\n"
2869+ },
2870+ {
2871+ "script" : 1,
2872+ "command" : "/lib/init/apparmor-profile-load usr.sbin.avahi-daemon\n"
2873+ },
2874+ {
2875+ "script" : 0,
2876+ "command" : null
2877+ },
2878+ {
2879+ "script" : 0,
2880+ "command" : null
2881+ },
2882+ {
2883+ "script" : 0,
2884+ "command" : null
2885+ },
2886+ {
2887+ "script" : 0,
2888+ "command" : null
2889+ }
2890+ ],
2891+ "apparmor_switch" : null,
2892+ "respawn_limit" : 10,
2893+ "author" : null,
2894+ "respawn" : 1,
2895+ "nice" : -21,
2896+ "limits" : [
2897+ {
2898+ "rlim_cur" : 0,
2899+ "rlim_max" : 0
2900+ },
2901+ {
2902+ "rlim_cur" : 0,
2903+ "rlim_max" : 0
2904+ },
2905+ {
2906+ "rlim_cur" : 0,
2907+ "rlim_max" : 0
2908+ },
2909+ {
2910+ "rlim_cur" : 0,
2911+ "rlim_max" : 0
2912+ },
2913+ {
2914+ "rlim_cur" : 0,
2915+ "rlim_max" : 0
2916+ },
2917+ {
2918+ "rlim_cur" : 0,
2919+ "rlim_max" : 0
2920+ },
2921+ {
2922+ "rlim_cur" : 0,
2923+ "rlim_max" : 0
2924+ },
2925+ {
2926+ "rlim_cur" : 0,
2927+ "rlim_max" : 0
2928+ },
2929+ {
2930+ "rlim_cur" : 0,
2931+ "rlim_max" : 0
2932+ },
2933+ {
2934+ "rlim_cur" : 0,
2935+ "rlim_max" : 0
2936+ },
2937+ {
2938+ "rlim_cur" : 0,
2939+ "rlim_max" : 0
2940+ },
2941+ {
2942+ "rlim_cur" : 0,
2943+ "rlim_max" : 0
2944+ },
2945+ {
2946+ "rlim_cur" : 0,
2947+ "rlim_max" : 0
2948+ },
2949+ {
2950+ "rlim_cur" : 0,
2951+ "rlim_max" : 0
2952+ },
2953+ {
2954+ "rlim_cur" : 0,
2955+ "rlim_max" : 0
2956+ },
2957+ {
2958+ "rlim_cur" : 0,
2959+ "rlim_max" : 0
2960+ }
2961+ ],
2962+ "oom_score_adj" : 0,
2963+ "normalexit" : [],
2964+ "kill_timeout" : 5,
2965+ "usage" : null,
2966+ "env" : [],
2967+ "version" : null,
2968+ "task" : 0,
2969+ "path" : "/com/ubuntu/Upstart/jobs/avahi_2ddaemon",
2970+ "deleted" : 0,
2971+ "chdir" : null,
2972+ "start_on" : "(filesystem and started dbus)",
2973+ "umask" : 18,
2974+ "setgid" : null
2975+ },
2976+ {
2977+ "setuid" : null,
2978+ "jobs" : [
2979+ {
2980+ "trace_state" : "TRACE_NONE",
2981+ "failed" : 0,
2982+ "state" : "JOB_RUNNING",
2983+ "failed_process" : "PROCESS_INVALID",
2984+ "pid" : [
2985+ 0,
2986+ 0,
2987+ 0,
2988+ 0,
2989+ 0,
2990+ 0
2991+ ],
2992+ "start_env" : [],
2993+ "trace_forks" : 0,
2994+ "respawn_time" : 0,
2995+ "log" : [
2996+ {
2997+ "path" : null
2998+ },
2999+ {
3000+ "path" : null
3001+ },
3002+ {
3003+ "path" : null
3004+ },
3005+ {
3006+ "path" : null
3007+ },
3008+ {
3009+ "path" : null
3010+ },
3011+ {
3012+ "path" : null
3013+ }
3014+ ],
3015+ "respawn_count" : 0,
3016+ "kill_process" : "PROCESS_INVALID",
3017+ "env" : [
3018+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
3019+ "TERM=linux",
3020+ "UPSTART_EVENTS=virtual-filesystems"
3021+ ],
3022+ "name" : "",
3023+ "path" : "/com/ubuntu/Upstart/jobs/mountnfs_2dbootclean_2esh/_",
3024+ "exit_status" : 0,
3025+ "goal" : "JOB_START",
3026+ "stop_env" : [],
3027+ "fds" : []
3028+ }
3029+ ],
3030+ "emits" : [],
3031+ "session" : 0,
3032+ "debug" : 0,
3033+ "export" : [],
3034+ "expect" : "EXPECT_NONE",
3035+ "chroot" : null,
3036+ "console" : "CONSOLE_LOG",
3037+ "kill_signal" : 15,
3038+ "name" : "mountnfs-bootclean.sh",
3039+ "instance" : "",
3040+ "description" : null,
3041+ "respawn_interval" : 5,
3042+ "process" : [
3043+ {
3044+ "script" : 0,
3045+ "command" : null
3046+ },
3047+ {
3048+ "script" : 0,
3049+ "command" : null
3050+ },
3051+ {
3052+ "script" : 0,
3053+ "command" : null
3054+ },
3055+ {
3056+ "script" : 0,
3057+ "command" : null
3058+ },
3059+ {
3060+ "script" : 0,
3061+ "command" : null
3062+ },
3063+ {
3064+ "script" : 0,
3065+ "command" : null
3066+ }
3067+ ],
3068+ "apparmor_switch" : null,
3069+ "respawn_limit" : 10,
3070+ "author" : null,
3071+ "respawn" : 0,
3072+ "nice" : -21,
3073+ "limits" : [
3074+ {
3075+ "rlim_cur" : 0,
3076+ "rlim_max" : 0
3077+ },
3078+ {
3079+ "rlim_cur" : 0,
3080+ "rlim_max" : 0
3081+ },
3082+ {
3083+ "rlim_cur" : 0,
3084+ "rlim_max" : 0
3085+ },
3086+ {
3087+ "rlim_cur" : 0,
3088+ "rlim_max" : 0
3089+ },
3090+ {
3091+ "rlim_cur" : 0,
3092+ "rlim_max" : 0
3093+ },
3094+ {
3095+ "rlim_cur" : 0,
3096+ "rlim_max" : 0
3097+ },
3098+ {
3099+ "rlim_cur" : 0,
3100+ "rlim_max" : 0
3101+ },
3102+ {
3103+ "rlim_cur" : 0,
3104+ "rlim_max" : 0
3105+ },
3106+ {
3107+ "rlim_cur" : 0,
3108+ "rlim_max" : 0
3109+ },
3110+ {
3111+ "rlim_cur" : 0,
3112+ "rlim_max" : 0
3113+ },
3114+ {
3115+ "rlim_cur" : 0,
3116+ "rlim_max" : 0
3117+ },
3118+ {
3119+ "rlim_cur" : 0,
3120+ "rlim_max" : 0
3121+ },
3122+ {
3123+ "rlim_cur" : 0,
3124+ "rlim_max" : 0
3125+ },
3126+ {
3127+ "rlim_cur" : 0,
3128+ "rlim_max" : 0
3129+ },
3130+ {
3131+ "rlim_cur" : 0,
3132+ "rlim_max" : 0
3133+ },
3134+ {
3135+ "rlim_cur" : 0,
3136+ "rlim_max" : 0
3137+ }
3138+ ],
3139+ "oom_score_adj" : 0,
3140+ "normalexit" : [],
3141+ "kill_timeout" : 5,
3142+ "usage" : null,
3143+ "env" : [],
3144+ "version" : null,
3145+ "task" : 0,
3146+ "path" : "/com/ubuntu/Upstart/jobs/mountnfs_2dbootclean_2esh",
3147+ "deleted" : 0,
3148+ "chdir" : null,
3149+ "start_on" : "virtual-filesystems",
3150+ "umask" : 18,
3151+ "setgid" : null
3152+ },
3153+ {
3154+ "setuid" : null,
3155+ "jobs" : [
3156+ {
3157+ "trace_state" : "TRACE_NONE",
3158+ "failed" : 0,
3159+ "state" : "JOB_RUNNING",
3160+ "failed_process" : "PROCESS_INVALID",
3161+ "pid" : [
3162+ 659,
3163+ 0,
3164+ 0,
3165+ 0,
3166+ 0,
3167+ 0
3168+ ],
3169+ "start_env" : [],
3170+ "trace_forks" : 1,
3171+ "respawn_time" : 0,
3172+ "log" : [
3173+ {
3174+ "path" : null
3175+ },
3176+ {
3177+ "path" : null
3178+ },
3179+ {
3180+ "path" : null
3181+ },
3182+ {
3183+ "path" : null
3184+ },
3185+ {
3186+ "path" : null
3187+ },
3188+ {
3189+ "path" : null
3190+ }
3191+ ],
3192+ "respawn_count" : 0,
3193+ "kill_process" : "PROCESS_INVALID",
3194+ "stop_on" : "runlevel [06]",
3195+ "env" : [
3196+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
3197+ "TERM=linux",
3198+ "UPSTART_EVENTS=filesystem"
3199+ ],
3200+ "name" : "",
3201+ "path" : "/com/ubuntu/Upstart/jobs/rsyslog/_",
3202+ "exit_status" : 0,
3203+ "goal" : "JOB_START",
3204+ "stop_env" : [],
3205+ "fds" : []
3206+ }
3207+ ],
3208+ "emits" : [],
3209+ "session" : 0,
3210+ "debug" : 0,
3211+ "export" : [],
3212+ "expect" : "EXPECT_FORK",
3213+ "stop_on" : "runlevel [06]",
3214+ "chroot" : null,
3215+ "console" : "CONSOLE_LOG",
3216+ "kill_signal" : 15,
3217+ "name" : "rsyslog",
3218+ "instance" : "",
3219+ "description" : "system logging daemon",
3220+ "respawn_interval" : 5,
3221+ "process" : [
3222+ {
3223+ "script" : 1,
3224+ "command" : ". /etc/default/rsyslog\nexec rsyslogd $RSYSLOGD_OPTIONS\n"
3225+ },
3226+ {
3227+ "script" : 1,
3228+ "command" : "/lib/init/apparmor-profile-load usr.sbin.rsyslogd\n"
3229+ },
3230+ {
3231+ "script" : 0,
3232+ "command" : null
3233+ },
3234+ {
3235+ "script" : 0,
3236+ "command" : null
3237+ },
3238+ {
3239+ "script" : 0,
3240+ "command" : null
3241+ },
3242+ {
3243+ "script" : 0,
3244+ "command" : null
3245+ }
3246+ ],
3247+ "apparmor_switch" : null,
3248+ "respawn_limit" : 10,
3249+ "author" : null,
3250+ "respawn" : 1,
3251+ "nice" : -21,
3252+ "limits" : [
3253+ {
3254+ "rlim_cur" : 0,
3255+ "rlim_max" : 0
3256+ },
3257+ {
3258+ "rlim_cur" : 0,
3259+ "rlim_max" : 0
3260+ },
3261+ {
3262+ "rlim_cur" : 0,
3263+ "rlim_max" : 0
3264+ },
3265+ {
3266+ "rlim_cur" : 0,
3267+ "rlim_max" : 0
3268+ },
3269+ {
3270+ "rlim_cur" : 0,
3271+ "rlim_max" : 0
3272+ },
3273+ {
3274+ "rlim_cur" : 0,
3275+ "rlim_max" : 0
3276+ },
3277+ {
3278+ "rlim_cur" : 0,
3279+ "rlim_max" : 0
3280+ },
3281+ {
3282+ "rlim_cur" : 0,
3283+ "rlim_max" : 0
3284+ },
3285+ {
3286+ "rlim_cur" : 0,
3287+ "rlim_max" : 0
3288+ },
3289+ {
3290+ "rlim_cur" : 0,
3291+ "rlim_max" : 0
3292+ },
3293+ {
3294+ "rlim_cur" : 0,
3295+ "rlim_max" : 0
3296+ },
3297+ {
3298+ "rlim_cur" : 0,
3299+ "rlim_max" : 0
3300+ },
3301+ {
3302+ "rlim_cur" : 0,
3303+ "rlim_max" : 0
3304+ },
3305+ {
3306+ "rlim_cur" : 0,
3307+ "rlim_max" : 0
3308+ },
3309+ {
3310+ "rlim_cur" : 0,
3311+ "rlim_max" : 0
3312+ },
3313+ {
3314+ "rlim_cur" : 0,
3315+ "rlim_max" : 0
3316+ }
3317+ ],
3318+ "oom_score_adj" : 0,
3319+ "normalexit" : [],
3320+ "kill_timeout" : 5,
3321+ "usage" : null,
3322+ "env" : [],
3323+ "version" : null,
3324+ "task" : 0,
3325+ "path" : "/com/ubuntu/Upstart/jobs/rsyslog",
3326+ "deleted" : 0,
3327+ "chdir" : null,
3328+ "start_on" : "filesystem",
3329+ "umask" : 18,
3330+ "setgid" : null
3331+ },
3332+ {
3333+ "setuid" : null,
3334+ "jobs" : [
3335+ {
3336+ "trace_state" : "TRACE_NONE",
3337+ "failed" : 0,
3338+ "state" : "JOB_RUNNING",
3339+ "failed_process" : "PROCESS_INVALID",
3340+ "pid" : [
3341+ 1023,
3342+ 0,
3343+ 0,
3344+ 0,
3345+ 0,
3346+ 0
3347+ ],
3348+ "start_env" : [],
3349+ "trace_forks" : 0,
3350+ "respawn_time" : 0,
3351+ "log" : [
3352+ {
3353+ "path" : null
3354+ },
3355+ {
3356+ "path" : null
3357+ },
3358+ {
3359+ "path" : null
3360+ },
3361+ {
3362+ "path" : null
3363+ },
3364+ {
3365+ "path" : null
3366+ },
3367+ {
3368+ "path" : null
3369+ }
3370+ ],
3371+ "respawn_count" : 0,
3372+ "kill_process" : "PROCESS_INVALID",
3373+ "stop_on" : "runlevel [!23]",
3374+ "env" : [
3375+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
3376+ "TERM=linux",
3377+ "RUNLEVEL=2",
3378+ "PREVLEVEL=N",
3379+ "UPSTART_EVENTS=runlevel not-container"
3380+ ],
3381+ "name" : "",
3382+ "path" : "/com/ubuntu/Upstart/jobs/tty4/_",
3383+ "exit_status" : 0,
3384+ "goal" : "JOB_START",
3385+ "stop_env" : [],
3386+ "fds" : []
3387+ }
3388+ ],
3389+ "emits" : [],
3390+ "session" : 0,
3391+ "debug" : 0,
3392+ "export" : [],
3393+ "expect" : "EXPECT_NONE",
3394+ "stop_on" : "runlevel [!23]",
3395+ "chroot" : null,
3396+ "console" : "CONSOLE_LOG",
3397+ "kill_signal" : 15,
3398+ "name" : "tty4",
3399+ "instance" : "",
3400+ "description" : null,
3401+ "respawn_interval" : 5,
3402+ "process" : [
3403+ {
3404+ "script" : 0,
3405+ "command" : "/sbin/getty -8 38400 tty4"
3406+ },
3407+ {
3408+ "script" : 0,
3409+ "command" : null
3410+ },
3411+ {
3412+ "script" : 0,
3413+ "command" : null
3414+ },
3415+ {
3416+ "script" : 0,
3417+ "command" : null
3418+ },
3419+ {
3420+ "script" : 0,
3421+ "command" : null
3422+ },
3423+ {
3424+ "script" : 0,
3425+ "command" : null
3426+ }
3427+ ],
3428+ "apparmor_switch" : null,
3429+ "respawn_limit" : 10,
3430+ "author" : null,
3431+ "respawn" : 1,
3432+ "nice" : -21,
3433+ "limits" : [
3434+ {
3435+ "rlim_cur" : 0,
3436+ "rlim_max" : 0
3437+ },
3438+ {
3439+ "rlim_cur" : 0,
3440+ "rlim_max" : 0
3441+ },
3442+ {
3443+ "rlim_cur" : 0,
3444+ "rlim_max" : 0
3445+ },
3446+ {
3447+ "rlim_cur" : 0,
3448+ "rlim_max" : 0
3449+ },
3450+ {
3451+ "rlim_cur" : 0,
3452+ "rlim_max" : 0
3453+ },
3454+ {
3455+ "rlim_cur" : 0,
3456+ "rlim_max" : 0
3457+ },
3458+ {
3459+ "rlim_cur" : 0,
3460+ "rlim_max" : 0
3461+ },
3462+ {
3463+ "rlim_cur" : 0,
3464+ "rlim_max" : 0
3465+ },
3466+ {
3467+ "rlim_cur" : 0,
3468+ "rlim_max" : 0
3469+ },
3470+ {
3471+ "rlim_cur" : 0,
3472+ "rlim_max" : 0
3473+ },
3474+ {
3475+ "rlim_cur" : 0,
3476+ "rlim_max" : 0
3477+ },
3478+ {
3479+ "rlim_cur" : 0,
3480+ "rlim_max" : 0
3481+ },
3482+ {
3483+ "rlim_cur" : 0,
3484+ "rlim_max" : 0
3485+ },
3486+ {
3487+ "rlim_cur" : 0,
3488+ "rlim_max" : 0
3489+ },
3490+ {
3491+ "rlim_cur" : 0,
3492+ "rlim_max" : 0
3493+ },
3494+ {
3495+ "rlim_cur" : 0,
3496+ "rlim_max" : 0
3497+ }
3498+ ],
3499+ "oom_score_adj" : 0,
3500+ "normalexit" : [],
3501+ "kill_timeout" : 5,
3502+ "usage" : null,
3503+ "env" : [],
3504+ "version" : null,
3505+ "task" : 0,
3506+ "path" : "/com/ubuntu/Upstart/jobs/tty4",
3507+ "deleted" : 0,
3508+ "chdir" : null,
3509+ "start_on" : "(runlevel [23] and ((not-container or container CONTAINER=lxc) or container CONTAINER=lxc-libvirt))",
3510+ "umask" : 18,
3511+ "setgid" : null
3512+ },
3513+ {
3514+ "setuid" : null,
3515+ "jobs" : [
3516+ {
3517+ "trace_state" : "TRACE_NONE",
3518+ "failed" : 0,
3519+ "state" : "JOB_RUNNING",
3520+ "failed_process" : "PROCESS_INVALID",
3521+ "pid" : [
3522+ 562,
3523+ 0,
3524+ 0,
3525+ 0,
3526+ 0,
3527+ 0
3528+ ],
3529+ "start_env" : [],
3530+ "trace_forks" : 1,
3531+ "respawn_time" : 0,
3532+ "log" : [
3533+ {
3534+ "path" : null
3535+ },
3536+ {
3537+ "path" : null
3538+ },
3539+ {
3540+ "path" : null
3541+ },
3542+ {
3543+ "path" : null
3544+ },
3545+ {
3546+ "path" : null
3547+ },
3548+ {
3549+ "path" : null
3550+ }
3551+ ],
3552+ "respawn_count" : 0,
3553+ "kill_process" : "PROCESS_INVALID",
3554+ "stop_on" : "runlevel [06]",
3555+ "env" : [
3556+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
3557+ "TERM=linux",
3558+ "UPSTART_EVENTS=virtual-filesystems"
3559+ ],
3560+ "name" : "",
3561+ "path" : "/com/ubuntu/Upstart/jobs/udev/_",
3562+ "exit_status" : 0,
3563+ "goal" : "JOB_START",
3564+ "stop_env" : [],
3565+ "fds" : []
3566+ }
3567+ ],
3568+ "emits" : [],
3569+ "session" : 0,
3570+ "debug" : 0,
3571+ "export" : [],
3572+ "expect" : "EXPECT_FORK",
3573+ "stop_on" : "runlevel [06]",
3574+ "chroot" : null,
3575+ "console" : "CONSOLE_LOG",
3576+ "kill_signal" : 15,
3577+ "name" : "udev",
3578+ "instance" : "",
3579+ "description" : "device node and kernel event manager",
3580+ "respawn_interval" : 5,
3581+ "process" : [
3582+ {
3583+ "script" : 0,
3584+ "command" : "/lib/systemd/systemd-udevd --daemon"
3585+ },
3586+ {
3587+ "script" : 0,
3588+ "command" : null
3589+ },
3590+ {
3591+ "script" : 0,
3592+ "command" : null
3593+ },
3594+ {
3595+ "script" : 0,
3596+ "command" : null
3597+ },
3598+ {
3599+ "script" : 0,
3600+ "command" : null
3601+ },
3602+ {
3603+ "script" : 0,
3604+ "command" : null
3605+ }
3606+ ],
3607+ "apparmor_switch" : null,
3608+ "respawn_limit" : 10,
3609+ "author" : null,
3610+ "respawn" : 1,
3611+ "nice" : -21,
3612+ "limits" : [
3613+ {
3614+ "rlim_cur" : 0,
3615+ "rlim_max" : 0
3616+ },
3617+ {
3618+ "rlim_cur" : 0,
3619+ "rlim_max" : 0
3620+ },
3621+ {
3622+ "rlim_cur" : 0,
3623+ "rlim_max" : 0
3624+ },
3625+ {
3626+ "rlim_cur" : 0,
3627+ "rlim_max" : 0
3628+ },
3629+ {
3630+ "rlim_cur" : 0,
3631+ "rlim_max" : 0
3632+ },
3633+ {
3634+ "rlim_cur" : 0,
3635+ "rlim_max" : 0
3636+ },
3637+ {
3638+ "rlim_cur" : 0,
3639+ "rlim_max" : 0
3640+ },
3641+ {
3642+ "rlim_cur" : 0,
3643+ "rlim_max" : 0
3644+ },
3645+ {
3646+ "rlim_cur" : 0,
3647+ "rlim_max" : 0
3648+ },
3649+ {
3650+ "rlim_cur" : 0,
3651+ "rlim_max" : 0
3652+ },
3653+ {
3654+ "rlim_cur" : 0,
3655+ "rlim_max" : 0
3656+ },
3657+ {
3658+ "rlim_cur" : 0,
3659+ "rlim_max" : 0
3660+ },
3661+ {
3662+ "rlim_cur" : 0,
3663+ "rlim_max" : 0
3664+ },
3665+ {
3666+ "rlim_cur" : 0,
3667+ "rlim_max" : 0
3668+ },
3669+ {
3670+ "rlim_cur" : 0,
3671+ "rlim_max" : 0
3672+ },
3673+ {
3674+ "rlim_cur" : 0,
3675+ "rlim_max" : 0
3676+ }
3677+ ],
3678+ "oom_score_adj" : 0,
3679+ "normalexit" : [],
3680+ "kill_timeout" : 5,
3681+ "usage" : null,
3682+ "env" : [],
3683+ "version" : null,
3684+ "task" : 0,
3685+ "path" : "/com/ubuntu/Upstart/jobs/udev",
3686+ "deleted" : 0,
3687+ "chdir" : null,
3688+ "start_on" : "virtual-filesystems",
3689+ "umask" : 18,
3690+ "setgid" : null
3691+ },
3692+ {
3693+ "setuid" : null,
3694+ "jobs" : [
3695+ {
3696+ "trace_state" : "TRACE_NONE",
3697+ "failed" : 0,
3698+ "state" : "JOB_RUNNING",
3699+ "failed_process" : "PROCESS_INVALID",
3700+ "pid" : [
3701+ 545,
3702+ 0,
3703+ 0,
3704+ 0,
3705+ 0,
3706+ 0
3707+ ],
3708+ "start_env" : [],
3709+ "trace_forks" : 2,
3710+ "respawn_time" : 0,
3711+ "log" : [
3712+ {
3713+ "path" : null
3714+ },
3715+ {
3716+ "path" : null
3717+ },
3718+ {
3719+ "path" : null
3720+ },
3721+ {
3722+ "path" : null
3723+ },
3724+ {
3725+ "path" : null
3726+ },
3727+ {
3728+ "path" : null
3729+ }
3730+ ],
3731+ "respawn_count" : 0,
3732+ "kill_process" : "PROCESS_INVALID",
3733+ "stop_on" : "stopped udev",
3734+ "env" : [
3735+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
3736+ "TERM=linux",
3737+ "JOB=udev",
3738+ "INSTANCE=",
3739+ "UPSTART_EVENTS=starting"
3740+ ],
3741+ "name" : "",
3742+ "path" : "/com/ubuntu/Upstart/jobs/upstart_2dudev_2dbridge/_",
3743+ "exit_status" : 0,
3744+ "goal" : "JOB_START",
3745+ "stop_env" : [],
3746+ "fds" : []
3747+ }
3748+ ],
3749+ "emits" : [
3750+ "*-device-added",
3751+ "*-device-removed",
3752+ "*-device-changed",
3753+ "*-device-online",
3754+ "*-device-offline"
3755+ ],
3756+ "session" : 0,
3757+ "debug" : 0,
3758+ "export" : [],
3759+ "expect" : "EXPECT_DAEMON",
3760+ "stop_on" : "stopped udev",
3761+ "chroot" : null,
3762+ "console" : "CONSOLE_LOG",
3763+ "kill_signal" : 15,
3764+ "name" : "upstart-udev-bridge",
3765+ "instance" : "",
3766+ "description" : "Bridge udev events into upstart",
3767+ "respawn_interval" : 5,
3768+ "process" : [
3769+ {
3770+ "script" : 0,
3771+ "command" : "upstart-udev-bridge --daemon"
3772+ },
3773+ {
3774+ "script" : 0,
3775+ "command" : null
3776+ },
3777+ {
3778+ "script" : 0,
3779+ "command" : null
3780+ },
3781+ {
3782+ "script" : 0,
3783+ "command" : null
3784+ },
3785+ {
3786+ "script" : 0,
3787+ "command" : null
3788+ },
3789+ {
3790+ "script" : 0,
3791+ "command" : null
3792+ }
3793+ ],
3794+ "apparmor_switch" : null,
3795+ "respawn_limit" : 10,
3796+ "author" : null,
3797+ "respawn" : 1,
3798+ "nice" : -21,
3799+ "limits" : [
3800+ {
3801+ "rlim_cur" : 0,
3802+ "rlim_max" : 0
3803+ },
3804+ {
3805+ "rlim_cur" : 0,
3806+ "rlim_max" : 0
3807+ },
3808+ {
3809+ "rlim_cur" : 0,
3810+ "rlim_max" : 0
3811+ },
3812+ {
3813+ "rlim_cur" : 0,
3814+ "rlim_max" : 0
3815+ },
3816+ {
3817+ "rlim_cur" : 0,
3818+ "rlim_max" : 0
3819+ },
3820+ {
3821+ "rlim_cur" : 0,
3822+ "rlim_max" : 0
3823+ },
3824+ {
3825+ "rlim_cur" : 0,
3826+ "rlim_max" : 0
3827+ },
3828+ {
3829+ "rlim_cur" : 0,
3830+ "rlim_max" : 0
3831+ },
3832+ {
3833+ "rlim_cur" : 0,
3834+ "rlim_max" : 0
3835+ },
3836+ {
3837+ "rlim_cur" : 0,
3838+ "rlim_max" : 0
3839+ },
3840+ {
3841+ "rlim_cur" : 0,
3842+ "rlim_max" : 0
3843+ },
3844+ {
3845+ "rlim_cur" : 0,
3846+ "rlim_max" : 0
3847+ },
3848+ {
3849+ "rlim_cur" : 0,
3850+ "rlim_max" : 0
3851+ },
3852+ {
3853+ "rlim_cur" : 0,
3854+ "rlim_max" : 0
3855+ },
3856+ {
3857+ "rlim_cur" : 0,
3858+ "rlim_max" : 0
3859+ },
3860+ {
3861+ "rlim_cur" : 0,
3862+ "rlim_max" : 0
3863+ }
3864+ ],
3865+ "oom_score_adj" : 0,
3866+ "normalexit" : [],
3867+ "kill_timeout" : 5,
3868+ "usage" : null,
3869+ "env" : [],
3870+ "version" : null,
3871+ "task" : 0,
3872+ "path" : "/com/ubuntu/Upstart/jobs/upstart_2dudev_2dbridge",
3873+ "deleted" : 0,
3874+ "chdir" : null,
3875+ "start_on" : "starting udev",
3876+ "umask" : 18,
3877+ "setgid" : null
3878+ },
3879+ {
3880+ "setuid" : null,
3881+ "jobs" : [
3882+ {
3883+ "trace_state" : "TRACE_NONE",
3884+ "failed" : 0,
3885+ "state" : "JOB_RUNNING",
3886+ "failed_process" : "PROCESS_INVALID",
3887+ "pid" : [
3888+ 1143,
3889+ 0,
3890+ 0,
3891+ 0,
3892+ 0,
3893+ 0
3894+ ],
3895+ "start_env" : [],
3896+ "trace_forks" : 1,
3897+ "respawn_time" : 0,
3898+ "log" : [
3899+ {
3900+ "path" : null
3901+ },
3902+ {
3903+ "path" : null
3904+ },
3905+ {
3906+ "path" : null
3907+ },
3908+ {
3909+ "path" : null
3910+ },
3911+ {
3912+ "path" : null
3913+ },
3914+ {
3915+ "path" : null
3916+ }
3917+ ],
3918+ "respawn_count" : 0,
3919+ "kill_process" : "PROCESS_INVALID",
3920+ "stop_on" : "runlevel [!2345]",
3921+ "env" : [
3922+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
3923+ "TERM=linux",
3924+ "CRASH_DB_URL=https://daisy.ubuntu.com",
3925+ "RUNLEVEL=2",
3926+ "PREVLEVEL=N",
3927+ "UPSTART_EVENTS=runlevel"
3928+ ],
3929+ "name" : "",
3930+ "path" : "/com/ubuntu/Upstart/jobs/whoopsie/_",
3931+ "exit_status" : 0,
3932+ "goal" : "JOB_START",
3933+ "stop_env" : [],
3934+ "fds" : []
3935+ }
3936+ ],
3937+ "emits" : [],
3938+ "session" : 0,
3939+ "debug" : 0,
3940+ "export" : [],
3941+ "expect" : "EXPECT_FORK",
3942+ "stop_on" : "runlevel [!2345]",
3943+ "chroot" : null,
3944+ "console" : "CONSOLE_LOG",
3945+ "kill_signal" : 15,
3946+ "name" : "whoopsie",
3947+ "instance" : "",
3948+ "description" : "crash report submission daemon",
3949+ "respawn_interval" : 5,
3950+ "process" : [
3951+ {
3952+ "script" : 0,
3953+ "command" : "whoopsie"
3954+ },
3955+ {
3956+ "script" : 1,
3957+ "command" : " [ -x /usr/bin/ubiquity-dm ] && { stop; exit 0; }\n\n if ! grep report_crashes=true /etc/default/whoopsie -sqi; then\n stop; exit 0\n fi\n"
3958+ },
3959+ {
3960+ "script" : 0,
3961+ "command" : null
3962+ },
3963+ {
3964+ "script" : 0,
3965+ "command" : null
3966+ },
3967+ {
3968+ "script" : 0,
3969+ "command" : null
3970+ },
3971+ {
3972+ "script" : 0,
3973+ "command" : null
3974+ }
3975+ ],
3976+ "apparmor_switch" : null,
3977+ "respawn_limit" : 10,
3978+ "author" : null,
3979+ "respawn" : 1,
3980+ "nice" : -21,
3981+ "limits" : [
3982+ {
3983+ "rlim_cur" : 0,
3984+ "rlim_max" : 0
3985+ },
3986+ {
3987+ "rlim_cur" : 0,
3988+ "rlim_max" : 0
3989+ },
3990+ {
3991+ "rlim_cur" : 0,
3992+ "rlim_max" : 0
3993+ },
3994+ {
3995+ "rlim_cur" : 0,
3996+ "rlim_max" : 0
3997+ },
3998+ {
3999+ "rlim_cur" : 0,
4000+ "rlim_max" : 0
4001+ },
4002+ {
4003+ "rlim_cur" : 0,
4004+ "rlim_max" : 0
4005+ },
4006+ {
4007+ "rlim_cur" : 0,
4008+ "rlim_max" : 0
4009+ },
4010+ {
4011+ "rlim_cur" : 0,
4012+ "rlim_max" : 0
4013+ },
4014+ {
4015+ "rlim_cur" : 0,
4016+ "rlim_max" : 0
4017+ },
4018+ {
4019+ "rlim_cur" : 0,
4020+ "rlim_max" : 0
4021+ },
4022+ {
4023+ "rlim_cur" : 0,
4024+ "rlim_max" : 0
4025+ },
4026+ {
4027+ "rlim_cur" : 0,
4028+ "rlim_max" : 0
4029+ },
4030+ {
4031+ "rlim_cur" : 0,
4032+ "rlim_max" : 0
4033+ },
4034+ {
4035+ "rlim_cur" : 0,
4036+ "rlim_max" : 0
4037+ },
4038+ {
4039+ "rlim_cur" : 0,
4040+ "rlim_max" : 0
4041+ },
4042+ {
4043+ "rlim_cur" : 0,
4044+ "rlim_max" : 0
4045+ }
4046+ ],
4047+ "oom_score_adj" : 0,
4048+ "normalexit" : [],
4049+ "kill_timeout" : 5,
4050+ "usage" : null,
4051+ "env" : [
4052+ "CRASH_DB_URL=https://daisy.ubuntu.com"
4053+ ],
4054+ "version" : null,
4055+ "task" : 0,
4056+ "path" : "/com/ubuntu/Upstart/jobs/whoopsie",
4057+ "deleted" : 0,
4058+ "chdir" : null,
4059+ "start_on" : "runlevel [2345]",
4060+ "umask" : 18,
4061+ "setgid" : null
4062+ },
4063+ {
4064+ "setuid" : null,
4065+ "jobs" : [
4066+ {
4067+ "trace_state" : "TRACE_NONE",
4068+ "failed" : 0,
4069+ "state" : "JOB_RUNNING",
4070+ "failed_process" : "PROCESS_INVALID",
4071+ "pid" : [
4072+ 0,
4073+ 0,
4074+ 0,
4075+ 0,
4076+ 0,
4077+ 0
4078+ ],
4079+ "start_env" : [],
4080+ "trace_forks" : 0,
4081+ "respawn_time" : 0,
4082+ "log" : [
4083+ {
4084+ "path" : null
4085+ },
4086+ {
4087+ "path" : null
4088+ },
4089+ {
4090+ "path" : null
4091+ },
4092+ {
4093+ "path" : null
4094+ },
4095+ {
4096+ "path" : null
4097+ },
4098+ {
4099+ "path" : null
4100+ }
4101+ ],
4102+ "respawn_count" : 0,
4103+ "kill_process" : "PROCESS_INVALID",
4104+ "stop_on" : "runlevel [!2345]",
4105+ "env" : [
4106+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
4107+ "TERM=linux",
4108+ "enabled=1",
4109+ "RUNLEVEL=2",
4110+ "PREVLEVEL=N",
4111+ "UPSTART_EVENTS=runlevel"
4112+ ],
4113+ "name" : "",
4114+ "path" : "/com/ubuntu/Upstart/jobs/apport/_",
4115+ "exit_status" : 0,
4116+ "goal" : "JOB_START",
4117+ "stop_env" : [],
4118+ "fds" : []
4119+ }
4120+ ],
4121+ "emits" : [],
4122+ "session" : 0,
4123+ "debug" : 0,
4124+ "export" : [],
4125+ "expect" : "EXPECT_NONE",
4126+ "stop_on" : "runlevel [!2345]",
4127+ "chroot" : null,
4128+ "console" : "CONSOLE_LOG",
4129+ "kill_signal" : 15,
4130+ "name" : "apport",
4131+ "instance" : "",
4132+ "description" : "automatic crash report generation",
4133+ "respawn_interval" : 5,
4134+ "process" : [
4135+ {
4136+ "script" : 0,
4137+ "command" : null
4138+ },
4139+ {
4140+ "script" : 1,
4141+ "command" : " . /etc/default/apport\n [ \"$enabled\" = \"1\" ] || [ \"$force_start\" = \"1\" ] || exit 0\n\n mkdir -p -m 1777 /var/crash\n\n # check for kernel crash dump, convert it to apport report\n if [ -e /var/crash/vmcore ] || [ -n \"`ls /var/crash | egrep ^[0-9]{12}$`\" ]\n then\n\t/usr/share/apport/kernel_crashdump || true\n fi\n\n # check for incomplete suspend/resume or hibernate\n if [ -e /var/lib/pm-utils/status ]\n then\n /usr/share/apport/apportcheckresume || true\n rm -f /var/lib/pm-utils/status\n rm -f /var/lib/pm-utils/resume-hang.log\n fi\n\n echo \"|/usr/share/apport/apport %p %s %c\" > /proc/sys/kernel/core_pattern\n"
4142+ },
4143+ {
4144+ "script" : 0,
4145+ "command" : null
4146+ },
4147+ {
4148+ "script" : 0,
4149+ "command" : null
4150+ },
4151+ {
4152+ "script" : 1,
4153+ "command" : " # Check for a hung resume. If we find one try and grab everything\n # we can to aid in its discovery\n if [ -e /var/lib/pm-utils/status ]\n then\n\tps -wwef > /var/lib/pm-utils/resume-hang.log\n fi\n\n if [ \"`dd if=/proc/sys/kernel/core_pattern count=1 bs=1 2>/dev/null`\" != \"|\" ]\n then\n\texit 1\n else\n\techo \"core\" > /proc/sys/kernel/core_pattern\n fi\n"
4154+ },
4155+ {
4156+ "script" : 0,
4157+ "command" : null
4158+ }
4159+ ],
4160+ "apparmor_switch" : null,
4161+ "respawn_limit" : 10,
4162+ "author" : null,
4163+ "respawn" : 0,
4164+ "nice" : -21,
4165+ "limits" : [
4166+ {
4167+ "rlim_cur" : 0,
4168+ "rlim_max" : 0
4169+ },
4170+ {
4171+ "rlim_cur" : 0,
4172+ "rlim_max" : 0
4173+ },
4174+ {
4175+ "rlim_cur" : 0,
4176+ "rlim_max" : 0
4177+ },
4178+ {
4179+ "rlim_cur" : 0,
4180+ "rlim_max" : 0
4181+ },
4182+ {
4183+ "rlim_cur" : 0,
4184+ "rlim_max" : 0
4185+ },
4186+ {
4187+ "rlim_cur" : 0,
4188+ "rlim_max" : 0
4189+ },
4190+ {
4191+ "rlim_cur" : 0,
4192+ "rlim_max" : 0
4193+ },
4194+ {
4195+ "rlim_cur" : 0,
4196+ "rlim_max" : 0
4197+ },
4198+ {
4199+ "rlim_cur" : 0,
4200+ "rlim_max" : 0
4201+ },
4202+ {
4203+ "rlim_cur" : 0,
4204+ "rlim_max" : 0
4205+ },
4206+ {
4207+ "rlim_cur" : 0,
4208+ "rlim_max" : 0
4209+ },
4210+ {
4211+ "rlim_cur" : 0,
4212+ "rlim_max" : 0
4213+ },
4214+ {
4215+ "rlim_cur" : 0,
4216+ "rlim_max" : 0
4217+ },
4218+ {
4219+ "rlim_cur" : 0,
4220+ "rlim_max" : 0
4221+ },
4222+ {
4223+ "rlim_cur" : 0,
4224+ "rlim_max" : 0
4225+ },
4226+ {
4227+ "rlim_cur" : 0,
4228+ "rlim_max" : 0
4229+ }
4230+ ],
4231+ "oom_score_adj" : 0,
4232+ "normalexit" : [],
4233+ "kill_timeout" : 5,
4234+ "usage" : null,
4235+ "env" : [
4236+ "enabled=1"
4237+ ],
4238+ "version" : null,
4239+ "task" : 0,
4240+ "path" : "/com/ubuntu/Upstart/jobs/apport",
4241+ "deleted" : 0,
4242+ "chdir" : null,
4243+ "start_on" : "runlevel [2345]",
4244+ "umask" : 18,
4245+ "setgid" : null
4246+ },
4247+ {
4248+ "setuid" : null,
4249+ "jobs" : [
4250+ {
4251+ "trace_state" : "TRACE_NONE",
4252+ "failed" : 0,
4253+ "state" : "JOB_RUNNING",
4254+ "failed_process" : "PROCESS_INVALID",
4255+ "pid" : [
4256+ 628,
4257+ 0,
4258+ 0,
4259+ 0,
4260+ 0,
4261+ 0
4262+ ],
4263+ "start_env" : [],
4264+ "trace_forks" : 0,
4265+ "respawn_time" : 0,
4266+ "log" : [
4267+ {
4268+ "remote_closed" : 0,
4269+ "detached" : 0,
4270+ "uid" : 0,
4271+ "open_errno" : 9,
4272+ "path" : "/var/log/upstart/systemd-logind.log",
4273+ "io_watch_fd" : 17,
4274+ "fd" : 15
4275+ },
4276+ {
4277+ "path" : null
4278+ },
4279+ {
4280+ "path" : null
4281+ },
4282+ {
4283+ "path" : null
4284+ },
4285+ {
4286+ "path" : null
4287+ },
4288+ {
4289+ "path" : null
4290+ }
4291+ ],
4292+ "respawn_count" : 0,
4293+ "kill_process" : "PROCESS_INVALID",
4294+ "stop_on" : "stopping dbus",
4295+ "env" : [
4296+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
4297+ "TERM=linux",
4298+ "SYSTEMD_LOG_TARGET=syslog",
4299+ "JOB=dbus",
4300+ "INSTANCE=",
4301+ "UPSTART_EVENTS=started"
4302+ ],
4303+ "name" : "",
4304+ "path" : "/com/ubuntu/Upstart/jobs/systemd_2dlogind/_",
4305+ "exit_status" : 0,
4306+ "goal" : "JOB_START",
4307+ "stop_env" : [],
4308+ "fds" : []
4309+ }
4310+ ],
4311+ "emits" : [],
4312+ "session" : 0,
4313+ "debug" : 0,
4314+ "export" : [],
4315+ "expect" : "EXPECT_NONE",
4316+ "stop_on" : "stopping dbus",
4317+ "chroot" : null,
4318+ "console" : "CONSOLE_LOG",
4319+ "kill_signal" : 15,
4320+ "name" : "systemd-logind",
4321+ "instance" : "",
4322+ "description" : "SystemD login management service",
4323+ "respawn_interval" : 5,
4324+ "process" : [
4325+ {
4326+ "script" : 0,
4327+ "command" : "/lib/systemd/systemd-logind"
4328+ },
4329+ {
4330+ "script" : 1,
4331+ "command" : " # only start if PAM module is actually available, not if libpam-systemd is\n # removed but not purged\n [ -e /lib/*/security/pam_systemd.so ] || { stop; exit 0; }\n\n # this is being done by systemd or mountall usually, but not during\n # upgrades from earlier distro releases\n if ! mountpoint -q /sys/fs/cgroup; then\n mount -t tmpfs -o uid=0,gid=0,mode=0755,size=1024 none /sys/fs/cgroup\n fi\n mkdir -p /run/systemd\n if ! mountpoint -q /sys/fs/cgroup/systemd; then\n mkdir -p /sys/fs/cgroup/systemd\n mount -t cgroup -o nosuid,noexec,nodev,none,name=systemd systemd /sys/fs/cgroup/systemd\n fi\n"
4332+ },
4333+ {
4334+ "script" : 0,
4335+ "command" : null
4336+ },
4337+ {
4338+ "script" : 0,
4339+ "command" : null
4340+ },
4341+ {
4342+ "script" : 0,
4343+ "command" : null
4344+ },
4345+ {
4346+ "script" : 0,
4347+ "command" : null
4348+ }
4349+ ],
4350+ "apparmor_switch" : null,
4351+ "respawn_limit" : 10,
4352+ "author" : null,
4353+ "respawn" : 1,
4354+ "nice" : -21,
4355+ "limits" : [
4356+ {
4357+ "rlim_cur" : 0,
4358+ "rlim_max" : 0
4359+ },
4360+ {
4361+ "rlim_cur" : 0,
4362+ "rlim_max" : 0
4363+ },
4364+ {
4365+ "rlim_cur" : 0,
4366+ "rlim_max" : 0
4367+ },
4368+ {
4369+ "rlim_cur" : 0,
4370+ "rlim_max" : 0
4371+ },
4372+ {
4373+ "rlim_cur" : 0,
4374+ "rlim_max" : 0
4375+ },
4376+ {
4377+ "rlim_cur" : 0,
4378+ "rlim_max" : 0
4379+ },
4380+ {
4381+ "rlim_cur" : 0,
4382+ "rlim_max" : 0
4383+ },
4384+ {
4385+ "rlim_cur" : 16384,
4386+ "rlim_max" : 16384
4387+ },
4388+ {
4389+ "rlim_cur" : 0,
4390+ "rlim_max" : 0
4391+ },
4392+ {
4393+ "rlim_cur" : 0,
4394+ "rlim_max" : 0
4395+ },
4396+ {
4397+ "rlim_cur" : 0,
4398+ "rlim_max" : 0
4399+ },
4400+ {
4401+ "rlim_cur" : 0,
4402+ "rlim_max" : 0
4403+ },
4404+ {
4405+ "rlim_cur" : 0,
4406+ "rlim_max" : 0
4407+ },
4408+ {
4409+ "rlim_cur" : 0,
4410+ "rlim_max" : 0
4411+ },
4412+ {
4413+ "rlim_cur" : 0,
4414+ "rlim_max" : 0
4415+ },
4416+ {
4417+ "rlim_cur" : 0,
4418+ "rlim_max" : 0
4419+ }
4420+ ],
4421+ "oom_score_adj" : 0,
4422+ "normalexit" : [],
4423+ "kill_timeout" : 5,
4424+ "usage" : null,
4425+ "env" : [
4426+ "SYSTEMD_LOG_TARGET=syslog"
4427+ ],
4428+ "version" : null,
4429+ "task" : 0,
4430+ "path" : "/com/ubuntu/Upstart/jobs/systemd_2dlogind",
4431+ "deleted" : 0,
4432+ "chdir" : null,
4433+ "start_on" : "started dbus",
4434+ "umask" : 18,
4435+ "setgid" : null
4436+ },
4437+ {
4438+ "setuid" : null,
4439+ "jobs" : [
4440+ {
4441+ "trace_state" : "TRACE_NONE",
4442+ "failed" : 0,
4443+ "state" : "JOB_RUNNING",
4444+ "failed_process" : "PROCESS_INVALID",
4445+ "pid" : [
4446+ 1032,
4447+ 0,
4448+ 0,
4449+ 0,
4450+ 0,
4451+ 0
4452+ ],
4453+ "start_env" : [],
4454+ "trace_forks" : 0,
4455+ "respawn_time" : 0,
4456+ "log" : [
4457+ {
4458+ "path" : null
4459+ },
4460+ {
4461+ "path" : null
4462+ },
4463+ {
4464+ "path" : null
4465+ },
4466+ {
4467+ "path" : null
4468+ },
4469+ {
4470+ "path" : null
4471+ },
4472+ {
4473+ "path" : null
4474+ }
4475+ ],
4476+ "respawn_count" : 0,
4477+ "kill_process" : "PROCESS_INVALID",
4478+ "stop_on" : "runlevel [!23]",
4479+ "env" : [
4480+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
4481+ "TERM=linux",
4482+ "RUNLEVEL=2",
4483+ "PREVLEVEL=N",
4484+ "UPSTART_EVENTS=runlevel not-container"
4485+ ],
4486+ "name" : "",
4487+ "path" : "/com/ubuntu/Upstart/jobs/tty5/_",
4488+ "exit_status" : 0,
4489+ "goal" : "JOB_START",
4490+ "stop_env" : [],
4491+ "fds" : []
4492+ }
4493+ ],
4494+ "emits" : [],
4495+ "session" : 0,
4496+ "debug" : 0,
4497+ "export" : [],
4498+ "expect" : "EXPECT_NONE",
4499+ "stop_on" : "runlevel [!23]",
4500+ "chroot" : null,
4501+ "console" : "CONSOLE_LOG",
4502+ "kill_signal" : 15,
4503+ "name" : "tty5",
4504+ "instance" : "",
4505+ "description" : null,
4506+ "respawn_interval" : 5,
4507+ "process" : [
4508+ {
4509+ "script" : 0,
4510+ "command" : "/sbin/getty -8 38400 tty5"
4511+ },
4512+ {
4513+ "script" : 0,
4514+ "command" : null
4515+ },
4516+ {
4517+ "script" : 0,
4518+ "command" : null
4519+ },
4520+ {
4521+ "script" : 0,
4522+ "command" : null
4523+ },
4524+ {
4525+ "script" : 0,
4526+ "command" : null
4527+ },
4528+ {
4529+ "script" : 0,
4530+ "command" : null
4531+ }
4532+ ],
4533+ "apparmor_switch" : null,
4534+ "respawn_limit" : 10,
4535+ "author" : null,
4536+ "respawn" : 1,
4537+ "nice" : -21,
4538+ "limits" : [
4539+ {
4540+ "rlim_cur" : 0,
4541+ "rlim_max" : 0
4542+ },
4543+ {
4544+ "rlim_cur" : 0,
4545+ "rlim_max" : 0
4546+ },
4547+ {
4548+ "rlim_cur" : 0,
4549+ "rlim_max" : 0
4550+ },
4551+ {
4552+ "rlim_cur" : 0,
4553+ "rlim_max" : 0
4554+ },
4555+ {
4556+ "rlim_cur" : 0,
4557+ "rlim_max" : 0
4558+ },
4559+ {
4560+ "rlim_cur" : 0,
4561+ "rlim_max" : 0
4562+ },
4563+ {
4564+ "rlim_cur" : 0,
4565+ "rlim_max" : 0
4566+ },
4567+ {
4568+ "rlim_cur" : 0,
4569+ "rlim_max" : 0
4570+ },
4571+ {
4572+ "rlim_cur" : 0,
4573+ "rlim_max" : 0
4574+ },
4575+ {
4576+ "rlim_cur" : 0,
4577+ "rlim_max" : 0
4578+ },
4579+ {
4580+ "rlim_cur" : 0,
4581+ "rlim_max" : 0
4582+ },
4583+ {
4584+ "rlim_cur" : 0,
4585+ "rlim_max" : 0
4586+ },
4587+ {
4588+ "rlim_cur" : 0,
4589+ "rlim_max" : 0
4590+ },
4591+ {
4592+ "rlim_cur" : 0,
4593+ "rlim_max" : 0
4594+ },
4595+ {
4596+ "rlim_cur" : 0,
4597+ "rlim_max" : 0
4598+ },
4599+ {
4600+ "rlim_cur" : 0,
4601+ "rlim_max" : 0
4602+ }
4603+ ],
4604+ "oom_score_adj" : 0,
4605+ "normalexit" : [],
4606+ "kill_timeout" : 5,
4607+ "usage" : null,
4608+ "env" : [],
4609+ "version" : null,
4610+ "task" : 0,
4611+ "path" : "/com/ubuntu/Upstart/jobs/tty5",
4612+ "deleted" : 0,
4613+ "chdir" : null,
4614+ "start_on" : "(runlevel [23] and not-container)",
4615+ "umask" : 18,
4616+ "setgid" : null
4617+ },
4618+ {
4619+ "setuid" : null,
4620+ "jobs" : [
4621+ {
4622+ "trace_state" : "TRACE_NONE",
4623+ "failed" : 0,
4624+ "state" : "JOB_RUNNING",
4625+ "failed_process" : "PROCESS_INVALID",
4626+ "pid" : [
4627+ 819,
4628+ 0,
4629+ 0,
4630+ 0,
4631+ 0,
4632+ 0
4633+ ],
4634+ "start_env" : [],
4635+ "trace_forks" : 0,
4636+ "respawn_time" : 0,
4637+ "log" : [
4638+ {
4639+ "remote_closed" : 0,
4640+ "detached" : 0,
4641+ "uid" : 0,
4642+ "open_errno" : 9,
4643+ "path" : "/var/log/upstart/modemmanager.log",
4644+ "io_watch_fd" : 21,
4645+ "fd" : 14
4646+ },
4647+ {
4648+ "path" : null
4649+ },
4650+ {
4651+ "path" : null
4652+ },
4653+ {
4654+ "path" : null
4655+ },
4656+ {
4657+ "path" : null
4658+ },
4659+ {
4660+ "path" : null
4661+ }
4662+ ],
4663+ "respawn_count" : 0,
4664+ "kill_process" : "PROCESS_INVALID",
4665+ "stop_on" : "stopped network-manager",
4666+ "env" : [
4667+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
4668+ "TERM=linux",
4669+ "JOB=network-manager",
4670+ "INSTANCE=",
4671+ "UPSTART_EVENTS=starting"
4672+ ],
4673+ "name" : "",
4674+ "path" : "/com/ubuntu/Upstart/jobs/modemmanager/_",
4675+ "exit_status" : 0,
4676+ "goal" : "JOB_START",
4677+ "stop_env" : [],
4678+ "fds" : []
4679+ }
4680+ ],
4681+ "emits" : [],
4682+ "session" : 0,
4683+ "debug" : 0,
4684+ "export" : [],
4685+ "expect" : "EXPECT_NONE",
4686+ "stop_on" : "stopped network-manager",
4687+ "chroot" : null,
4688+ "console" : "CONSOLE_LOG",
4689+ "kill_signal" : 15,
4690+ "name" : "modemmanager",
4691+ "instance" : "",
4692+ "description" : "modem connection manager",
4693+ "respawn_interval" : 5,
4694+ "process" : [
4695+ {
4696+ "script" : 0,
4697+ "command" : "/usr/sbin/modem-manager"
4698+ },
4699+ {
4700+ "script" : 1,
4701+ "command" : "[ -x /usr/sbin/modem-manager ] || stop\n"
4702+ },
4703+ {
4704+ "script" : 0,
4705+ "command" : null
4706+ },
4707+ {
4708+ "script" : 0,
4709+ "command" : null
4710+ },
4711+ {
4712+ "script" : 0,
4713+ "command" : null
4714+ },
4715+ {
4716+ "script" : 0,
4717+ "command" : null
4718+ }
4719+ ],
4720+ "apparmor_switch" : null,
4721+ "respawn_limit" : 10,
4722+ "author" : null,
4723+ "respawn" : 1,
4724+ "nice" : -21,
4725+ "limits" : [
4726+ {
4727+ "rlim_cur" : 0,
4728+ "rlim_max" : 0
4729+ },
4730+ {
4731+ "rlim_cur" : 0,
4732+ "rlim_max" : 0
4733+ },
4734+ {
4735+ "rlim_cur" : 0,
4736+ "rlim_max" : 0
4737+ },
4738+ {
4739+ "rlim_cur" : 0,
4740+ "rlim_max" : 0
4741+ },
4742+ {
4743+ "rlim_cur" : 0,
4744+ "rlim_max" : 0
4745+ },
4746+ {
4747+ "rlim_cur" : 0,
4748+ "rlim_max" : 0
4749+ },
4750+ {
4751+ "rlim_cur" : 0,
4752+ "rlim_max" : 0
4753+ },
4754+ {
4755+ "rlim_cur" : 0,
4756+ "rlim_max" : 0
4757+ },
4758+ {
4759+ "rlim_cur" : 0,
4760+ "rlim_max" : 0
4761+ },
4762+ {
4763+ "rlim_cur" : 0,
4764+ "rlim_max" : 0
4765+ },
4766+ {
4767+ "rlim_cur" : 0,
4768+ "rlim_max" : 0
4769+ },
4770+ {
4771+ "rlim_cur" : 0,
4772+ "rlim_max" : 0
4773+ },
4774+ {
4775+ "rlim_cur" : 0,
4776+ "rlim_max" : 0
4777+ },
4778+ {
4779+ "rlim_cur" : 0,
4780+ "rlim_max" : 0
4781+ },
4782+ {
4783+ "rlim_cur" : 0,
4784+ "rlim_max" : 0
4785+ },
4786+ {
4787+ "rlim_cur" : 0,
4788+ "rlim_max" : 0
4789+ }
4790+ ],
4791+ "oom_score_adj" : 0,
4792+ "normalexit" : [],
4793+ "kill_timeout" : 5,
4794+ "usage" : null,
4795+ "env" : [],
4796+ "version" : null,
4797+ "task" : 0,
4798+ "path" : "/com/ubuntu/Upstart/jobs/modemmanager",
4799+ "deleted" : 0,
4800+ "chdir" : null,
4801+ "start_on" : "starting network-manager",
4802+ "umask" : 18,
4803+ "setgid" : null
4804+ },
4805+ {
4806+ "setuid" : null,
4807+ "jobs" : [
4808+ {
4809+ "trace_state" : "TRACE_NONE",
4810+ "failed" : 0,
4811+ "state" : "JOB_RUNNING",
4812+ "failed_process" : "PROCESS_INVALID",
4813+ "pid" : [
4814+ 0,
4815+ 0,
4816+ 0,
4817+ 0,
4818+ 0,
4819+ 0
4820+ ],
4821+ "start_env" : [],
4822+ "trace_forks" : 0,
4823+ "respawn_time" : 0,
4824+ "log" : [
4825+ {
4826+ "path" : null
4827+ },
4828+ {
4829+ "path" : null
4830+ },
4831+ {
4832+ "path" : null
4833+ },
4834+ {
4835+ "path" : null
4836+ },
4837+ {
4838+ "path" : null
4839+ },
4840+ {
4841+ "path" : null
4842+ }
4843+ ],
4844+ "respawn_count" : 0,
4845+ "kill_process" : "PROCESS_INVALID",
4846+ "env" : [
4847+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin",
4848+ "TERM=linux",
4849+ "UPSTART_EVENTS=local-filesystems"
4850+ ],
4851+ "name" : "",
4852+ "path" : "/com/ubuntu/Upstart/jobs/mountall_2esh/_",
4853+ "exit_status" : 0,
4854+ "goal" : "JOB_START",
4855+ "stop_env" : [],
4856+ "fds" : []
4857+ }
4858+ ],
4859+ "emits" : [],
4860+ "session" : 0,
4861+ "debug" : 0,
4862+ "export" : [],
4863+ "expect" : "EXPECT_NONE",
4864+ "chroot" : null,
4865+ "console" : "CONSOLE_LOG",
4866+ "kill_signal" : 15,
4867+ "name" : "mountall.sh",
4868+ "instance" : "",
4869+ "description" : "Signal sysvinit that local filesystems are mounted",
4870+ "respawn_interval" : 5,
4871+ "process" : [
4872+ {
4873+ "script" : 0,
4874+ "command" : null
4875+ },
4876+ {
4877+ "script" : 0,
4878+ "command" : null
4879+ },
4880+ {
4881+ "script" : 0,
4882+ "command" : null
4883+ },
4884+ {
4885+ "script" : 0,
4886+ "command" : null
4887+ },
4888+ {
4889+ "script" : 0,
4890+ "command" : null
4891+ },
4892+ {
4893+ "script" : 0,
4894+ "command" : null
4895+ }
4896+ ],
4897+ "apparmor_switch" : null,
4898+ "respawn_limit" : 10,
4899+ "author" : null,
4900+ "respawn" : 0,
4901+ "nice" : -21,
4902+ "limits" : [
4903+ {
4904+ "rlim_cur" : 0,
4905+ "rlim_max" : 0
4906+ },
4907+ {
4908+ "rlim_cur" : 0,
4909+ "rlim_max" : 0
4910+ },
4911+ {
4912+ "rlim_cur" : 0,
4913+ "rlim_max" : 0
4914+ },
4915+ {
4916+ "rlim_cur" : 0,
4917+ "rlim_max" : 0
4918+ },
4919+ {
4920+ "rlim_cur" : 0,
4921+ "rlim_max" : 0
4922+ },
4923+ {
4924+ "rlim_cur" : 0,
4925+ "rlim_max" : 0
4926+ },
4927+ {
4928+ "rlim_cur" : 0,
4929+ "rlim_max" : 0
4930+ },
4931+ {
4932+ "rlim_cur" : 0,
4933+ "rlim_max" : 0
4934+ },
4935+ {
4936+ "rlim_cur" : 0,
4937+ "rlim_max" : 0
4938+ },
4939+ {
4940+ "rlim_cur" : 0,
4941+ "rlim_max" : 0
4942+ },
4943+ {
4944+ "rlim_cur" : 0,
4945+ "rlim_max" : 0
4946+ },
4947+ {
4948+ "rlim_cur" : 0,
4949+ "rlim_max" : 0
4950+ },
4951+ {
4952+ "rlim_cur" : 0,
4953+ "rlim_max" : 0
4954+ },
4955+ {
4956+ "rlim_cur" : 0,
4957+ "rlim_max" : 0
4958+ },
4959+ {
4960+ "rlim_cur" : 0,
4961+ "rlim_max" : 0
4962+ },
4963+ {
4964+ "rlim_cur" : 0,
4965+ "rlim_max" : 0
4966+ }
4967+ ],
4968+ "oom_score_adj" : 0,
4969+ "normalexit" : [],
4970+ "kill_timeout" : 5,
4971+ "usage" : null,
4972+ "env" : [],
4973+ "version" : null,
4974+ "task" : 0,
4975+ "path" : "/com/ubuntu/Upstart/jobs/mountall_2esh",
4976+ "deleted" : 0,
4977+ "chdir" : null,
4978+ "start_on" : "local-filesystems",
4979+ "umask" : 18,
4980+ "setgid" : null
4981+ },
4982+ {
4983+ "setuid" : null,
4984+ "jobs" : [
4985+ {
4986+ "trace_state" : "TRACE_NONE",
4987+ "failed" : 0,
4988+ "state" : "JOB_RUNNING",
4989+ "failed_process" : "PROCESS_INVALID",
4990+ "pid" : [
4991+ 1136,
4992+ 0,
4993+ 0,
4994+ 0,
4995+ 0,
4996+ 0
4997+ ],
4998+ "start_env" : [],
4999+ "trace_forks" : 1,
5000+ "respawn_time" : 0,
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches