Merge lp:~upstart-devel/upstart/stateful-reexec into lp:upstart

Proposed by James Hunt on 2012-11-07
Status: Superseded
Proposed branch: lp:~upstart-devel/upstart/stateful-reexec
Merge into: lp:upstart
Diff against target: 15415 lines (+10987/-672)
40 files modified
ChangeLog (+16/-0)
configure.ac (+33/-0)
init/Makefile.am (+64/-33)
init/blocked.c (+55/-0)
init/blocked.h (+8/-0)
init/conf.c (+93/-35)
init/conf.h (+12/-4)
init/control.c (+145/-2)
init/control.h (+15/-0)
init/event.c (+331/-0)
init/event.h (+22/-0)
init/event_operator.c (+175/-7)
init/event_operator.h (+7/-4)
init/job.c (+726/-7)
init/job.h (+14/-2)
init/job_class.c (+716/-3)
init/job_class.h (+36/-1)
init/job_process.c (+44/-7)
init/job_process.h (+6/-0)
init/log.c (+202/-13)
init/log.h (+13/-5)
init/main.c (+114/-26)
init/parse_job.c (+42/-1)
init/parse_job.h (+5/-0)
init/process.c (+225/-0)
init/process.h (+38/-3)
init/session.c (+301/-33)
init/session.h (+19/-6)
init/state.c (+2008/-0)
init/state.h (+1222/-0)
init/tests/test_conf.c (+1/-43)
init/tests/test_event_operator.c (+92/-1)
init/tests/test_job.c (+215/-60)
init/tests/test_job_process.c (+508/-291)
init/tests/test_log.c (+19/-35)
init/tests/test_state.c (+2622/-0)
init/tests/test_util.c (+27/-0)
init/tests/test_util.h (+304/-0)
util/man/telinit.8 (+4/-3)
util/tests/test_initctl.c (+488/-47)
To merge this branch: bzr merge lp:~upstart-devel/upstart/stateful-reexec
Reviewer Review Type Date Requested Status
Upstart Reviewers 2012-11-07 Pending
Review via email: mp+133265@code.launchpad.net

This proposal has been superseded by a proposal from 2012-11-07.

Description of the Change

* Added stateful re-exec support such that when Upstart is asked
  to restart itself using 'telinit u', the new instance of PID 1
  will retain knowledge of all system jobs and running instances.

See also: https://wiki.ubuntu.com/FoundationsTeam/Specs/QuantalUpstartStatefulReexec

To post a comment you must log in.
1473. By James Hunt on 2012-11-07

Upstream sync.

1474. By James Hunt on 2012-11-14

Sync with lp:~upstart-devel/upstart/stateful-reexec.

1475. By James Hunt on 2012-11-14

* init/job_class.c: Formatting.
* init/log.c: Corrected documentation for log_unflushed_files.
* init/state.h: Explained why log_unflushed_files is not currently
  serialised.

1476. By James Hunt on 2012-11-14

* extra/man/upstart-udev-bridge.8: Corrected explanation of actions in
  udev events (thanks to Stefan Bader for pointing out this problem).
* init/blocked.c: blocked_type_str_to_enum(): Assert argument.
* init/job_class.c: job_class_expect_type_str_to_enum(): Assert
  argument.
* init/job_process.c: job_process_run(): Reverted unnecessary check on 'p'.
* init/log.c: log_deserialise(): Added cast and explanation why return code
  from state_toggle_cloexec() not checked.
* init/process.c: process_type_str_to_enum(): Assert argument.
* init/session.c:
  - session_create_conf_source(): Removed redundant getenv() call.
  - session_deserialise(): Removed incorrect uid_t initialisation.
* init/state.h:
  - state_get_json_str_array_generic(): Actually use env parameter.
  - Remove warn_unused_result from state_toggle_cloexec() to avoid compile
    failure with strict flags.
* init/tests/test_state.c:
  - Use rlim_t.
  - test_enums():
    - Don't call *_str_to_enum() if previous *_enum_to_str() failed.
    - Add additional checks for invalid enum values.
  - main(): Remove redundant setenv().
* init/tests/test_util.c: string_check(): Make initial test less
  cryptic.

Unmerged revisions

Preview Diff

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

Subscribers

People subscribed via source and target branches