Merge lp:~jamesodhunt/upstart/upstream-initctl-check-config into lp:upstart

Proposed by James Hunt
Status: Merged
Merged at revision: 1306
Proposed branch: lp:~jamesodhunt/upstart/upstream-initctl-check-config
Merge into: lp:upstart
Diff against target: 5002 lines (+3534/-334)
26 files modified
ChangeLog (+114/-0)
conf/rc-sysinit.conf (+2/-0)
configure.ac (+1/-1)
init/conf.c (+0/-1)
init/control.c (+33/-7)
init/control.h (+15/-1)
init/environ.c (+0/-1)
init/environ.h (+0/-1)
init/job_process.c (+0/-1)
init/job_process.h (+0/-1)
init/main.c (+229/-136)
init/man/init.5 (+16/-1)
init/man/init.8 (+23/-2)
init/paths.h (+16/-2)
init/tests/test_conf.c (+0/-1)
init/tests/test_control.c (+0/-1)
init/tests/test_environ.c (+0/-1)
init/tests/test_job_class.c (+0/-1)
init/tests/test_job_process.c (+0/-1)
po/upstart.pot (+268/-134)
util/Makefile.am (+1/-1)
util/initctl.c (+1014/-17)
util/initctl.h (+458/-0)
util/man/initctl.8 (+163/-2)
util/reboot.c (+0/-1)
util/tests/test_initctl.c (+1181/-19)
To merge this branch: bzr merge lp:~jamesodhunt/upstart/upstream-initctl-check-config
Reviewer Review Type Date Requested Status
Upstart Developers Pending
Review via email: mp+63350@code.launchpad.net

Description of the change

Add "check-config" command to initctl. This builds upon (requires) the "show-config" command.

* util/initctl.c:
  - New functions:
    - allow_event(): Determine if specified event is erroneous or not.
      Handles globbing.
    - allow_job(): Determine if specified job is erroneous or not.
      Handles variables (such as instance variables).
    - check_condition(): High-level function to handle checking start
      on/stop on conditions.
    - check_config_action: Handler for "check-config" command.
    - display_check_errors(): Display errors from expression tree nodes
      that are in error.
    - eval_expr_tree(): Evaluate expression tree.
    - ignored_events_setter(): handler for '--ignore-events' command-line
      option for "check-config" command.
    - tree_filter(): Used for filtering expression tree nodes.
  - show_config_action(): Update for check-config mode.
  - job_class_parse_events(): Update for check-config mode.
  - job_class_show_emits(): Update for check-config mode.
* util/initctl.h:
  - Added structs for JobCondition, CheckConfigData and ExprNode.
  - New macros: MAKE_EXPR_NODE() and MAKE_JOB_CONDITION().
* util/tests/test_initctl.c:
  - test_check_config(): New function to test "initctl check-config".
  - main(): Added call to test_check_config(), conditional on
    a non-chroot environment and a working D-Bus system.
* util/man/initctl.8: Updated for "check-config" command and associated
  options.
* Changelog: updated.
* conf/rc-sysinit.conf: Added emits stanza as required by
  "initctl check-config".

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

* util/man/init.5:
  - Update for format of "emit" events.
  - Add reference to initctl.
  - Typo.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2011-05-12 20:42:28 +0000
3+++ ChangeLog 2011-06-06 16:54:17 +0000
4@@ -1,3 +1,117 @@
5+2011-06-01 James Hunt <james.hunt@ubuntu.com>
6+
7+ Add D-Bus session support to initctl.
8+
9+ * util/initctl.c:
10+ - Added "--session" command-line option.
11+ - dbus_bus_type_setter(): New function used by option parser to
12+ distinguish system/session D-Bus bus type.
13+ - system_bus variable now replaced by two others: use_dbus (boolean)
14+ and dbus_bus_type.
15+ - upstart_open(): Updated to handle multiple D-Bus bus types.
16+ * util/man/initctl.8: Update for "--session" option.
17+ * util/tests/test_initctl.c: Updated to make use of use_dbus and
18+ dbus_bus_type rather than system_bus.
19+
20+ Add "show-config" command to initctl.
21+
22+ * util/initctl.c:
23+ - New functions:
24+ - job_class_condition_handler(): Handler function to retrieve job conditions.
25+ - job_class_condition_err_handler(): Handler error function for
26+ job_class_condition_handler().
27+ - job_class_parse_events(): Convert RPN "start on" and "stop on" conditions to
28+ human-readable format.
29+ - job_class_show_emits(): Display events which job emits.
30+ - job_class_show_conditions(): Make D-Bus calls to retrieve "start on" and
31+ "stop on" conditions.
32+ - show_config_action: Handle "show-config" command..
33+ * util/initctl.h: New file providing stack-handling functionality for
34+ RPN parsing for "show-config" command.
35+ * util/Makefile.am: Added initctl.h to initctl_SOURCES.
36+ * util/man/initctl.8: Updated for "show-config" command and associated
37+ options.
38+ * util/tests/test_initctl.c:
39+ - New macros START_UPSTART, STOP_UPSTART, RUN_COMMAND, CREATE_FILE and DELETE_FILE.
40+ These are required since due to the introduction of the
41+ "show-config" initctl command, initctl is no longer solely a proxy
42+ to Upstart: it now has some intelligence (it parses the
43+ "emits", "start on" and "stop on" conditions) and thus must be
44+ tested directly.
45+ - test_show_config(): New function to test "initctl show-config".
46+ - in_chroot(): New function to detect if tests are being run from
47+ within a chroot environment.
48+ - dbus_configured(): New function which performs a basic check to
49+ establish if D-Bus is configured correctly.
50+ - main(): Added call to test_show_config(), conditional on
51+ a non-chroot environment and a working D-Bus system.
52+
53+ Add "check-config" command to initctl.
54+
55+ * util/initctl.c:
56+ - New functions:
57+ - allow_event(): Determine if specified event is erroneous or not.
58+ Handles globbing.
59+ - allow_job(): Determine if specified job is erroneous or not.
60+ Handles variables (such as instance variables).
61+ - check_condition(): High-level function to handle checking start
62+ on/stop on conditions.
63+ - check_config_action: Handler for "check-config" command.
64+ - display_check_errors(): Display errors from expression tree nodes
65+ that are in error.
66+ - eval_expr_tree(): Evaluate expression tree.
67+ - ignored_events_setter(): handler for '--ignore-events' command-line
68+ option for "check-config" command.
69+ - tree_filter(): Used for filtering expression tree nodes.
70+ - show_config_action(): Update for check-config mode.
71+ - job_class_parse_events(): Update for check-config mode.
72+ - job_class_show_emits(): Update for check-config mode.
73+ * util/initctl.h:
74+ - Added structs for JobCondition, CheckConfigData and ExprNode.
75+ - New macros: MAKE_EXPR_NODE() and MAKE_JOB_CONDITION().
76+ * util/tests/test_initctl.c:
77+ - test_check_config(): New function to test "initctl check-config".
78+ - main(): Added call to test_check_config(), conditional on
79+ a non-chroot environment and a working D-Bus system.
80+ * util/man/initctl.8: Updated for "check-config" command and associated
81+ options.
82+ * conf/rc-sysinit.conf: Added "emits" stanza, required by
83+ "check-config".
84+
85+2011-05-31 James Hunt <james.hunt@ubuntu.com>
86+
87+ Add command-line option to use D-Bus session bus (for testing).
88+
89+ * init/control.c:
90+ - Added new boolean use_session_bus.
91+ - Updated comments.
92+ - control_handle_bus_type(): New function to allow selection of
93+ session bus via env var "UPSTART_USE_SESSION_BUS".
94+ Also logs use of session bus if use_session_bus set.
95+ - control_bus_open(): Now connects to either D-Bus system bus or session bus.
96+ * init/control.h: New define for USE_SESSION_BUS_ENV.
97+ * init/main.c: Addition of "--session" command-line option.
98+ * init/man/init.8: Update for new "--session" command-line option.
99+
100+ * Corrected copyright notices.
101+
102+ Add option to allow alternate location for job config files.
103+
104+ * init/main.c:
105+ - Added "--confdir <dir>" command-line option.
106+ - handle_confdir(): New function to select alternate confdir using env
107+ var "UPSTART_CONFDIR" or command-line option (for testing).
108+ * init/paths.h: Added define for CONFDIR_ENV.
109+ * init/man/init.8: Update for new "--confdir" command-line option.
110+
111+ Add ability to suppress initial event and/or change its name.
112+
113+ * init/main.c: New command-line options: "--no-startup-event" and
114+ "--startup-event". If "--no-startup-event" specified, log message as a
115+ debug aid.
116+ * init/man/init.8: Documentation for new command-line options:
117+ "--no-startup-event" and "--startup-event".
118+
119 2011-05-12 Marc - A. Dahlhaus <mad@wol.de>
120
121 * init/job_class.h (JobClass): Add kill signal member
122
123=== modified file 'conf/rc-sysinit.conf'
124--- conf/rc-sysinit.conf 2010-02-04 03:59:06 +0000
125+++ conf/rc-sysinit.conf 2011-06-06 16:54:17 +0000
126@@ -13,6 +13,8 @@
127 # or by faking an old /etc/inittab entry
128 env DEFAULT_RUNLEVEL=2
129
130+emits runlevel
131+
132 # There can be no previous runlevel here, but there might be old
133 # information in /var/run/utmp that we pick up, and we don't want
134 # that.
135
136=== modified file 'configure.ac'
137--- configure.ac 2011-03-22 17:53:17 +0000
138+++ configure.ac 2011-06-06 16:54:17 +0000
139@@ -2,7 +2,7 @@
140
141 AC_PREREQ(2.61)
142 AC_INIT([upstart], [1.3], [upstart-devel@lists.ubuntu.com])
143-NIH_COPYRIGHT([[Copyright © 2011 Scott James Remnant, Google Inc., Canonical Ltd.]])
144+NIH_COPYRIGHT([[Copyright © 2011 Scott James Remnant, Canonical Ltd.]])
145 AC_CONFIG_SRCDIR([init/main.c])
146 AC_CONFIG_MACRO_DIR([m4])
147
148
149=== modified file 'init/conf.c'
150--- init/conf.c 2011-03-15 18:44:09 +0000
151+++ init/conf.c 2011-06-06 16:54:17 +0000
152@@ -2,7 +2,6 @@
153 *
154 * conf.c - configuration management
155 *
156- * Copyright © 2011 Google Inc.
157 * Copyright © 2009 Canonical Ltd.
158 * Author: Scott James Remnant <scott@netsplit.com>.
159 *
160
161=== modified file 'init/control.c'
162--- init/control.c 2009-07-11 11:47:12 +0000
163+++ init/control.c 2011-06-06 16:54:17 +0000
164@@ -2,7 +2,7 @@
165 *
166 * control.c - D-Bus connections, objects and methods
167 *
168- * Copyright © 2009 Canonical Ltd.
169+ * Copyright © 2009-2011 Canonical Ltd.
170 * Author: Scott James Remnant <scott@netsplit.com>.
171 *
172 * This program is free software; you can redistribute it and/or modify
173@@ -54,12 +54,19 @@
174
175 #include "com.ubuntu.Upstart.h"
176
177-
178 /* Prototypes for static functions */
179 static int control_server_connect (DBusServer *server, DBusConnection *conn);
180 static void control_disconnected (DBusConnection *conn);
181 static void control_register_all (DBusConnection *conn);
182
183+/**
184+ * use_session_bus:
185+ *
186+ * If TRUE, connect to the D-Bus sessio bus rather than the system bus.
187+ *
188+ * Used for testing.
189+ **/
190+int use_session_bus = FALSE;
191
192 /**
193 * control_server_address:
194@@ -78,7 +85,7 @@
195 /**
196 * control_bus:
197 *
198- * Open connection to D-Bus system bus. The connection may be opened with
199+ * Open connection to a D-Bus bus. The connection may be opened with
200 * control_bus_open() and if lost will become NULL.
201 **/
202 DBusConnection *control_bus = NULL;
203@@ -86,7 +93,7 @@
204 /**
205 * control_conns:
206 *
207- * Open control connections, including the connection to the D-Bus system
208+ * Open control connections, including the connection to a D-Bus
209 * bus and any private client connections.
210 **/
211 NihList *control_conns = NULL;
212@@ -190,8 +197,9 @@
213 /**
214 * control_bus_open:
215 *
216- * Open a connection to the D-Bus system bus and store it in the control_bus
217- * global. The connection is handled automatically in the main loop.
218+ * Open a connection to the appropriate D-Bus bus and store it in the
219+ * control_bus global. The connection is handled automatically
220+ * in the main loop.
221 *
222 * Returns: zero on success, negative value on raised error.
223 **/
224@@ -207,10 +215,13 @@
225
226 control_init ();
227
228+ control_handle_bus_type ();
229+
230 /* Connect to the D-Bus System Bus and hook everything up into
231 * our own main loop automatically.
232 */
233- conn = nih_dbus_bus (DBUS_BUS_SYSTEM, control_disconnected);
234+ conn = nih_dbus_bus (use_session_bus ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM,
235+ control_disconnected);
236 if (! conn)
237 return -1;
238
239@@ -669,3 +680,18 @@
240
241 return 0;
242 }
243+
244+/**
245+ * control_handle_bus_type:
246+ *
247+ * Determine D-Bus bus type to connect to.
248+ **/
249+void
250+control_handle_bus_type (void)
251+{
252+ if (getenv (USE_SESSION_BUS_ENV))
253+ use_session_bus = TRUE;
254+
255+ if (use_session_bus)
256+ nih_debug ("Using session bus");
257+}
258
259=== modified file 'init/control.h'
260--- init/control.h 2009-07-09 08:36:52 +0000
261+++ init/control.h 2011-06-06 16:54:17 +0000
262@@ -1,6 +1,6 @@
263 /* upstart
264 *
265- * Copyright © 2009 Canonical Ltd.
266+ * Copyright © 2009-2011 Canonical Ltd.
267 * Author: Scott James Remnant <scott@netsplit.com>.
268 *
269 * This program is free software; you can redistribute it and/or modify
270@@ -27,6 +27,18 @@
271 #include <nih-dbus/dbus_connection.h>
272 #include <nih-dbus/dbus_message.h>
273
274+/**
275+ * USE_SESSION_BUS_ENV:
276+ *
277+ * If this environment variable is set to any value, connect to
278+ * D-Bus session bus rather than the system bus.
279+ *
280+ * Used for testing.
281+ **/
282+#ifndef USE_SESSION_BUS_ENV
283+#define USE_SESSION_BUS_ENV "UPSTART_USE_SESSION_BUS"
284+#endif
285+
286
287 NIH_BEGIN_EXTERN
288
289@@ -72,6 +84,8 @@
290 const char *log_priority)
291 __attribute__ ((warn_unused_result));
292
293+void control_handle_bus_type (void);
294+
295 NIH_END_EXTERN
296
297 #endif /* INIT_CONTROL_H */
298
299=== modified file 'init/environ.c'
300--- init/environ.c 2011-03-16 22:42:48 +0000
301+++ init/environ.c 2011-06-06 16:54:17 +0000
302@@ -2,7 +2,6 @@
303 *
304 * environ.c - environment table utilities
305 *
306- * Copyright © 2011 Google Inc.
307 * Copyright © 2009 Canonical Ltd.
308 * Author: Scott James Remnant <scott@netsplit.com>.
309 *
310
311=== modified file 'init/environ.h'
312--- init/environ.h 2011-03-16 22:42:48 +0000
313+++ init/environ.h 2011-06-06 16:54:17 +0000
314@@ -1,6 +1,5 @@
315 /* upstart
316 *
317- * Copyright © 2011 Google Inc.
318 * Copyright © 2009 Canonical Ltd.
319 * Author: Scott James Remnant <scott@netsplit.com>.
320 *
321
322=== modified file 'init/job_process.c'
323--- init/job_process.c 2011-05-12 20:42:28 +0000
324+++ init/job_process.c 2011-06-06 16:54:17 +0000
325@@ -2,7 +2,6 @@
326 *
327 * job_process.c - job process handling
328 *
329- * Copyright © 2011 Google Inc.
330 * Copyright © 2011 Canonical Ltd.
331 * Author: Scott James Remnant <scott@netsplit.com>.
332 *
333
334=== modified file 'init/job_process.h'
335--- init/job_process.h 2011-05-12 19:21:16 +0000
336+++ init/job_process.h 2011-06-06 16:54:17 +0000
337@@ -1,6 +1,5 @@
338 /* upstart
339 *
340- * Copyright © 2011 Google Inc.
341 * Copyright © 2009 Canonical Ltd.
342 * Author: Scott James Remnant <scott@netsplit.com>.
343 *
344
345=== modified file 'init/main.c'
346--- init/main.c 2011-03-16 22:54:56 +0000
347+++ init/main.c 2011-06-06 16:54:17 +0000
348@@ -1,7 +1,6 @@
349 /* upstart
350 *
351- * Copyright © 2011 Google Inc.
352- * Copyright © 2010 Canonical Ltd.
353+ * Copyright © 2009-2011 Canonical Ltd.
354 * Author: Scott James Remnant <scott@netsplit.com>.
355 *
356 * This program is free software; you can redistribute it and/or modify
357@@ -71,6 +70,8 @@
358 static void usr1_handler (void *data, NihSignal *signal);
359 #endif /* DEBUG */
360
361+static void handle_confdir (void);
362+
363
364 /**
365 * argv0:
366@@ -90,13 +91,49 @@
367
368
369 /**
370+ * conf_dir:
371+ *
372+ * Full path to job configuration file directory.
373+ *
374+ **/
375+static char *conf_dir = NULL;
376+
377+/**
378+ * initial_event:
379+ *
380+ * Alternate event to emit at startup (rather than STARTUP_EVENT).
381+ **/
382+static char *initial_event = NULL;
383+
384+/**
385+ * disable_startup_event:
386+ *
387+ * If TRUE, do not emit a startup event.
388+ **/
389+static int disable_startup_event = FALSE;
390+
391+extern int use_session_bus;
392+
393+/**
394 * options:
395 *
396 * Command-line options we accept.
397 **/
398 static NihOption options[] = {
399+ { 0, "confdir", N_("specify alternative directory to load configuration files from"),
400+ NULL, "DIR", &conf_dir, NULL },
401+
402+ { 0, "no-startup-event", N_("do not emit any startup event (for testing)"),
403+ NULL, NULL, &disable_startup_event, NULL },
404+
405 { 0, "restart", NULL, NULL, NULL, &restart, NULL },
406
407+ { 0, "session", N_("use D-Bus session bus rather than system bus (for testing)"),
408+ NULL, NULL, &use_session_bus, NULL },
409+
410+ { 0, "startup-event", N_("specify an alternative initial event (for testing)"),
411+ NULL, "NAME", &initial_event, NULL },
412+
413 /* Ignore invalid options */
414 { '-', "--", NULL, NULL, NULL, NULL, NULL },
415
416@@ -124,94 +161,105 @@
417 if (! args)
418 exit (1);
419
420+ handle_confdir ();
421+ control_handle_bus_type ();
422+
423 #ifndef DEBUG
424- /* Check we're root */
425- if (getuid ()) {
426- nih_fatal (_("Need to be root"));
427- exit (1);
428- }
429-
430- /* Check we're process #1 */
431- if (getpid () > 1) {
432- execv (TELINIT, argv);
433- /* Ignore failure, probably just that telinit doesn't exist */
434-
435- nih_fatal (_("Not being executed as init"));
436- exit (1);
437- }
438-
439- /* Clear our arguments from the command-line, so that we show up in
440- * ps or top output as /sbin/init, with no extra flags.
441- *
442- * This is a very Linux-specific trick; by deleting the NULL
443- * terminator at the end of the last argument, we fool the kernel
444- * into believing we used a setproctitle()-a-like to extend the
445- * argument space into the environment space, and thus make it use
446- * strlen() instead of its own assumed length. In fact, we've done
447- * the exact opposite, and shrunk the command line length to just that
448- * of whatever is in argv[0].
449- *
450- * If we don't do this, and just write \0 over the rest of argv, for
451- * example; the command-line length still includes those \0s, and ps
452- * will show whitespace in their place.
453- */
454- if (argc > 1) {
455- char *arg_end;
456-
457- arg_end = argv[argc-1] + strlen (argv[argc-1]);
458- *arg_end = ' ';
459- }
460-
461-
462- /* Become the leader of a new session and process group, shedding
463- * any controlling tty (which we shouldn't have had anyway - but
464- * you never know what initramfs did).
465- */
466- setsid ();
467-
468- /* Set the standard file descriptors to the ordinary console device,
469- * resetting it to sane defaults unless we're inheriting from another
470- * init process which we know left it in a sane state.
471- */
472- if (system_setup_console (CONSOLE_OUTPUT, (! restart)) < 0)
473- nih_free (nih_error_get ());
474-
475- /* Set the PATH environment variable */
476- setenv ("PATH", PATH, TRUE);
477-
478- /* Switch to the root directory in case we were started from some
479- * strange place, or worse, some directory in the initramfs that's
480- * going to go away soon.
481- */
482- if (chdir ("/"))
483- nih_warn ("%s: %s", _("Unable to set root directory"),
484- strerror (errno));
485-
486- /* Mount the /proc and /sys filesystems, which are pretty much
487- * essential for any Linux system; not to mention used by
488- * ourselves.
489- */
490- if (system_mount ("proc", "/proc") < 0) {
491- NihError *err;
492-
493- err = nih_error_get ();
494- nih_warn ("%s: %s", _("Unable to mount /proc filesystem"),
495- err->message);
496- nih_free (err);
497- }
498-
499- if (system_mount ("sysfs", "/sys") < 0) {
500- NihError *err;
501-
502- err = nih_error_get ();
503- nih_warn ("%s: %s", _("Unable to mount /sys filesystem"),
504- err->message);
505- nih_free (err);
506- }
507+ if (use_session_bus == FALSE) {
508+
509+ /* Check we're root */
510+ if (getuid ()) {
511+ nih_fatal (_("Need to be root"));
512+ exit (1);
513+ }
514+
515+ /* Check we're process #1 */
516+ if (getpid () > 1) {
517+ execv (TELINIT, argv);
518+ /* Ignore failure, probably just that telinit doesn't exist */
519+
520+ nih_fatal (_("Not being executed as init"));
521+ exit (1);
522+ }
523+
524+ /* Clear our arguments from the command-line, so that we show up in
525+ * ps or top output as /sbin/init, with no extra flags.
526+ *
527+ * This is a very Linux-specific trick; by deleting the NULL
528+ * terminator at the end of the last argument, we fool the kernel
529+ * into believing we used a setproctitle()-a-like to extend the
530+ * argument space into the environment space, and thus make it use
531+ * strlen() instead of its own assumed length. In fact, we've done
532+ * the exact opposite, and shrunk the command line length to just that
533+ * of whatever is in argv[0].
534+ *
535+ * If we don't do this, and just write \0 over the rest of argv, for
536+ * example; the command-line length still includes those \0s, and ps
537+ * will show whitespace in their place.
538+ */
539+ if (argc > 1) {
540+ char *arg_end;
541+
542+ arg_end = argv[argc-1] + strlen (argv[argc-1]);
543+ *arg_end = ' ';
544+ }
545+
546+
547+ /* Become the leader of a new session and process group, shedding
548+ * any controlling tty (which we shouldn't have had anyway - but
549+ * you never know what initramfs did).
550+ */
551+ setsid ();
552+
553+ /* Set the standard file descriptors to the ordinary console device,
554+ * resetting it to sane defaults unless we're inheriting from another
555+ * init process which we know left it in a sane state.
556+ */
557+ if (system_setup_console (CONSOLE_OUTPUT, (! restart)) < 0)
558+ nih_free (nih_error_get ());
559+
560+ /* Set the PATH environment variable */
561+ setenv ("PATH", PATH, TRUE);
562+
563+ /* Switch to the root directory in case we were started from some
564+ * strange place, or worse, some directory in the initramfs that's
565+ * going to go away soon.
566+ */
567+ if (chdir ("/"))
568+ nih_warn ("%s: %s", _("Unable to set root directory"),
569+ strerror (errno));
570+
571+ /* Mount the /proc and /sys filesystems, which are pretty much
572+ * essential for any Linux system; not to mention used by
573+ * ourselves.
574+ */
575+ if (system_mount ("proc", "/proc") < 0) {
576+ NihError *err;
577+
578+ err = nih_error_get ();
579+ nih_warn ("%s: %s", _("Unable to mount /proc filesystem"),
580+ err->message);
581+ nih_free (err);
582+ }
583+
584+ if (system_mount ("sysfs", "/sys") < 0) {
585+ NihError *err;
586+
587+ err = nih_error_get ();
588+ nih_warn ("%s: %s", _("Unable to mount /sys filesystem"),
589+ err->message);
590+ nih_free (err);
591+ }
592+ } else {
593+ nih_log_set_priority (NIH_LOG_DEBUG);
594+ nih_debug ("Running with UID %d as PID %d (PPID %d)",
595+ (int)getuid (), (int)getpid (), (int)getppid ());
596+ }
597+
598 #else /* DEBUG */
599 nih_log_set_priority (NIH_LOG_DEBUG);
600- nih_debug ("Running as PID %d (PPID %d)",
601- (int)getpid (), (int)getppid ());
602+ nih_debug ("Running with UID %d as PID %d (PPID %d)",
603+ (int)getuid (), (int)getpid (), (int)getppid ());
604 #endif /* DEBUG */
605
606
607@@ -223,11 +271,13 @@
608 nih_signal_reset ();
609
610 #ifndef DEBUG
611- /* Catch fatal errors immediately rather than waiting for a new
612- * iteration through the main loop.
613- */
614- nih_signal_set_handler (SIGSEGV, crash_handler);
615- nih_signal_set_handler (SIGABRT, crash_handler);
616+ if (use_session_bus == FALSE) {
617+ /* Catch fatal errors immediately rather than waiting for a new
618+ * iteration through the main loop.
619+ */
620+ nih_signal_set_handler (SIGSEGV, crash_handler);
621+ nih_signal_set_handler (SIGABRT, crash_handler);
622+ }
623 #endif /* DEBUG */
624
625 /* Don't ignore SIGCHLD or SIGALRM, but don't respond to them
626@@ -238,33 +288,35 @@
627 nih_signal_set_handler (SIGALRM, nih_signal_handler);
628
629 #ifndef DEBUG
630- /* Ask the kernel to send us SIGINT when control-alt-delete is
631- * pressed; generate an event with the same name.
632- */
633- reboot (RB_DISABLE_CAD);
634- nih_signal_set_handler (SIGINT, nih_signal_handler);
635- NIH_MUST (nih_signal_add_handler (NULL, SIGINT, cad_handler, NULL));
636-
637- /* Ask the kernel to send us SIGWINCH when alt-uparrow is pressed;
638- * generate a keyboard-request event.
639- */
640- if (ioctl (0, KDSIGACCEPT, SIGWINCH) == 0) {
641- nih_signal_set_handler (SIGWINCH, nih_signal_handler);
642- NIH_MUST (nih_signal_add_handler (NULL, SIGWINCH,
643- kbd_handler, NULL));
644+ if (use_session_bus == FALSE) {
645+ /* Ask the kernel to send us SIGINT when control-alt-delete is
646+ * pressed; generate an event with the same name.
647+ */
648+ reboot (RB_DISABLE_CAD);
649+ nih_signal_set_handler (SIGINT, nih_signal_handler);
650+ NIH_MUST (nih_signal_add_handler (NULL, SIGINT, cad_handler, NULL));
651+
652+ /* Ask the kernel to send us SIGWINCH when alt-uparrow is pressed;
653+ * generate a keyboard-request event.
654+ */
655+ if (ioctl (0, KDSIGACCEPT, SIGWINCH) == 0) {
656+ nih_signal_set_handler (SIGWINCH, nih_signal_handler);
657+ NIH_MUST (nih_signal_add_handler (NULL, SIGWINCH,
658+ kbd_handler, NULL));
659+ }
660+
661+ /* powstatd sends us SIGPWR when it changes /etc/powerstatus */
662+ nih_signal_set_handler (SIGPWR, nih_signal_handler);
663+ NIH_MUST (nih_signal_add_handler (NULL, SIGPWR, pwr_handler, NULL));
664+
665+ /* SIGHUP instructs us to re-load our configuration */
666+ nih_signal_set_handler (SIGHUP, nih_signal_handler);
667+ NIH_MUST (nih_signal_add_handler (NULL, SIGHUP, hup_handler, NULL));
668+
669+ /* SIGUSR1 instructs us to reconnect to D-Bus */
670+ nih_signal_set_handler (SIGUSR1, nih_signal_handler);
671+ NIH_MUST (nih_signal_add_handler (NULL, SIGUSR1, usr1_handler, NULL));
672 }
673-
674- /* powstatd sends us SIGPWR when it changes /etc/powerstatus */
675- nih_signal_set_handler (SIGPWR, nih_signal_handler);
676- NIH_MUST (nih_signal_add_handler (NULL, SIGPWR, pwr_handler, NULL));
677-
678- /* SIGHUP instructs us to re-load our configuration */
679- nih_signal_set_handler (SIGHUP, nih_signal_handler);
680- NIH_MUST (nih_signal_add_handler (NULL, SIGHUP, hup_handler, NULL));
681-
682- /* SIGUSR1 instructs us to reconnect to D-Bus */
683- nih_signal_set_handler (SIGUSR1, nih_signal_handler);
684- NIH_MUST (nih_signal_add_handler (NULL, SIGUSR1, usr1_handler, NULL));
685 #endif /* DEBUG */
686
687
688@@ -279,25 +331,27 @@
689
690 /* Read configuration */
691 NIH_MUST (conf_source_new (NULL, CONFFILE, CONF_FILE));
692- NIH_MUST (conf_source_new (NULL, CONFDIR, CONF_JOB_DIR));
693+ NIH_MUST (conf_source_new (NULL, conf_dir, CONF_JOB_DIR));
694
695 conf_reload ();
696
697 /* Create a listening server for private connections. */
698- while (control_server_open () < 0) {
699- NihError *err;
700+ if (use_session_bus == FALSE) {
701+ while (control_server_open () < 0) {
702+ NihError *err;
703
704- err = nih_error_get ();
705- if (err->number != ENOMEM) {
706- nih_warn ("%s: %s", _("Unable to listen for private connections"),
707- err->message);
708+ err = nih_error_get ();
709+ if (err->number != ENOMEM) {
710+ nih_warn ("%s: %s", _("Unable to listen for private connections"),
711+ err->message);
712+ nih_free (err);
713+ break;
714+ }
715 nih_free (err);
716- break;
717 }
718- nih_free (err);
719 }
720
721- /* Open connection to the system bus; we normally expect this to
722+ /* Open connection to the appropriate D-Bus bus; we normally expect this to
723 * fail and will try again later - don't let ENOMEM stop us though.
724 */
725 while (control_bus_open () < 0) {
726@@ -313,21 +367,32 @@
727 }
728
729 #ifndef DEBUG
730- /* Now that the startup is complete, send all further logging output
731- * to kmsg instead of to the console.
732- */
733- if (system_setup_console (CONSOLE_NONE, FALSE) < 0)
734- nih_free (nih_error_get ());
735+ if (use_session_bus == FALSE) {
736+ /* Now that the startup is complete, send all further logging output
737+ * to kmsg instead of to the console.
738+ */
739+ if (system_setup_console (CONSOLE_NONE, FALSE) < 0)
740+ nih_free (nih_error_get ());
741
742- nih_log_set_logger (logger_kmsg);
743+ nih_log_set_logger (logger_kmsg);
744+ }
745 #endif /* DEBUG */
746
747
748 /* Generate and run the startup event or read the state from the
749 * init daemon that exec'd us
750 */
751- if (! restart) {
752- NIH_MUST (event_new (NULL, STARTUP_EVENT, NULL));
753+ if (! restart ) {
754+ if (disable_startup_event) {
755+ nih_debug ("Startup event disabled");
756+ } else {
757+ NIH_MUST (event_new (NULL,
758+ initial_event
759+ ? initial_event
760+ : STARTUP_EVENT,
761+ NULL));
762+ }
763+
764 } else {
765 sigset_t mask;
766
767@@ -573,3 +638,31 @@
768 }
769 }
770 #endif /* DEBUG */
771+
772+/**
773+ * handle_confdir:
774+ *
775+ * Determine where system configuration files should be loaded from.
776+ **/
777+static void
778+handle_confdir (void)
779+{
780+ char *dir;
781+
782+ /* user has already specified directory on command-line */
783+ if (conf_dir)
784+ goto out;
785+
786+ conf_dir = CONFDIR;
787+
788+ dir = getenv (CONFDIR_ENV);
789+ if (! dir)
790+ return;
791+
792+ conf_dir = dir;
793+
794+out:
795+ nih_debug ("Using alternate configuration directory %s",
796+ conf_dir);
797+}
798+
799
800=== modified file 'init/man/init.5'
801--- init/man/init.5 2011-05-12 20:42:28 +0000
802+++ init/man/init.5 2011-06-06 16:54:17 +0000
803@@ -288,7 +288,7 @@
804 and
805 .B post-stop
806 scripts are run with the environment of the events or commands that
807-stopped the job. THe
808+stopped the job. The
809 .B UPSTART_STOP_EVENTS
810 environment variable contains the list of events that stopped the job,
811 it will not be present if the job was stopped manually.
812@@ -461,6 +461,20 @@
813
814 This stanza allows a job to document in its job configuration what events
815 it emits itself, and may be useful for graphing possible transitions.
816+
817+The
818+.BR initctl "(8) " check\-config
819+command attempts to use this stanza to resolve events.
820+
821+.I EVENT
822+can be either a literal string or a string including shell
823+wildcard meta-characters (asterisk (\(aq*\(aq), question mark
824+(\(aq?\(aq), and square brackets (\(aq[\(aq and \(aq]\(aq)).
825+Meta-characters are useful to allow
826+.BR initctl "(8) " check\-config
827+to resolve a class of events, such as those emitted by
828+.BR upstart-udev-bridge (8) "" .
829+
830 .\"
831 .SS Process environment
832 Many common adjustments to the process environment, such as resource
833@@ -621,4 +635,5 @@
834 .\"
835 .SH SEE ALSO
836 .BR init (8)
837+.BR initctl (8)
838 .BR sh (1)
839
840=== modified file 'init/man/init.8'
841--- init/man/init.8 2010-02-04 19:26:17 +0000
842+++ init/man/init.8 2011-06-06 16:54:17 +0000
843@@ -1,4 +1,4 @@
844-.TH init 8 2010-02-04 "Upstart"
845+.TH init 8 2011-05-31 "Upstart"
846 .\"
847 .SH NAME
848 init \- Upstart process management daemon
849@@ -64,6 +64,27 @@
850 by placing them on the kernel command-line.
851 .\"
852 .TP
853+.B \-\-confdir \fIdirectory\fP
854+Read job configuration files from a directory other than
855+\fI/etc/init\fP.
856+.\"
857+.TP
858+.B \-\-no\-startup\-event
859+Suppress emission of the initial startup event. This option should only
860+be used for testing since it will stop the
861+.BR init (8)
862+daemon from starting \fBany\fP jobs automatically.
863+.\"
864+.TP
865+.B \-\-session
866+Connect to the D\-Bus session bus. This should only be used for testing.
867+.\"
868+.TP
869+.B \-\-startup-event \fIevent\fP
870+Specify a different initial startup event from the standard
871+.BR startup (7) .
872+.\"
873+.TP
874 .B --verbose
875 Outputs verbose messages about job state changes and event emissions to the
876 system console or log, useful for debugging boot.
877@@ -90,7 +111,7 @@
878 .RB < https://launchpad.net/upstart/+bugs >
879 .\"
880 .SH COPYRIGHT
881-Copyright \(co 2010 Canonical Ltd.
882+Copyright \(co 2009-2011 Canonical Ltd.
883 .br
884 This is free software; see the source for copying conditions. There is NO
885 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
886
887=== modified file 'init/paths.h'
888--- init/paths.h 2010-02-26 15:27:14 +0000
889+++ init/paths.h 2011-06-06 16:54:17 +0000
890@@ -1,6 +1,6 @@
891 /* upstart
892 *
893- * Copyright © 2010 Canonical Ltd.
894+ * Copyright © 2010-2011 Canonical Ltd.
895 * Author: Scott James Remnant <scott@netsplit.com>.
896 *
897 * This program is free software; you can redistribute it and/or modify
898@@ -67,7 +67,7 @@
899 /**
900 * CONFDIR:
901 *
902- * Top-level directory of the system configuration files.
903+ * Default top-level directory of the system configuration files.
904 **/
905 #ifndef CONFDIR
906 #define CONFDIR "/etc/init"
907@@ -75,6 +75,20 @@
908
909
910 /**
911+ * CONFDIR_ENV:
912+ *
913+ * If this environment variable is set, read configuration files
914+ * from the location specified, rather than CONFDIR.
915+ *
916+ * Value is expected to be the full path to an alternative job
917+ * configuration directory.
918+ **/
919+#ifndef CONFDIR_ENV
920+#define CONFDIR_ENV "UPSTART_CONFDIR"
921+#endif
922+
923+
924+/**
925 * SHELL:
926 *
927 * This is the shell binary used whenever we need special processing for
928
929=== modified file 'init/tests/test_conf.c'
930--- init/tests/test_conf.c 2011-02-17 23:38:17 +0000
931+++ init/tests/test_conf.c 2011-06-06 16:54:17 +0000
932@@ -2,7 +2,6 @@
933 *
934 * test_conf.c - test suite for init/conf.c
935 *
936- * Copyright © 2011 Google Inc.
937 * Copyright © 2009 Canonical Ltd.
938 * Author: Scott James Remnant <scott@netsplit.com>.
939 *
940
941=== modified file 'init/tests/test_control.c'
942--- init/tests/test_control.c 2011-03-16 22:42:48 +0000
943+++ init/tests/test_control.c 2011-06-06 16:54:17 +0000
944@@ -2,7 +2,6 @@
945 *
946 * test_dbus.c - test suite for init/dbus.c
947 *
948- * Copyright © 2011 Google Inc.
949 * Copyright © 2010 Canonical Ltd.
950 * Author: Scott James Remnant <scott@netsplit.com>.
951 *
952
953=== modified file 'init/tests/test_environ.c'
954--- init/tests/test_environ.c 2011-03-16 22:42:48 +0000
955+++ init/tests/test_environ.c 2011-06-06 16:54:17 +0000
956@@ -2,7 +2,6 @@
957 *
958 * test_environ.c - test suite for init/environ.c
959 *
960- * Copyright © 2011 Google Inc.
961 * Copyright © 2009 Canonical Ltd.
962 * Author: Scott James Remnant <scott@netsplit.com>.
963 *
964
965=== modified file 'init/tests/test_job_class.c'
966--- init/tests/test_job_class.c 2011-05-12 20:42:28 +0000
967+++ init/tests/test_job_class.c 2011-06-06 16:54:17 +0000
968@@ -2,7 +2,6 @@
969 *
970 * test_job_class.c - test suite for init/job_class.c
971 *
972- * Copyright © 2011 Google Inc.
973 * Copyright © 2010 Canonical Ltd.
974 * Author: Scott James Remnant <scott@netsplit.com>.
975 *
976
977=== modified file 'init/tests/test_job_process.c'
978--- init/tests/test_job_process.c 2011-05-12 19:21:16 +0000
979+++ init/tests/test_job_process.c 2011-06-06 16:54:17 +0000
980@@ -2,7 +2,6 @@
981 *
982 * test_job_process.c - test suite for init/job_process.c
983 *
984- * Copyright © 2011 Google Inc.
985 * Copyright © 2011 Canonical Ltd.
986 * Author: Scott James Remnant <scott@netsplit.com>.
987 *
988
989=== modified file 'po/upstart.pot'
990--- po/upstart.pot 2011-03-22 17:52:25 +0000
991+++ po/upstart.pot 2011-06-06 16:54:17 +0000
992@@ -6,76 +6,141 @@
993 #, fuzzy
994 msgid ""
995 msgstr ""
996-"Project-Id-Version: upstart 1.2\n"
997+"Project-Id-Version: upstart 1.3\n"
998 "Report-Msgid-Bugs-To: new@bugs.launchpad.net\n"
999-"POT-Creation-Date: 2011-03-22 10:52-0700\n"
1000+"POT-Creation-Date: 2011-06-01 13:58+0100\n"
1001 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1002 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1003 "Language-Team: LANGUAGE <LL@li.org>\n"
1004+"Language: \n"
1005 "MIME-Version: 1.0\n"
1006 "Content-Type: text/plain; charset=CHARSET\n"
1007 "Content-Transfer-Encoding: 8bit\n"
1008
1009-#: init/conf.c:238
1010+#: init/conf.c:237
1011 msgid "Unable to load configuration"
1012 msgstr ""
1013
1014-#: init/conf.c:271
1015+#: init/conf.c:270
1016 #, c-format
1017 msgid "Loading configuration from %s"
1018 msgstr ""
1019
1020-#: init/conf.c:308
1021+#: init/conf.c:307
1022 #, c-format
1023 msgid "Handling deletion of %s"
1024 msgstr ""
1025
1026-#: init/conf.c:379
1027+#: init/conf.c:378
1028 msgid "Unable to watch configuration file"
1029 msgstr ""
1030
1031-#: init/conf.c:457
1032+#: init/conf.c:456
1033 msgid "Unable to watch configuration directory"
1034 msgstr ""
1035
1036-#: init/conf.c:569 init/conf.c:653
1037+#: init/conf.c:568 init/conf.c:652
1038 msgid "Error while loading configuration file"
1039 msgstr ""
1040
1041-#: init/conf.c:609
1042+#: init/conf.c:608
1043 msgid "Configuration directory deleted"
1044 msgstr ""
1045
1046-#: init/control.c:155
1047+#: init/control.c:162
1048 msgid "Connection from private client"
1049 msgstr ""
1050
1051-#: init/control.c:290
1052+#: init/control.c:301
1053 msgid "Disconnected from system bus"
1054 msgstr ""
1055
1056-#: init/control.c:356 init/main.c:546
1057+#: init/control.c:367 init/main.c:611
1058 msgid "Reloading configuration"
1059 msgstr ""
1060
1061-#: init/control.c:396 init/control.c:507
1062+#: init/control.c:407 init/control.c:518
1063 msgid "Name may not be empty string"
1064 msgstr ""
1065
1066-#: init/control.c:405
1067+#: init/control.c:416
1068 #, c-format
1069 msgid "Unknown job: %s"
1070 msgstr ""
1071
1072-#: init/control.c:514 init/job_class.c:522 init/job_class.c:710
1073-#: init/job_class.c:829 init/job_class.c:953
1074+#: init/control.c:525 init/job_class.c:524 init/job_class.c:712
1075+#: init/job_class.c:831 init/job_class.c:955
1076 msgid "Env must be KEY=VALUE pairs"
1077 msgstr ""
1078
1079-#: init/control.c:666
1080+#: init/control.c:677
1081 msgid "The log priority given was not recognised"
1082 msgstr ""
1083
1084+#: init/errors.h:58
1085+msgid "Illegal parameter"
1086+msgstr ""
1087+
1088+#: init/errors.h:59
1089+msgid "Unknown parameter"
1090+msgstr ""
1091+
1092+#: init/errors.h:60 init/errors.h:71
1093+msgid "Expected operator"
1094+msgstr ""
1095+
1096+#: init/errors.h:61
1097+msgid "Mismatched braces"
1098+msgstr ""
1099+
1100+#: init/errors.h:62
1101+msgid "Illegal interval, expected number of seconds"
1102+msgstr ""
1103+
1104+#: init/errors.h:63
1105+msgid "Illegal exit status, expected integer"
1106+msgstr ""
1107+
1108+#: init/errors.h:64
1109+msgid "Illegal signal status, expected integer"
1110+msgstr ""
1111+
1112+#: init/errors.h:65
1113+msgid "Illegal file creation mask, expected octal integer"
1114+msgstr ""
1115+
1116+#: init/errors.h:66
1117+msgid "Illegal nice value, expected -20 to 19"
1118+msgstr ""
1119+
1120+#: init/errors.h:67
1121+msgid "Illegal oom adjustment, expected -16 to 15 or 'never'"
1122+msgstr ""
1123+
1124+#: init/errors.h:68
1125+msgid "Illegal oom score adjustment, expected -999 to 1000 or 'never'"
1126+msgstr ""
1127+
1128+#: init/errors.h:69
1129+msgid "Illegal limit, expected 'unlimited' or integer"
1130+msgstr ""
1131+
1132+#: init/errors.h:70
1133+msgid "Expected event"
1134+msgstr ""
1135+
1136+#: init/errors.h:72
1137+msgid "Expected variable name before value"
1138+msgstr ""
1139+
1140+#: init/errors.h:73
1141+msgid "Mismatched parentheses"
1142+msgstr ""
1143+
1144+#: init/errors.h:74
1145+msgid "Name already taken"
1146+msgstr ""
1147+
1148 #: init/event.c:273
1149 #, c-format
1150 msgid "Handling %s event"
1151@@ -164,241 +229,252 @@
1152 msgid "post-stop"
1153 msgstr ""
1154
1155-#: init/job.c:1113 init/job_class.c:756
1156+#: init/job.c:1113 init/job_class.c:758
1157 #, c-format
1158 msgid "Job is already running: %s"
1159 msgstr ""
1160
1161-#: init/job.c:1177 init/job.c:1242 init/job_class.c:875 init/job_class.c:998
1162+#: init/job.c:1177 init/job.c:1242 init/job_class.c:877 init/job_class.c:1000
1163 #, c-format
1164 msgid "Job has already been stopped: %s"
1165 msgstr ""
1166
1167-#: init/job_class.c:559 init/job_class.c:604 init/job_class.c:867
1168-#: init/job_class.c:990
1169+#: init/job_class.c:561 init/job_class.c:606 init/job_class.c:869
1170+#: init/job_class.c:992
1171 #, c-format
1172 msgid "Unknown instance: %s"
1173 msgstr ""
1174
1175-#: init/job_process.c:279
1176+#: init/job_process.c:276
1177 #, c-format
1178 msgid "Failed to spawn %s %s process: %s"
1179 msgstr ""
1180
1181-#: init/job_process.c:285
1182+#: init/job_process.c:282
1183 msgid "Temporary process spawn error"
1184 msgstr ""
1185
1186-#: init/job_process.c:292
1187+#: init/job_process.c:289
1188 #, c-format
1189 msgid "%s %s process (%d)"
1190 msgstr ""
1191
1192-#: init/job_process.c:399
1193+#: init/job_process.c:402
1194 #, c-format
1195 msgid "Pausing %s (%d) [pre-exec] for debug"
1196 msgstr ""
1197
1198-#: init/job_process.c:453
1199+#: init/job_process.c:472
1200 #, c-format
1201 msgid "Failed to open system console: %s"
1202 msgstr ""
1203
1204-#: init/job_process.c:669
1205+#: init/job_process.c:696
1206+#, c-format
1207+msgid "unable to move script fd: %s"
1208+msgstr ""
1209+
1210+#: init/job_process.c:701
1211 #, c-format
1212 msgid "unable to open console: %s"
1213 msgstr ""
1214
1215-#: init/job_process.c:724
1216+#: init/job_process.c:756
1217 #, c-format
1218 msgid "unable to set \"%s\" resource limit: %s"
1219 msgstr ""
1220
1221-#: init/job_process.c:729
1222+#: init/job_process.c:761
1223 #, c-format
1224 msgid "unable to set priority: %s"
1225 msgstr ""
1226
1227-#: init/job_process.c:734
1228+#: init/job_process.c:766
1229 #, c-format
1230 msgid "unable to set oom adjustment: %s"
1231 msgstr ""
1232
1233-#: init/job_process.c:739
1234+#: init/job_process.c:771
1235 #, c-format
1236 msgid "unable to change root directory: %s"
1237 msgstr ""
1238
1239-#: init/job_process.c:744
1240+#: init/job_process.c:776
1241 #, c-format
1242 msgid "unable to change working directory: %s"
1243 msgstr ""
1244
1245-#: init/job_process.c:749
1246+#: init/job_process.c:781
1247 #, c-format
1248 msgid "unable to set trace: %s"
1249 msgstr ""
1250
1251-#: init/job_process.c:754
1252+#: init/job_process.c:786
1253 #, c-format
1254 msgid "unable to execute: %s"
1255 msgstr ""
1256
1257-#: init/job_process.c:785
1258-#, c-format
1259-msgid "Sending TERM signal to %s %s process (%d)"
1260-msgstr ""
1261-
1262-#: init/job_process.c:793
1263-#, c-format
1264-msgid "Failed to send TERM signal to %s %s process (%d): %s"
1265-msgstr ""
1266-
1267-#: init/job_process.c:833
1268-#, c-format
1269-msgid "Sending KILL signal to %s %s process (%d)"
1270-msgstr ""
1271-
1272-#: init/job_process.c:841
1273-#, c-format
1274-msgid "Failed to send KILL signal to %s %s process (%d): %s"
1275-msgstr ""
1276-
1277-#: init/job_process.c:901
1278+#: init/job_process.c:817 init/job_process.c:867
1279+#, c-format
1280+msgid "Sending %s signal to %s %s process (%d)"
1281+msgstr ""
1282+
1283+#: init/job_process.c:826 init/job_process.c:876
1284+#, c-format
1285+msgid "Failed to send %s signal to %s %s process (%d): %s"
1286+msgstr ""
1287+
1288+#: init/job_process.c:937
1289 #, c-format
1290 msgid "%s %s process (%d) terminated with status %d"
1291 msgstr ""
1292
1293-#: init/job_process.c:906
1294+#: init/job_process.c:942
1295 #, c-format
1296 msgid "%s %s process (%d) exited normally"
1297 msgstr ""
1298
1299-#: init/job_process.c:921
1300+#: init/job_process.c:957
1301 #, c-format
1302 msgid "%s %s process (%d) killed by %s signal"
1303 msgstr ""
1304
1305-#: init/job_process.c:925
1306+#: init/job_process.c:961
1307 #, c-format
1308 msgid "%s %s process (%d) killed by signal %d"
1309 msgstr ""
1310
1311-#: init/job_process.c:939
1312+#: init/job_process.c:975
1313 #, c-format
1314 msgid "%s %s process (%d) stopped by %s signal"
1315 msgstr ""
1316
1317-#: init/job_process.c:943
1318+#: init/job_process.c:979
1319 #, c-format
1320 msgid "%s %s process (%d) stopped by signal %d"
1321 msgstr ""
1322
1323-#: init/job_process.c:957
1324+#: init/job_process.c:993
1325 #, c-format
1326 msgid "%s %s process (%d) continued by %s signal"
1327 msgstr ""
1328
1329-#: init/job_process.c:961
1330+#: init/job_process.c:997
1331 #, c-format
1332 msgid "%s %s process (%d) continued by signal %d"
1333 msgstr ""
1334
1335-#: init/job_process.c:1096
1336+#: init/job_process.c:1132
1337 #, c-format
1338 msgid "%s respawning too fast, stopped"
1339 msgstr ""
1340
1341-#: init/job_process.c:1102
1342+#: init/job_process.c:1138
1343 #, c-format
1344 msgid "%s %s process ended, respawning"
1345 msgstr ""
1346
1347-#: init/job_process.c:1342
1348+#: init/job_process.c:1378
1349 #, c-format
1350 msgid "Failed to set ptrace options for %s %s process (%d): %s"
1351 msgstr ""
1352
1353-#: init/job_process.c:1355 init/job_process.c:1550
1354+#: init/job_process.c:1391 init/job_process.c:1586
1355 #, c-format
1356 msgid "Failed to continue traced %s %s process (%d): %s"
1357 msgstr ""
1358
1359-#: init/job_process.c:1395 init/job_process.c:1486 init/job_process.c:1541
1360+#: init/job_process.c:1431 init/job_process.c:1522 init/job_process.c:1577
1361 #, c-format
1362 msgid "Failed to detach traced %s %s process (%d): %s"
1363 msgstr ""
1364
1365-#: init/job_process.c:1435
1366+#: init/job_process.c:1471
1367 #, c-format
1368 msgid "Failed to deliver signal to traced %s %s process (%d): %s"
1369 msgstr ""
1370
1371-#: init/job_process.c:1470
1372+#: init/job_process.c:1506
1373 #, c-format
1374 msgid "Failed to obtain child process id for %s %s process (%d): %s"
1375 msgstr ""
1376
1377-#: init/job_process.c:1477
1378+#: init/job_process.c:1513
1379 #, c-format
1380 msgid "%s %s process (%d) became new process (%d)"
1381 msgstr ""
1382
1383-#: init/job_process.c:1536
1384+#: init/job_process.c:1572
1385 #, c-format
1386 msgid "%s %s process (%d) executable changed"
1387 msgstr ""
1388
1389-#: init/main.c:117
1390+#: init/main.c:123
1391+msgid "specify alternative directory to load configuration files from"
1392+msgstr ""
1393+
1394+#: init/main.c:126
1395+msgid "do not emit any startup event (for testing)"
1396+msgstr ""
1397+
1398+#: init/main.c:131
1399+msgid "use D-Bus session bus rather than system bus (for testing)"
1400+msgstr ""
1401+
1402+#: init/main.c:134
1403+msgid "specify an alternative initial event (for testing)"
1404+msgstr ""
1405+
1406+#: init/main.c:154
1407 msgid "Process management daemon."
1408 msgstr ""
1409
1410-#: init/main.c:119
1411+#: init/main.c:156
1412 msgid ""
1413 "This daemon is normally executed by the kernel and given process id 1 to "
1414 "denote its special status. When executed by a user process, it will "
1415 "actually run /sbin/telinit."
1416 msgstr ""
1417
1418-#: init/main.c:130 util/reboot.c:167 util/shutdown.c:363 util/telinit.c:148
1419+#: init/main.c:172 util/reboot.c:166 util/shutdown.c:363 util/telinit.c:148
1420 msgid "Need to be root"
1421 msgstr ""
1422
1423-#: init/main.c:139
1424+#: init/main.c:181
1425 msgid "Not being executed as init"
1426 msgstr ""
1427
1428-#: init/main.c:187 init/main.c:456
1429+#: init/main.c:229 init/main.c:521
1430 msgid "Unable to set root directory"
1431 msgstr ""
1432
1433-#: init/main.c:198
1434+#: init/main.c:240
1435 msgid "Unable to mount /proc filesystem"
1436 msgstr ""
1437
1438-#: init/main.c:207
1439+#: init/main.c:249
1440 msgid "Unable to mount /sys filesystem"
1441 msgstr ""
1442
1443-#: init/main.c:292
1444+#: init/main.c:345
1445 msgid "Unable to listen for private connections"
1446 msgstr ""
1447
1448-#: init/main.c:473
1449+#: init/main.c:538
1450 #, c-format
1451 msgid "Caught %s, core dumped"
1452 msgstr ""
1453
1454-#: init/main.c:477
1455+#: init/main.c:542
1456 #, c-format
1457 msgid "Caught %s, unable to dump core"
1458 msgstr ""
1459
1460-#: init/main.c:563
1461+#: init/main.c:628
1462 msgid "Reconnecting to system bus"
1463 msgstr ""
1464
1465-#: init/main.c:569
1466+#: init/main.c:634
1467 msgid "Unable to connect to the system bus"
1468 msgstr ""
1469
1470@@ -406,76 +482,110 @@
1471 msgid "main"
1472 msgstr ""
1473
1474-#: util/initctl.c:145
1475+#: util/initctl.c:305
1476 msgid "Unable to connect to system bus"
1477 msgstr ""
1478
1479-#: util/initctl.c:154
1480+#: util/initctl.c:306
1481+msgid "Unable to connect to session bus"
1482+msgstr ""
1483+
1484+#: util/initctl.c:315
1485 #, c-format
1486 msgid "%s: --dest given without --system\n"
1487 msgstr ""
1488
1489-#: util/initctl.c:162
1490+#: util/initctl.c:323
1491 msgid "Unable to connect to Upstart"
1492 msgstr ""
1493
1494-#: util/initctl.c:348 util/initctl.c:483 util/initctl.c:611 util/initctl.c:745
1495-#: util/initctl.c:850
1496+#: util/initctl.c:509 util/initctl.c:644 util/initctl.c:772 util/initctl.c:907
1497+#: util/initctl.c:1012
1498 #, c-format
1499 msgid "%s: missing job name\n"
1500 msgstr ""
1501
1502-#: util/initctl.c:796
1503+#: util/initctl.c:958
1504 msgid "Not running"
1505 msgstr ""
1506
1507-#: util/initctl.c:1065
1508+#: util/initctl.c:1314
1509 #, c-format
1510 msgid "%s: missing event name\n"
1511 msgstr ""
1512
1513-#: util/initctl.c:1261
1514+#: util/initctl.c:1511
1515+msgid "Invalid job class"
1516+msgstr ""
1517+
1518+#: util/initctl.c:2108
1519+msgid "unknown event"
1520+msgstr ""
1521+
1522+#: util/initctl.c:2112
1523+msgid "unknown job"
1524+msgstr ""
1525+
1526+#: util/initctl.c:2213
1527+msgid "use D-Bus session bus to connect to init daemon (for testing)"
1528+msgstr ""
1529+
1530+#: util/initctl.c:2215
1531 msgid "use D-Bus system bus to connect to init daemon"
1532 msgstr ""
1533
1534-#: util/initctl.c:1263
1535-msgid "destination well-known name on system bus"
1536+#: util/initctl.c:2217
1537+msgid "destination well-known name on D-Bus bus"
1538 msgstr ""
1539
1540-#: util/initctl.c:1276
1541+#: util/initctl.c:2230
1542 msgid "do not wait for job to start before exiting"
1543 msgstr ""
1544
1545-#: util/initctl.c:1288
1546+#: util/initctl.c:2242
1547 msgid "do not wait for job to stop before exiting"
1548 msgstr ""
1549
1550-#: util/initctl.c:1300
1551+#: util/initctl.c:2254
1552 msgid "do not wait for job to restart before exiting"
1553 msgstr ""
1554
1555-#: util/initctl.c:1339
1556+#: util/initctl.c:2293
1557 msgid "do not wait for event to finish before exiting"
1558 msgstr ""
1559
1560-#: util/initctl.c:1378
1561+#: util/initctl.c:2334
1562+msgid ""
1563+"enumerate list of events and jobs causing job created from job config to "
1564+"start/stop"
1565+msgstr ""
1566+
1567+#: util/initctl.c:2347
1568+msgid "ignore specified list of events (comma-separated)"
1569+msgstr ""
1570+
1571+#: util/initctl.c:2349
1572+msgid "Generate warning for any unreachable events/jobs"
1573+msgstr ""
1574+
1575+#: util/initctl.c:2358
1576 msgid "Job"
1577 msgstr ""
1578
1579-#: util/initctl.c:1385
1580+#: util/initctl.c:2365
1581 msgid "Event"
1582 msgstr ""
1583
1584-#: util/initctl.c:1393 util/initctl.c:1405 util/initctl.c:1416
1585-#: util/initctl.c:1427 util/initctl.c:1434
1586+#: util/initctl.c:2373 util/initctl.c:2385 util/initctl.c:2396
1587+#: util/initctl.c:2407 util/initctl.c:2414
1588 msgid "JOB [KEY=VALUE]..."
1589 msgstr ""
1590
1591-#: util/initctl.c:1394
1592+#: util/initctl.c:2374
1593 msgid "Start job."
1594 msgstr ""
1595
1596-#: util/initctl.c:1395
1597+#: util/initctl.c:2375
1598 msgid ""
1599 "JOB is the name of the job that is to be started, this may be followed by "
1600 "zero or more environment variables to be defined in the new job.\n"
1601@@ -485,11 +595,11 @@
1602 "an existing instance is already running."
1603 msgstr ""
1604
1605-#: util/initctl.c:1406
1606+#: util/initctl.c:2386
1607 msgid "Stop job."
1608 msgstr ""
1609
1610-#: util/initctl.c:1407
1611+#: util/initctl.c:2387
1612 msgid ""
1613 "JOB is the name of the job that is to be stopped, this may be followed by "
1614 "zero or more environment variables to be passed to the job's pre-stop and "
1615@@ -499,11 +609,11 @@
1616 "decide which of multiple instances will be stopped."
1617 msgstr ""
1618
1619-#: util/initctl.c:1417
1620+#: util/initctl.c:2397
1621 msgid "Restart job."
1622 msgstr ""
1623
1624-#: util/initctl.c:1418
1625+#: util/initctl.c:2398
1626 msgid ""
1627 "JOB is the name of the job that is to be restarted, this may be followed by "
1628 "zero or more environment variables to be defined in the job after "
1629@@ -513,66 +623,66 @@
1630 "decide which of multiple instances will be restarted."
1631 msgstr ""
1632
1633-#: util/initctl.c:1428
1634+#: util/initctl.c:2408
1635 msgid "Send HUP signal to job."
1636 msgstr ""
1637
1638-#: util/initctl.c:1429
1639+#: util/initctl.c:2409
1640 msgid ""
1641 "JOB is the name of the job that is to be sent the signal, this may be "
1642 "followed by zero or more environment variables to distinguish between job "
1643 "instances.\n"
1644 msgstr ""
1645
1646-#: util/initctl.c:1435
1647+#: util/initctl.c:2415
1648 msgid "Query status of job."
1649 msgstr ""
1650
1651-#: util/initctl.c:1436
1652+#: util/initctl.c:2416
1653 msgid ""
1654 "JOB is the name of the job that is to be queried, this may be followed by "
1655 "zero or more environment variables to distguish between job instances.\n"
1656 msgstr ""
1657
1658-#: util/initctl.c:1442
1659+#: util/initctl.c:2422
1660 msgid "List known jobs."
1661 msgstr ""
1662
1663-#: util/initctl.c:1443
1664+#: util/initctl.c:2423
1665 msgid "The known jobs and their current status will be output."
1666 msgstr ""
1667
1668-#: util/initctl.c:1446
1669+#: util/initctl.c:2426
1670 msgid "EVENT [KEY=VALUE]..."
1671 msgstr ""
1672
1673-#: util/initctl.c:1447
1674+#: util/initctl.c:2427
1675 msgid "Emit an event."
1676 msgstr ""
1677
1678-#: util/initctl.c:1448
1679+#: util/initctl.c:2428
1680 msgid ""
1681 "EVENT is the name of an event the init daemon should emit, this may be "
1682 "followed by zero or more environment variables to be included in the event.\n"
1683 msgstr ""
1684
1685-#: util/initctl.c:1454
1686+#: util/initctl.c:2434
1687 msgid "Reload the configuration of the init daemon."
1688 msgstr ""
1689
1690-#: util/initctl.c:1458
1691+#: util/initctl.c:2438
1692 msgid "Request the version of the init daemon."
1693 msgstr ""
1694
1695-#: util/initctl.c:1461
1696+#: util/initctl.c:2441
1697 msgid "[PRIORITY]"
1698 msgstr ""
1699
1700-#: util/initctl.c:1462
1701+#: util/initctl.c:2442
1702 msgid "Change the minimum priority of log messages from the init daemon"
1703 msgstr ""
1704
1705-#: util/initctl.c:1464
1706+#: util/initctl.c:2444
1707 msgid ""
1708 "PRIORITY may be one of:\n"
1709 " `debug' (messages useful for debugging upstart are logged, equivalent to --"
1710@@ -589,59 +699,83 @@
1711 "Without arguments, this outputs the current log priority."
1712 msgstr ""
1713
1714-#: util/reboot.c:114
1715+#: util/initctl.c:2461 util/initctl.c:2467
1716+msgid "[CONF]"
1717+msgstr ""
1718+
1719+#: util/initctl.c:2462
1720+msgid "Show emits, start on and stop on details for job configurations."
1721+msgstr ""
1722+
1723+#: util/initctl.c:2463
1724+msgid ""
1725+"If CONF specified, show configuration details for single job configuration, "
1726+"else show details for all jobs configurations.\n"
1727+msgstr ""
1728+
1729+#: util/initctl.c:2468
1730+msgid "Check for unreachable jobs/event conditions."
1731+msgstr ""
1732+
1733+#: util/initctl.c:2469
1734+msgid ""
1735+"List all jobs and events which cannot be satisfied by currently available "
1736+"job configuration files"
1737+msgstr ""
1738+
1739+#: util/reboot.c:113
1740 msgid "don't sync before reboot or halt"
1741 msgstr ""
1742
1743-#: util/reboot.c:116
1744+#: util/reboot.c:115
1745 msgid "force reboot or halt, don't call shutdown(8)"
1746 msgstr ""
1747
1748-#: util/reboot.c:118
1749+#: util/reboot.c:117
1750 msgid "switch off the power when called as halt"
1751 msgstr ""
1752
1753-#: util/reboot.c:120
1754+#: util/reboot.c:119
1755 msgid "don't actually reboot or halt, just write wtmp record"
1756 msgstr ""
1757
1758-#: util/reboot.c:145
1759+#: util/reboot.c:144
1760 msgid "Halt the system."
1761 msgstr ""
1762
1763-#: util/reboot.c:148
1764+#: util/reboot.c:147
1765 msgid "Power off the system."
1766 msgstr ""
1767
1768-#: util/reboot.c:151
1769+#: util/reboot.c:150
1770 msgid "Reboot the system."
1771 msgstr ""
1772
1773-#: util/reboot.c:155
1774+#: util/reboot.c:154
1775 msgid ""
1776 "This command is intended to instruct the kernel to reboot or halt the "
1777 "system; when run without the -f option, or when in a system runlevel other "
1778 "than 0 or 6, it will actually execute /sbin/shutdown.\n"
1779 msgstr ""
1780
1781-#: util/reboot.c:211
1782+#: util/reboot.c:210
1783 msgid "Calling shutdown"
1784 msgstr ""
1785
1786-#: util/reboot.c:214
1787+#: util/reboot.c:213
1788 #, c-format
1789 msgid "Unable to execute shutdown: %s"
1790 msgstr ""
1791
1792-#: util/reboot.c:235
1793+#: util/reboot.c:234
1794 msgid "Rebooting"
1795 msgstr ""
1796
1797-#: util/reboot.c:239
1798+#: util/reboot.c:238
1799 msgid "Halting"
1800 msgstr ""
1801
1802-#: util/reboot.c:243
1803+#: util/reboot.c:242
1804 msgid "Powering off"
1805 msgstr ""
1806
1807
1808=== modified file 'util/Makefile.am'
1809--- util/Makefile.am 2011-03-16 22:49:20 +0000
1810+++ util/Makefile.am 2011-06-06 16:54:17 +0000
1811@@ -29,7 +29,7 @@
1812 telinit
1813
1814 initctl_SOURCES = \
1815- initctl.c
1816+ initctl.c initctl.h
1817 nodist_initctl_SOURCES = \
1818 $(com_ubuntu_Upstart_OUTPUTS) \
1819 $(com_ubuntu_Upstart_Job_OUTPUTS) \
1820
1821=== modified file 'util/initctl.c'
1822--- util/initctl.c 2010-02-04 19:08:07 +0000
1823+++ util/initctl.c 2011-06-06 16:54:17 +0000
1824@@ -29,6 +29,7 @@
1825 #include <stdio.h>
1826 #include <stdlib.h>
1827 #include <unistd.h>
1828+#include <fnmatch.h>
1829
1830 #include <nih/macros.h>
1831 #include <nih/alloc.h>
1832@@ -38,10 +39,13 @@
1833 #include <nih/command.h>
1834 #include <nih/logging.h>
1835 #include <nih/error.h>
1836+#include <nih/hash.h>
1837+#include <nih/tree.h>
1838
1839 #include <nih-dbus/dbus_error.h>
1840 #include <nih-dbus/dbus_proxy.h>
1841 #include <nih-dbus/errors.h>
1842+#include <nih-dbus/dbus_connection.h>
1843
1844 #include "dbus/upstart.h"
1845
1846@@ -49,6 +53,9 @@
1847 #include "com.ubuntu.Upstart.Job.h"
1848 #include "com.ubuntu.Upstart.Instance.h"
1849
1850+#include "../init/events.h"
1851+#include "initctl.h"
1852+
1853
1854 /* Prototypes for local functions */
1855 NihDBusProxy *upstart_open (const void *parent)
1856@@ -63,6 +70,43 @@
1857 static void reply_handler (int *ret, NihDBusMessage *message);
1858 static void error_handler (void *data, NihDBusMessage *message);
1859
1860+static void job_class_condition_handler (void *data,
1861+ NihDBusMessage *message,
1862+ char ** const *value);
1863+
1864+static void job_class_condition_err_handler (void *data,
1865+ NihDBusMessage *message);
1866+
1867+static void job_class_parse_events (const ConditionHandlerData *data,
1868+ char ** const *variant_array);
1869+
1870+static void job_class_show_emits (const void *parent,
1871+ NihDBusProxy *job_class_proxy, const char *job_class_name);
1872+
1873+static void job_class_show_conditions (NihDBusProxy *job_class_proxy,
1874+ const char *job_class_name);
1875+
1876+static void eval_expr_tree (const char *expr, NihList **stack);
1877+
1878+static int check_condition (const char *job_class,
1879+ const char *condition, NihList *condition_list,
1880+ int *job_class_displayed)
1881+ __attribute__ ((warn_unused_result));
1882+
1883+static int tree_filter (void *data, NihTree *node);
1884+
1885+static void display_check_errors (const char *job_class,
1886+ const char *condition, NihTree *node);
1887+
1888+static int allow_job (const char *job);
1889+static int allow_event (const char *event);
1890+
1891+#ifndef TEST
1892+
1893+static int dbus_bus_type_setter (NihOption *option, const char *arg);
1894+static int ignored_events_setter (NihOption *option, const char *arg);
1895+
1896+#endif
1897
1898 /* Prototypes for option and command functions */
1899 int start_action (NihCommand *command, char * const *args);
1900@@ -75,15 +119,26 @@
1901 int reload_configuration_action (NihCommand *command, char * const *args);
1902 int version_action (NihCommand *command, char * const *args);
1903 int log_priority_action (NihCommand *command, char * const *args);
1904-
1905-
1906-/**
1907- * system_bus:
1908- *
1909- * Whether to connect to the init daemon on the D-Bus system bus or
1910- * privately.
1911- **/
1912-int system_bus = -1;
1913+int show_config_action (NihCommand *command, char * const *args);
1914+int check_config_action (NihCommand *command, char * const *args);
1915+
1916+
1917+/**
1918+ * use_dbus:
1919+ *
1920+ * If 1, connect using a D-Bus bus.
1921+ * If 0, connect using private connection.
1922+ * If -1, determine appropriate connection based on UID.
1923+ */
1924+int use_dbus = -1;
1925+
1926+/**
1927+ * dbus_bus_type:
1928+ *
1929+ * D-Bus bus to connect to (DBUS_BUS_SYSTEM or DBUS_BUS_SESSION), or -1
1930+ * to have an appropriate bus selected.
1931+ */
1932+int dbus_bus_type = -1;
1933
1934 /**
1935 * dest_name:
1936@@ -107,6 +162,107 @@
1937 **/
1938 int no_wait = FALSE;
1939
1940+/**
1941+ * enumerate_events:
1942+ *
1943+ * If TRUE, list out all events/jobs that a particular job *may require* to
1944+ * be run: essentially any event/job mentioned in a job configuration files
1945+ * "start on" / "stop on" condition. Used for showing dependencies
1946+ * between jobs and events.
1947+ **/
1948+int enumerate_events = FALSE;
1949+
1950+/**
1951+ * check_config_mode:
1952+ *
1953+ * If TRUE, parse all job configuration files looking for unreachable
1954+ * jobs/events.
1955+ **/
1956+int check_config_mode = FALSE;
1957+
1958+/**
1959+ * check_config_warn:
1960+ *
1961+ * If TRUE, check-config will generate a warning for *any* unreachable
1962+ * events/jobs.
1963+ **/
1964+int check_config_warn = FALSE;
1965+
1966+/**
1967+ * check_config_data:
1968+ *
1969+ * Used to record details of all known jobs and events.
1970+ **/
1971+CheckConfigData check_config_data;
1972+
1973+/**
1974+ * NihOption setter function to handle selection of appropriate D-Bus
1975+ * bus.
1976+ *
1977+ * Always returns 1 denoting success.
1978+ **/
1979+int
1980+dbus_bus_type_setter (NihOption *option, const char *arg)
1981+{
1982+ nih_assert (option);
1983+
1984+ if (! strcmp (option->long_option, "system")) {
1985+ use_dbus = TRUE;
1986+ dbus_bus_type = DBUS_BUS_SYSTEM;
1987+ }
1988+ else if (! strcmp (option->long_option, "session")) {
1989+ use_dbus = TRUE;
1990+ dbus_bus_type = DBUS_BUS_SESSION;
1991+ }
1992+
1993+ return 1;
1994+}
1995+
1996+
1997+/**
1998+ * NihOption setter function to handle specification of events to
1999+ * ignore.
2000+ *
2001+ * Returns 1 on success, else 0.
2002+ **/
2003+int
2004+ignored_events_setter (NihOption *option, const char *arg)
2005+{
2006+ NihError *err;
2007+ char **events;
2008+ char **event;
2009+ NihListEntry *entry;
2010+
2011+ nih_assert (option);
2012+ nih_assert (arg);
2013+
2014+ if (! check_config_data.ignored_events_hash)
2015+ check_config_data.ignored_events_hash = NIH_MUST (nih_hash_string_new (NULL, 0));
2016+
2017+ events = nih_str_split (NULL, arg, ",", TRUE);
2018+
2019+ if (!events) {
2020+ goto error;
2021+ }
2022+
2023+ for (event = events; event && *event; ++event) {
2024+ entry = NIH_MUST (nih_list_entry_new (check_config_data.ignored_events_hash));
2025+ entry->str = NIH_MUST (nih_strdup (entry, *event));
2026+ nih_hash_add (check_config_data.ignored_events_hash, &entry->entry);
2027+ }
2028+
2029+ nih_free (events);
2030+
2031+ return 1;
2032+
2033+error:
2034+ err = nih_error_get ();
2035+ nih_error ("%s", err->message);
2036+ nih_free (err);
2037+
2038+ return 0;
2039+}
2040+
2041
2042 /**
2043 * upstart_open:
2044@@ -132,17 +288,22 @@
2045 DBusConnection *connection;
2046 NihDBusProxy * upstart;
2047
2048- if (system_bus < 0)
2049- system_bus = getuid () ? TRUE : FALSE;
2050+ if (use_dbus < 0)
2051+ use_dbus = getuid () ? TRUE : FALSE;
2052+ if (use_dbus >= 0 && dbus_bus_type < 0)
2053+ dbus_bus_type = DBUS_BUS_SYSTEM;
2054
2055 dbus_error_init (&dbus_error);
2056- if (system_bus) {
2057+ if (use_dbus) {
2058 if (! dest_name)
2059 dest_name = DBUS_SERVICE_UPSTART;
2060
2061- connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error);
2062+ connection = dbus_bus_get (dbus_bus_type, &dbus_error);
2063 if (! connection) {
2064- nih_error ("%s: %s", _("Unable to connect to system bus"),
2065+ nih_error ("%s: %s",
2066+ dbus_bus_type == DBUS_BUS_SYSTEM
2067+ ? _("Unable to connect to system bus")
2068+ : _("Unable to connect to session bus"),
2069 dbus_error.message);
2070 dbus_error_free (&dbus_error);
2071 return NULL;
2072@@ -209,7 +370,7 @@
2073 * of the returned string are freed, the returned string will also be
2074 * freed.
2075 *
2076- * Returns: newly allocated string or NULL on raised error..
2077+ * Returns: newly allocated string or NULL on raised error.
2078 **/
2079 char *
2080 job_status (const void * parent,
2081@@ -663,6 +824,7 @@
2082 (JobClassRestartReply)start_reply_handler,
2083 error_handler, &job_path,
2084 NIH_DBUS_TIMEOUT_NEVER);
2085+
2086 if (! pending_call)
2087 goto error;
2088 }
2089@@ -1039,6 +1201,93 @@
2090 return 1;
2091 }
2092
2093+/**
2094+ * show_config_action:
2095+ * @command: NihCommand invoked,
2096+ * @args: command-line arguments.
2097+ *
2098+ * This function is called for the "show-config" command.
2099+ *
2100+ * Returns: command exit status.
2101+ **/
2102+int
2103+show_config_action (NihCommand * command,
2104+ char * const *args)
2105+{
2106+ nih_local NihDBusProxy *upstart = NULL;
2107+ nih_local char **job_class_paths = NULL;
2108+ const char *upstart_job_class = NULL;
2109+ NihError *err;
2110+
2111+ nih_assert (command != NULL);
2112+ nih_assert (args != NULL);
2113+
2114+ upstart = upstart_open (NULL);
2115+ if (! upstart)
2116+ return 1;
2117+
2118+ if (args[0]) {
2119+ /* Single job specified */
2120+ upstart_job_class = args[0];
2121+ job_class_paths = NIH_MUST (nih_alloc (NULL, 2*sizeof (char *)));
2122+ job_class_paths[1] = NULL;
2123+
2124+ if (upstart_get_job_by_name_sync (NULL, upstart, upstart_job_class,
2125+ job_class_paths) < 0)
2126+ goto error;
2127+ } else {
2128+ /* Obtain a list of jobs */
2129+ if (upstart_get_all_jobs_sync (NULL, upstart, &job_class_paths) < 0)
2130+ goto error;
2131+ }
2132+
2133+ for (char **job_class_path = job_class_paths;
2134+ job_class_path && *job_class_path; job_class_path++) {
2135+ nih_local NihDBusProxy *job_class = NULL;
2136+ nih_local char *job_class_name = NULL;
2137+
2138+ job_class = nih_dbus_proxy_new (NULL, upstart->connection,
2139+ upstart->name, *job_class_path,
2140+ NULL, NULL);
2141+ if (! job_class)
2142+ goto error;
2143+
2144+ job_class->auto_start = FALSE;
2145+
2146+ if (job_class_get_name_sync (NULL, job_class, &job_class_name) < 0)
2147+ goto error;
2148+
2149+ if (! check_config_mode)
2150+ nih_message ("%s", job_class_name);
2151+
2152+ job_class_show_emits (NULL, job_class, job_class_name);
2153+ job_class_show_conditions (job_class, job_class_name);
2154+
2155+ /* Add any jobs *without* "start on"/"stop on" conditions
2156+ * to ensure we have a complete list of jobs for check-config to work with.
2157+ */
2158+ if (check_config_mode) {
2159+ JobCondition *entry;
2160+
2161+ entry = (JobCondition *)nih_hash_lookup (check_config_data.job_class_hash,
2162+ job_class_name);
2163+ if (!entry) {
2164+ MAKE_JOB_CONDITION (check_config_data.job_class_hash,
2165+ entry, job_class_name);
2166+ nih_hash_add (check_config_data.job_class_hash, &entry->list);
2167+ }
2168+ }
2169+ }
2170+
2171+ return 0;
2172+
2173+error:
2174+ err = nih_error_get ();
2175+ nih_error ("%s", err->message);
2176+ nih_free (err);
2177+
2178+ return 1;
2179+}
2180
2181 /**
2182 * emit_action:
2183@@ -1216,6 +1465,80 @@
2184 }
2185
2186
2187+/**
2188+ * check_config_action:
2189+ * @command: NihCommand invoked,
2190+ * @args: command-line arguments.
2191+ *
2192+ * This function is called for the "check-config" command.
2193+ *
2194+ * Returns: command exit status.
2195+ **/
2196+int
2197+check_config_action (NihCommand *command,
2198+ char * const *args)
2199+{
2200+ int ret;
2201+ char *no_args[1] = { NULL };
2202+ char *job_class = NULL;
2203+
2204+ check_config_data.job_class_hash = NIH_MUST (nih_hash_string_new (NULL, 0));
2205+ check_config_data.event_hash = NIH_MUST (nih_hash_string_new (NULL, 0));
2206+
2207+ if (! check_config_data.ignored_events_hash)
2208+ check_config_data.ignored_events_hash =
2209+ NIH_MUST (nih_hash_string_new (NULL, 0));
2210+
2211+ /* Tell other functions we are running in a special mode */
2212+ check_config_mode = TRUE;
2213+
2214+ /* Obtain emits, start on and stop on data.
2215+ *
2216+ * Note: we pass null args since we always want details of all jobs.
2217+ */
2218+ ret = show_config_action (command, no_args);
2219+
2220+ if (ret)
2221+ return 0;
2222+
2223+ if (args[0]) {
2224+ NihList *entry;
2225+ job_class = args[0];
2226+
2227+ entry = nih_hash_lookup (check_config_data.job_class_hash, job_class);
2228+
2229+ if (! entry) {
2230+ nih_error ("%s: %s", _("Invalid job class"), job_class);
2231+ return 1;
2232+ }
2233+ }
2234+
2235+ NIH_HASH_FOREACH (check_config_data.job_class_hash, iter) {
2236+ JobCondition *j = (JobCondition *)iter;
2237+ int job_class_displayed = FALSE;
2238+
2239+ if (job_class && strcmp (job_class, j->job_class) != 0) {
2240+ /* user specified a job on the command-line,
2241+ * so only show that one.
2242+ */
2243+ continue;
2244+ }
2245+
2246+ ret += check_condition (j->job_class, "start on", j->start_on,
2247+ &job_class_displayed);
2248+
2249+ ret += check_condition (j->job_class, "stop on" , j->stop_on,
2250+ &job_class_displayed);
2251+ }
2252+
2253+ nih_free (check_config_data.job_class_hash);
2254+ nih_free (check_config_data.event_hash);
2255+ if (check_config_data.ignored_events_hash)
2256+ nih_free (check_config_data.ignored_events_hash);
2257+
2258+ return ret ? 1 : 0;
2259+}
2260+
2261 static void
2262 start_reply_handler (char ** job_path,
2263 NihDBusMessage *message,
2264@@ -1250,6 +1573,635 @@
2265 nih_free (err);
2266 }
2267
2268+/**
2269+ * job_class_parse_events:
2270+ * @condition_data: type of condition we are parsing (used as an indicator to
2271+ * user) and name of job,
2272+ * @variant_array: pointer to array of variants.
2273+ *
2274+ * The array of variants encodes the event operator tree in
2275+ * Reverse Polish Notation (RPN).
2276+ *
2277+ * Each variant is itself an array. There are two types:
2278+ *
2279+ * - An "Operator" (array length == 1).
2280+ *
2281+ * Operators are: "/AND" and "/OR".
2282+ *
2283+ * - An "Event" (array length >= 1).
2284+ *
2285+ * Each Event comprises a name (array element zero), followed by zero or
2286+ * more "Event Matches". If the first Event Match is of the form "JOB=name",
2287+ * or is a single token "name" (crucially not containining "="), then
2288+ * 'name' refers to the job which emitted the event.
2289+ **/
2290+void
2291+job_class_parse_events (const ConditionHandlerData *data, char ** const *variant_array)
2292+{
2293+ char ** const *variant;
2294+ char **arg;
2295+ char *token;
2296+ nih_local NihList *rpn_stack = NULL;
2297+ char *name = NULL;
2298+ const char *stanza_name;
2299+ const char *job_class_name;
2300+
2301+ nih_assert (data);
2302+
2303+ stanza_name = ((ConditionHandlerData *)data)->condition_name;
2304+ job_class_name = ((ConditionHandlerData *)data)->job_class_name;
2305+
2306+ if (! variant_array || ! *variant_array || ! **variant_array)
2307+ return;
2308+
2309+ STACK_CREATE (rpn_stack);
2310+ STACK_SHOW (rpn_stack);
2311+
2312+ for (variant = variant_array; variant && *variant && **variant; variant++, name = NULL) {
2313+
2314+ /* token is either the first token beyond the stanza name (ie the event name),
2315+ * or an operator.
2316+ */
2317+ token = **variant;
2318+
2319+ if (IS_OPERATOR (token)) {
2320+ /* Used to hold result of combining top two stack elements. */
2321+ nih_local char *new_token = NULL;
2322+
2323+ nih_local NihList *first = NULL;
2324+ nih_local NihList *second = NULL;
2325+
2326+ if (enumerate_events) {
2327+ /* We only care about operands in this mode. */
2328+ continue;
2329+ }
2330+
2331+ if (check_config_mode) {
2332+ /* Save token verbatim. */
2333+ new_token = NIH_MUST (nih_strdup (NULL, token));
2334+ STACK_PUSH_NEW_ELEM (rpn_stack, new_token);
2335+ continue;
2336+ }
2337+
2338+ first = NIH_MUST (nih_list_new (NULL));
2339+ second = NIH_MUST (nih_list_new (NULL));
2340+
2341+ /* Found an operator, so pop 2 values off stack,
2342+ * combine them and push back onto stack.
2343+ */
2344+ STACK_POP (rpn_stack, first);
2345+ STACK_POP (rpn_stack, second);
2346+
2347+ new_token = NIH_MUST (nih_strdup (NULL, ""));
2348+ new_token = NIH_MUST (nih_strcat_sprintf (&new_token,
2349+ NULL,
2350+ "(%s %s %s)",
2351+ ((NihListEntry *)second)->str,
2352+ IS_OP_AND (token) ? "and" : "or",
2353+ ((NihListEntry *)first)->str));
2354+
2355+ STACK_PUSH_NEW_ELEM (rpn_stack, new_token);
2356+ } else {
2357+ /* Save operand token (event or job), add
2358+ * arguments (job names and env vars) and push
2359+ * onto stack. If we are enumerating_events,
2360+ * this records the environment only.
2361+ */
2362+ nih_local char *element = NULL;
2363+ int i;
2364+
2365+ element = NIH_MUST (nih_strdup (NULL,
2366+ enumerate_events ? "" : token));
2367+
2368+ /* Handle arguments (job names and env vars). */
2369+ arg = (*variant)+1;
2370+
2371+ for (i=0; arg[i] && *arg[i]; i++) {
2372+ if ((enumerate_events || check_config_mode) && IS_JOB_EVENT (token)) {
2373+ if (!name) {
2374+ GET_JOB_NAME (name, i, arg[i]);
2375+ if (name)
2376+ continue;
2377+ }
2378+ }
2379+
2380+ if (! check_config_mode) {
2381+ element = NIH_MUST (nih_strcat (&element, NULL, " "));
2382+ element = NIH_MUST (nih_strcat (&element, NULL, arg[i]));
2383+ }
2384+ }
2385+
2386+ if (enumerate_events) {
2387+ nih_message (" %s %s (job:%s%s, env:%s)",
2388+ stanza_name,
2389+ token,
2390+ name ? " " : "",
2391+ name ? name : "",
2392+ element);
2393+ } else {
2394+ if (check_config_mode) {
2395+ element = NIH_MUST (nih_sprintf (NULL, "%s%s%s",
2396+ token,
2397+ name ? " " : "",
2398+ name ? name : ""));
2399+ }
2400+ STACK_PUSH_NEW_ELEM (rpn_stack, element);
2401+ }
2402+
2403+ }
2404+ }
2405+
2406+ if (enumerate_events)
2407+ return;
2408+
2409+ if (check_config_mode) {
2410+ int add = 0;
2411+ JobCondition *entry;
2412+
2413+ /* Create job class entry if necessary */
2414+ entry = (JobCondition *)nih_hash_lookup (check_config_data.job_class_hash, job_class_name);
2415+
2416+ if (!entry) {
2417+ add = 1;
2418+ MAKE_JOB_CONDITION (check_config_data.job_class_hash, entry, job_class_name);
2419+ }
2420+
2421+ /* Unstitch the conditions from the stack and stash them
2422+ * in the appropriate list for the job in question.
2423+ */
2424+ NIH_LIST_FOREACH_SAFE (rpn_stack, iter) {
2425+ NihListEntry *node = (NihListEntry *)iter;
2426+ NihList *l = !strcmp (stanza_name, "start on")
2427+ ? entry->start_on
2428+ : entry->stop_on;
2429+ nih_ref (node, l);
2430+ nih_unref (node, rpn_stack);
2431+ nih_list_add_after (l, &node->entry);
2432+ }
2433+
2434+ if (add)
2435+ nih_hash_add (check_config_data.job_class_hash, &entry->list);
2436+
2437+ return;
2438+ }
2439+
2440+ /* Handle case where a single event was specified (there
2441+ * was no operator to pop the entry off the stack).
2442+ */
2443+ if (! STACK_EMPTY (rpn_stack)) {
2444+ if (! enumerate_events) {
2445+ /* Our job is done: show the user what we found. */
2446+ nih_message (" %s %s", stanza_name,
2447+ STACK_PEEK (rpn_stack));
2448+ }
2449+ }
2450+}
2451+
2452+/**
2453+ * job_class_show_conditions:
2454+ * @job_class_proxy: D-Bus proxy for job class.
2455+ * @job_class_name: Name of config whose conditions we wish to display.
2456+ *
2457+ * Register D-Bus call-backs to display job classes start on and stop on
2458+ * conditions.
2459+ **/
2460+void
2461+job_class_show_conditions (NihDBusProxy *job_class_proxy, const char *job_class_name)
2462+{
2463+ DBusPendingCall *pending_call;
2464+ NihError *err;
2465+ ConditionHandlerData start_data, stop_data;
2466+
2467+ nih_assert (job_class_proxy);
2468+ nih_assert (job_class_name);
2469+
2470+ start_data.condition_name = "start on";
2471+ start_data.job_class_name = job_class_name;
2472+
2473+ stop_data.condition_name = "stop on";
2474+ stop_data.job_class_name = job_class_name;
2475+
2476+ pending_call = job_class_get_start_on (job_class_proxy,
2477+ job_class_condition_handler,
2478+ job_class_condition_err_handler,
2479+ &start_data,
2480+ NIH_DBUS_TIMEOUT_NEVER);
2481+
2482+ if (!pending_call)
2483+ goto error;
2484+
2485+ /* wait for completion */
2486+ dbus_pending_call_block (pending_call);
2487+ dbus_pending_call_unref (pending_call);
2488+
2489+ pending_call = job_class_get_stop_on (job_class_proxy,
2490+ job_class_condition_handler,
2491+ job_class_condition_err_handler,
2492+ &stop_data,
2493+ NIH_DBUS_TIMEOUT_NEVER);
2494+
2495+ if (!pending_call)
2496+ goto error;
2497+
2498+ /* wait for completion */
2499+ dbus_pending_call_block (pending_call);
2500+ dbus_pending_call_unref (pending_call);
2501+
2502+ return;
2503+
2504+error:
2505+ err = nih_error_get ();
2506+ nih_error ("%s", err->message);
2507+ nih_free (err);
2508+}
2509+
2510+/**
2511+ * job_class_show_emits:
2512+ * @parent: parent object,
2513+ * @job_class_proxy: D-Bus proxy for job class,
2514+ * @job_class_name: Name of job class that emits an event.
2515+ *
2516+ * Display events job class emits to user.
2517+ **/
2518+void
2519+job_class_show_emits (const void *parent, NihDBusProxy *job_class_proxy, const char *job_class_name)
2520+{
2521+ NihError *err;
2522+ nih_local char **job_emits = NULL;
2523+
2524+ nih_assert (job_class_proxy);
2525+
2526+ if (job_class_get_emits_sync (parent, job_class_proxy, &job_emits) < 0) {
2527+ goto error;
2528+ }
2529+
2530+ if (job_emits && *job_emits) {
2531+ char **p = job_emits;
2532+ while (*p) {
2533+ if (check_config_mode) {
2534+ /* Record event for later */
2535+ NihListEntry *node = NIH_MUST (nih_list_entry_new (check_config_data.event_hash));
2536+ node->str = NIH_MUST (nih_strdup (node, *p));
2537+ nih_hash_add_unique (check_config_data.event_hash, &node->entry);
2538+ }
2539+ else {
2540+ nih_message (" emits %s", *p);
2541+ }
2542+ p++;
2543+ }
2544+ }
2545+
2546+ return;
2547+
2548+error:
2549+ err = nih_error_get ();
2550+ nih_error ("%s", err->message);
2551+ nih_free (err);
2552+}
2553+
2554+/**
2555+ * job_class_condition_handler:
2556+ * @data: data passed via job_class_get_start_on() or job_class_get_stop_on(),
2557+ * @message: D-Bus message received,
2558+ * @value: array of variants generated by D-Bus call we are registering
2559+ * with.
2560+ *
2561+ * Handler for D-Bus message encoding a job classes "start on" or
2562+ * "stop on" condtions.
2563+ **/
2564+void
2565+job_class_condition_handler (void *data, NihDBusMessage *message, char ** const *value)
2566+{
2567+ job_class_parse_events ((const ConditionHandlerData *)data, value);
2568+}
2569+
2570+/**
2571+ * job_class_condition_err_handler:
2572+ *
2573+ * @data data passed via job_class_get_start_on() or job_class_get_stop_on(),
2574+ * @message: D-Bus message received.
2575+ *
2576+ * Error handler for D-Bus message encoding a job classes "start on" or
2577+ * "stop on" conditions.
2578+ **/
2579+void
2580+job_class_condition_err_handler (void *data, NihDBusMessage *message)
2581+{
2582+ /* no remedial action possible */
2583+}
2584+
2585+
2586+/**
2587+ * eval_expr_tree:
2588+ *
2589+ * @expr: expression to consider (see ExprNode),
2590+ * @stack: stack used to hold evaluated expressions.
2591+ *
2592+ * Evaluate @expr, in the context of @stack, creating ExprNode
2593+ * nodes as necessary.
2594+ *
2595+ * See ExprNode for details of @token.
2596+ **/
2597+void
2598+eval_expr_tree (const char *expr, NihList **stack)
2599+{
2600+ NihList *s;
2601+
2602+ nih_assert (stack);
2603+
2604+ s = *stack;
2605+
2606+ if (IS_OPERATOR (expr)) {
2607+ ExprNode *node, *first, *second;
2608+ NihListEntry *tmp = NULL;
2609+ NihListEntry *le;
2610+
2611+ /* make node for operator */
2612+ MAKE_EXPR_NODE (NULL, node, expr);
2613+
2614+ /* pop */
2615+ nih_assert (! NIH_LIST_EMPTY (s));
2616+ tmp = (NihListEntry *)nih_list_remove (s->next);
2617+
2618+ /* re-parent */
2619+ nih_ref (tmp, node);
2620+ nih_unref (tmp, s);
2621+
2622+ first = (ExprNode *)tmp->data;
2623+ nih_assert (first->value != -1);
2624+
2625+ /* attach to operator node */
2626+ nih_tree_add (&node->node, &first->node, NIH_TREE_LEFT);
2627+
2628+
2629+ /* pop */
2630+ nih_assert (! NIH_LIST_EMPTY (s));
2631+ tmp = (NihListEntry *)nih_list_remove (s->next);
2632+
2633+ /* re-parent */
2634+ nih_ref (tmp, node);
2635+ nih_unref (tmp, s);
2636+
2637+ second = (ExprNode *)tmp->data;
2638+ nih_assert (second->value != -1);
2639+
2640+ /* attach to operator node */
2641+ nih_tree_add (&node->node, &second->node, NIH_TREE_RIGHT);
2642+
2643+
2644+ /* Determine truth value for
2645+ * this node based on children
2646+ * and type of operator.
2647+ */
2648+ if (IS_OP_AND (expr) || check_config_warn)
2649+ node->value = first->value && second->value;
2650+ else
2651+ node->value = first->value || second->value;
2652+
2653+ /* Create list entry and hook
2654+ * node onto it.
2655+ */
2656+ le = NIH_MUST (nih_new (s, NihListEntry));
2657+ nih_list_init (&le->entry);
2658+ le->data = node;
2659+
2660+ /* push operator node */
2661+ nih_list_add_after (s, &le->entry);
2662+
2663+ } else {
2664+ char *event = NULL;
2665+ char *job = NULL;
2666+ NihListEntry *le;
2667+ ExprNode *en;
2668+ int errors = 0;
2669+
2670+ le = NIH_MUST (nih_new (s, NihListEntry));
2671+ nih_list_init (&le->entry);
2672+
2673+ MAKE_EXPR_NODE (le, en, expr);
2674+ le->data = en;
2675+
2676+ event = en->expr;
2677+
2678+ /* Determine the type of operand node we
2679+ * have.
2680+ */
2681+ job = strchr (en->expr, ' ');
2682+
2683+ /* found a job */
2684+ if (job) {
2685+ job++;
2686+ *(job-1) = '\0';
2687+
2688+ if (! allow_job (job)) {
2689+ errors++;
2690+
2691+ /* remember the error for later */
2692+ en->job_in_error = job;
2693+ }
2694+ }
2695+
2696+ /* handle event */
2697+ if (! allow_event (event)) {
2698+ errors++;
2699+ /* remember the error for later */
2700+ en->event_in_error = en->expr;
2701+ }
2702+
2703+ /* Determine if this this node is in error (0 means yes) */
2704+ en->value = errors ? 0 : 1;
2705+
2706+ nih_list_add_after (s, &le->entry);
2707+ }
2708+}
2709+
2710+
2711+/**
2712+ * check_condition:
2713+ *
2714+ * @job_class: name of job class,
2715+ * @condition: name of condition,
2716+ * @condition_list: conditions to check.
2717+ * @job_class_displayed: Will be set to TRUE when an error node is found
2718+ * and the job class has been displayed to the user.
2719+ *
2720+ * Evaluate all expression tree nodes in @condition_list looking for
2721+ * errors.
2722+ *
2723+ * Returns error count.
2724+ **/
2725+int
2726+check_condition (const char *job_class, const char *condition, NihList *condition_list,
2727+ int *job_class_displayed)
2728+{
2729+ nih_local NihList *stack = NULL;
2730+ NihTree *root = NULL;
2731+ int errors = 0;
2732+
2733+ nih_assert (job_class);
2734+ nih_assert (condition);
2735+
2736+ if (! condition_list || NIH_LIST_EMPTY (condition_list))
2737+ return 0;
2738+
2739+ STACK_CREATE (stack);
2740+ STACK_SHOW (stack);
2741+
2742+ NIH_LIST_FOREACH (condition_list, iter) {
2743+ NihListEntry *e = (NihListEntry *)iter;
2744+
2745+ eval_expr_tree (e->str, &stack);
2746+ }
2747+
2748+ root = &(((ExprNode *)((NihListEntry *)stack->next)->data)->node);
2749+
2750+ if (! root)
2751+ /* no conditions found */
2752+ return 0;
2753+
2754+ /* Look through the expression tree for nodes in error and
2755+ * display them.
2756+ */
2757+ NIH_TREE_FOREACH_PRE_FULL (root, iter, tree_filter, root) {
2758+ ExprNode *e = (ExprNode *)iter;
2759+
2760+ if ( e->value != 1 ) {
2761+ errors++;
2762+ if ( *job_class_displayed == FALSE) {
2763+ nih_message ("%s", job_class);
2764+ *job_class_displayed = TRUE;
2765+ }
2766+
2767+ display_check_errors (job_class, condition, iter);
2768+ break;
2769+ }
2770+ }
2771+ return errors;
2772+}
2773+
2774+/**
2775+ * display_check_errors:
2776+ *
2777+ * @job_class: name of job class,
2778+ * @condition: name of condition,
2779+ * @node: tree node that is in error.
2780+ *
2781+ * Display error details from expression tree nodes
2782+ * that are in error.
2783+ *
2784+ * Note that this function should only be passed operand nodes or the
2785+ * root node.
2786+ **/
2787+void
2788+display_check_errors (const char *job_class, const char *condition, NihTree *node)
2789+{
2790+ nih_assert (job_class);
2791+ nih_assert (node);
2792+
2793+ NIH_TREE_FOREACH_POST (node, iter) {
2794+ ExprNode *expr = (ExprNode *)iter;
2795+ const char *event = expr->event_in_error;
2796+ const char *job = expr->job_in_error;
2797+
2798+ if (event)
2799+ nih_message (" %s: %s %s", condition,
2800+ _("unknown event"), event);
2801+
2802+ if (job)
2803+ nih_message (" %s: %s %s", condition,
2804+ _("unknown job"), job);
2805+ }
2806+}
2807+
2808+
2809+/**
2810+ * tree_filter:
2811+ *
2812+ * @data: node representing root of tree,
2813+ * @node: node to consider.
2814+ *
2815+ * Node filter for NIH_TREE_FOREACH_PRE_FULL that ensures only
2816+ * operator nodes and the root node are to be considered.
2817+ *
2818+ * Returns FALSE if node should be considered, else TRUE.
2819+ *
2820+ **/
2821+int
2822+tree_filter (void *data, NihTree *node)
2823+{
2824+ NihTree *root = (NihTree *)data;
2825+ ExprNode *e = (ExprNode *)node;
2826+
2827+ nih_assert (root);
2828+ nih_assert (e);
2829+
2830+ if (IS_OPERATOR (e->expr) || node == root)
2831+ return FALSE;
2832+
2833+ /* ignore */
2834+ return TRUE;
2835+}
2836+
2837+
2838+/**
2839+ * allow_job:
2840+ *
2841+ * @job: name of job to check.
2842+ *
2843+ * Returns TRUE if @job is recognized or can be ignored,
2844+ * else FALSE.
2845+ **/
2846+int
2847+allow_job (const char *job)
2848+{
2849+ NihList *found;
2850+
2851+ nih_assert (job);
2852+
2853+ found = nih_hash_lookup (check_config_data.job_class_hash, job);
2854+
2855+ /* The second part of this test ensures we ignore the (unusual)
2856+ * situation whereby the condition references a
2857+ * variable (for example an instance).
2858+ */
2859+ if (!found && job[0] != '$')
2860+ return FALSE;
2861+
2862+ return TRUE;
2863+}
2864+
2865+
2866+/**
2867+ * allow_event:
2868+ *
2869+ * @event: name of event to check.
2870+ *
2871+ * Returns TRUE if @event is recognized or can be ignored,
2872+ * else FALSE.
2873+ **/
2874+int
2875+allow_event (const char *event)
2876+{
2877+ nih_assert (event);
2878+
2879+ NIH_HASH_FOREACH (check_config_data.event_hash, iter) {
2880+ NihListEntry *entry = (NihListEntry *)iter;
2881+
2882+ /* handles expansion of any globs */
2883+ if (fnmatch (entry->str, event, 0) == 0)
2884+ goto out;
2885+ }
2886+
2887+ if (IS_INIT_EVENT (event) ||
2888+ nih_hash_lookup (check_config_data.ignored_events_hash, event))
2889+ goto out;
2890+
2891+ return FALSE;
2892+
2893+out:
2894+ return TRUE;
2895+}
2896+
2897
2898 #ifndef TEST
2899 /**
2900@@ -1258,9 +2210,11 @@
2901 * Command-line options accepted for all arguments.
2902 **/
2903 static NihOption options[] = {
2904+ { 0, "session", N_("use D-Bus session bus to connect to init daemon (for testing)"),
2905+ NULL, NULL, NULL, dbus_bus_type_setter },
2906 { 0, "system", N_("use D-Bus system bus to connect to init daemon"),
2907- NULL, NULL, &system_bus, NULL },
2908- { 0, "dest", N_("destination well-known name on system bus"),
2909+ NULL, NULL, NULL, dbus_bus_type_setter },
2910+ { 0, "dest", N_("destination well-known name on D-Bus bus"),
2911 NULL, "NAME", &dest_name, NULL },
2912
2913 NIH_OPTION_LAST
2914@@ -1371,6 +2325,32 @@
2915
2916
2917 /**
2918+ * show_config_options:
2919+ *
2920+ * Command-line options accepted for the show-config command.
2921+ **/
2922+NihOption show_config_options[] = {
2923+ { 'e', "enumerate",
2924+ N_("enumerate list of events and jobs causing job "
2925+ "created from job config to start/stop"),
2926+ NULL, NULL, &enumerate_events, NULL },
2927+
2928+ NIH_OPTION_LAST
2929+};
2930+
2931+/**
2932+ * check_config_options:
2933+ *
2934+ * Command-line options accepted for the check-config command.
2935+ **/
2936+NihOption check_config_options[] = {
2937+ { 'i', "ignore-events", N_("ignore specified list of events (comma-separated)"),
2938+ NULL, "EVENT_LIST", NULL, ignored_events_setter },
2939+ { 'w', "warn", N_("Generate warning for any unreachable events/jobs"),
2940+ NULL, NULL, &check_config_warn, NULL },
2941+ NIH_OPTION_LAST
2942+};
2943+/**
2944 * job_group:
2945 *
2946 * Group of commands related to jobs.
2947@@ -1478,10 +2458,27 @@
2948 "Without arguments, this outputs the current log priority."),
2949 NULL, log_priority_options, log_priority_action },
2950
2951+ { "show-config", N_("[CONF]"),
2952+ N_("Show emits, start on and stop on details for job configurations."),
2953+ N_("If CONF specified, show configuration details for single job "
2954+ "configuration, else show details for all jobs configurations.\n"),
2955+ NULL, show_config_options, show_config_action },
2956+
2957+ { "check-config", N_("[CONF]"),
2958+ N_("Check for unreachable jobs/event conditions."),
2959+ N_("List all jobs and events which cannot be satisfied by "
2960+ "currently available job configuration files"),
2961+ NULL, check_config_options, check_config_action },
2962+
2963+
2964 NIH_COMMAND_LAST
2965 };
2966
2967
2968+
2969+
2970+
2971+
2972 int
2973 main (int argc,
2974 char *argv[])
2975
2976=== added file 'util/initctl.h'
2977--- util/initctl.h 1970-01-01 00:00:00 +0000
2978+++ util/initctl.h 2011-06-06 16:54:17 +0000
2979@@ -0,0 +1,458 @@
2980+/* upstart
2981+ *
2982+ * Copyright © 2011 Canonical Ltd.
2983+ * Author: James Hunt <james.hunt@canonical.com>
2984+ *
2985+ * This program is free software; you can redistribute it and/or modify
2986+ * it under the terms of the GNU General Public License version 2, as
2987+ * published by the Free Software Foundation.
2988+ *
2989+ * This program is distributed in the hope that it will be useful,
2990+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2991+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2992+ * GNU General Public License for more details.
2993+ *
2994+ * You should have received a copy of the GNU General Public License along
2995+ * with this program; if not, write to the Free Software Foundation, Inc.,
2996+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2997+ */
2998+
2999+#ifndef INITCTL_H
3000+#define INITCTL_H
3001+
3002+/**
3003+ * IS_OP_AND:
3004+ * @token: string token to check.
3005+ *
3006+ * Return TRUE if @token is an 'AND' operator, else FALSE.
3007+ **/
3008+#define IS_OP_AND(token) \
3009+ (! strcmp ((token), "/AND"))
3010+
3011+/**
3012+ * IS_OP_OR:
3013+ * @token: string token to check.
3014+ *
3015+ * Return TRUE if @token is an 'OR' operator, else FALSE.
3016+ **/
3017+#define IS_OP_OR(token) \
3018+ (! strcmp ((token), "/OR"))
3019+
3020+/**
3021+ * IS_OPERATOR
3022+ * @token: string token to check.
3023+ *
3024+ * Return TRUE if @token is either an 'AND' or an 'OR' operator,
3025+ * else FALSE.
3026+ **/
3027+#define IS_OPERATOR(token) \
3028+ IS_OP_AND (token) || IS_OP_OR (token)
3029+
3030+
3031+/**
3032+ * GET_JOB_NAME:
3033+ *
3034+ * @var: char pointer variable which will be set to the name
3035+ * of a job, or NULL,
3036+ * @index: zero-based index of tokens where zero represents
3037+ * the first token after the initial token,
3038+ * @token: string to check.
3039+ *
3040+ * Determine name of job considering the specified token and its
3041+ * index. If not a job, sets @name to NULL.
3042+ *
3043+ * Handles the following cases:
3044+ *
3045+ * [start|stop] on <job_event> foo
3046+ * (index==0, job="foo" => name="foo")
3047+ *
3048+ * [start|stop] on <job_event> JOB=foo
3049+ * (index==0, job="JOB=foo" => name="foo")
3050+ *
3051+ * [start|stop] on <job_event> A=B JOB=foo
3052+ * (index==1, job="JOB=foo" => name="foo")
3053+ *
3054+ * [start|stop] on <job_event> A=B c=hello JOB=foo
3055+ * (index==2, job="JOB=foo" => name="foo")
3056+ *
3057+ * [start|stop] on <job_event> $JOB A=B c=hello
3058+ * (index==0, job="$JOB" => name="$JOB")
3059+ *
3060+ **/
3061+#define GET_JOB_NAME(var, index, token) \
3062+{ \
3063+ char *_##var; \
3064+ \
3065+ nih_assert (index >= 0); \
3066+ nih_assert (token); \
3067+ \
3068+ var = NULL; \
3069+ \
3070+ _##var = strstr (token, "JOB="); \
3071+ \
3072+ if (_##var && _##var == token) \
3073+ var = _##var + strlen ("JOB="); \
3074+ else if (index == 0 ) { \
3075+ if (!strstr (token, "=")) \
3076+ var = token; \
3077+ } \
3078+}
3079+
3080+
3081+/**
3082+ * IS_JOB_EVENT:
3083+ * @token: string to check.
3084+ *
3085+ * Return TRUE if specified token refers to a standard job event, else
3086+ * FALSE.
3087+ **/
3088+#define IS_JOB_EVENT(token) \
3089+ (!strcmp (token, JOB_STARTING_EVENT) || \
3090+ !strcmp (token, JOB_STARTED_EVENT) || \
3091+ !strcmp (token, JOB_STOPPING_EVENT) || \
3092+ !strcmp (token, JOB_STOPPED_EVENT))
3093+
3094+/**
3095+ * IS_INIT_EVENT:
3096+ * @token: string to check.
3097+ *
3098+ * Return TRUE if specified token refers to an event emitted internally,
3099+ * else FALSE.
3100+ *
3101+ * Note: the raw string entries below are required to accommodate
3102+ * production versus debug builds (STARTUP_EVENT changes name depending
3103+ * on build type).
3104+ **/
3105+#define IS_INIT_EVENT(token) \
3106+ (!strcmp (token, STARTUP_EVENT) || \
3107+ !strcmp (token, "debug") || \
3108+ !strcmp (token, "startup") || \
3109+ !strcmp (token, CTRLALTDEL_EVENT) || \
3110+ !strcmp (token, KBDREQUEST_EVENT) || \
3111+ !strcmp (token, PWRSTATUS_EVENT) || \
3112+ IS_JOB_EVENT (token))
3113+
3114+/**
3115+ * STACK_EMPTY:
3116+ * @stack: address of stack to check.
3117+ *
3118+ * Return TRUE if @stack is empty, else FALSE.
3119+ **/
3120+#define STACK_EMPTY(stack) \
3121+ (NIH_LIST_EMPTY (stack))
3122+
3123+/**
3124+ * STACK_CREATE:
3125+ * @name: nih_list variable to assign stack to.
3126+ **/
3127+#define STACK_CREATE(name) \
3128+ name = NIH_MUST (nih_list_new (NULL))
3129+/**
3130+ * STACK_SHOW_POP:
3131+ * @stack: Address of stack,
3132+ * @str: string representing element popped off stack.
3133+ *
3134+ * Display message to denote that @str has been popped off @stack.
3135+ * Does nothing if debug build not enabled.
3136+ *
3137+ * Note: we cannot assert that stack is not empty since a caller may
3138+ * have just removed the last entry before calling us. Thus, it is up to
3139+ * the caller to perform such checks.
3140+ *
3141+ * Does nothing if debug build not enabled.
3142+ **/
3143+#ifdef DEBUG_STACK
3144+#define STACK_SHOW_POP(stack, str) \
3145+ STACK_SHOW_CHANGE (stack, "popped", str)
3146+#else
3147+#define STACK_SHOW_POP(stack, str)
3148+#endif
3149+
3150+/**
3151+ * STACK_SHOW_PUSH:
3152+ * @stack: Address of stack,
3153+ * @str: string representing element pushed onto stack.
3154+ *
3155+ * Display message to denote that @str has been pushed onto @stack.
3156+ *
3157+ * Does nothing if debug build not enabled.
3158+ **/
3159+#ifdef DEBUG_STACK
3160+#define STACK_SHOW_PUSH(stack, str) \
3161+ STACK_SHOW_CHANGE (stack, "pushed", str) \
3162+ \
3163+ nih_assert (! STACK_EMPTY (stack))
3164+#else
3165+#define STACK_SHOW_PUSH(stack, str)
3166+#endif
3167+
3168+/**
3169+ * STACK_SHOW:
3170+ * @stack: Address of stack.
3171+ *
3172+ * Display contents of @stack.
3173+ *
3174+ * Does nothing if debug build not enabled.
3175+ **/
3176+#ifdef DEBUG_STACK
3177+#define STACK_SHOW(stack) \
3178+{ \
3179+ size_t depth = 0; \
3180+ \
3181+ nih_assert (stack); \
3182+ \
3183+ NIH_LIST_FOREACH (stack, iter) { \
3184+ depth++; \
3185+ } \
3186+ \
3187+ if (STACK_EMPTY (stack)) { \
3188+ nih_message ("STACK@%p: empty", stack); \
3189+ } else { \
3190+ for (NihList *iter = (stack)->next; \
3191+ iter != (stack) && \
3192+ ((NihListEntry *)iter)->str; \
3193+ iter = iter->next, depth--) { \
3194+ nih_message ("STACK@%p[%u]='%s'", \
3195+ (void *)stack, \
3196+ (unsigned int)(depth-1), \
3197+ ((NihListEntry *)iter)->str); \
3198+ } \
3199+ } \
3200+}
3201+#else
3202+#define STACK_SHOW(stack)
3203+#endif
3204+
3205+/**
3206+ * STACK_SHOW_CHANGE:
3207+ * @stack: Address of stack,
3208+ * @msg: message to show a stack change,
3209+ * @element_str: string representing element changed on @stack.
3210+ *
3211+ * Display a message showing that the stack has been changed.
3212+ *
3213+ * Does nothing if debug build not enabled.
3214+ **/
3215+#ifdef DEBUG_STACK
3216+#define STACK_SHOW_CHANGE(stack, msg, element_str) \
3217+ nih_assert (msg); \
3218+ nih_assert (element_str); \
3219+ \
3220+ nih_message ("STACK@%p: %s '%s'", \
3221+ (void *)stack, msg, element_str); \
3222+ \
3223+ STACK_SHOW (stack); \
3224+ \
3225+ nih_message (" "); /* spacer */
3226+#else
3227+#define STACK_SHOW_CHANGE(stack, msg, element_str)
3228+#endif
3229+
3230+/**
3231+ * STACK_PUSH:
3232+ * @stack: Address of stack,
3233+ * @elem: element of type NihListEntry to add to stack.
3234+ *
3235+ * Add @elem to @stack and display message showing how stack changed.
3236+ **/
3237+#define STACK_PUSH(stack, elem) \
3238+ nih_list_add_after (stack, &(elem)->entry); \
3239+ \
3240+ STACK_SHOW_PUSH (stack, \
3241+ ((NihListEntry *)(elem))->str); \
3242+ \
3243+ nih_assert (! STACK_EMPTY (stack))
3244+
3245+/**
3246+ * STACK_PUSH_NEW_ELEM:
3247+ * @stack: Address of stack,
3248+ * @string: String to convert into a new NihListEntry
3249+ * stack element and push onto @stack.
3250+ *
3251+ * Create new stack element from @string and push onto @stack,
3252+ * displaying a message showing how stack changed.
3253+ **/
3254+#define STACK_PUSH_NEW_ELEM(stack, string) \
3255+{ \
3256+ NihListEntry *e; \
3257+ \
3258+ nih_assert (stack); \
3259+ nih_assert (string); \
3260+ \
3261+ e = NIH_MUST (nih_new (stack, NihListEntry)); \
3262+ nih_list_init (&e->entry); \
3263+ e->str = NIH_MUST (nih_strdup (e, string)); \
3264+ STACK_PUSH (stack, e); \
3265+}
3266+
3267+/**
3268+ * STACK_POP:
3269+ * @stack: Address of stack,
3270+ * @list: list which top stack element will be added to.
3271+ *
3272+ * Remove top element from @stack, returning to caller as @list
3273+ * and display message showing how stack changed.
3274+ *
3275+ * Note that @list is assumed to have had nih_list_new() called
3276+ * on it already.
3277+ **/
3278+#define STACK_POP(stack, list) \
3279+ nih_assert (stack); \
3280+ nih_assert ((stack)->next); \
3281+ \
3282+ list = nih_list_add (list, (stack)->next); \
3283+ STACK_SHOW_POP (stack, \
3284+ ((NihListEntry *)(list))->str)
3285+
3286+/**
3287+ * STACK_PEEK:
3288+ * @stack: Address of stack.
3289+ *
3290+ * Return string value of top element on stack.
3291+ **/
3292+#define STACK_PEEK(stack) \
3293+ (((NihListEntry *)(stack)->next)->str)
3294+
3295+/**
3296+ * JobCondition:
3297+ *
3298+ * @list: list that @list lives on,
3299+ * @job_class: name of job class,
3300+ * @start_on: start on conditions,
3301+ * @stop_on: stop on conditions.
3302+ *
3303+ * Structure used to represent a job classes start on and stop on
3304+ * conditions.
3305+ *
3306+ * Note that @start_on and @stop_on are lists of NihListEntry
3307+ * objects containing string data.
3308+ *
3309+ *
3310+ **/
3311+typedef struct condition {
3312+ NihList list;
3313+
3314+ const char *job_class;
3315+ NihList *start_on;
3316+ NihList *stop_on;
3317+} JobCondition;
3318+
3319+/**
3320+ * CheckConfigData:
3321+ *
3322+ * @job_class_hash: Job classes (.conf files)
3323+ * currently installed,
3324+ * @event_hash: Events that are documented
3325+ * as being emitted,
3326+ * @ignored_events_hash: Rvents we wish to ignore.
3327+ *
3328+ * Notes:
3329+ *
3330+ * Keys for @job_class_hash are job class names and values
3331+ * are of type JobCondition.
3332+ *
3333+ * Keys of @event_hash are event names and values are of type
3334+ * NihListEntry holding the event name as a string.
3335+ *
3336+ * Keys of @ignored_events_hash are event names and values are of type
3337+ * NihListEntry holding the event name as a string.
3338+ **/
3339+typedef struct check_config_data {
3340+ NihHash *job_class_hash;
3341+ NihHash *event_hash;
3342+ NihHash *ignored_events_hash;
3343+} CheckConfigData;
3344+
3345+
3346+/**
3347+ * ConditionHandlerData:
3348+ *
3349+ * @condition_name: "start on" or "stop on",
3350+ * @job_class_name: name of *.conf file less the extension.
3351+ *
3352+ * Used to pass multiple values to job_class_get_start_on() /
3353+ * job_class_get_stop_on() handlers.
3354+ *
3355+ **/
3356+typedef struct condition_handler_data {
3357+ const char *condition_name;
3358+ const char *job_class_name;
3359+} ConditionHandlerData;
3360+
3361+
3362+/**
3363+ * ExprNode:
3364+ *
3365+ * @node: tree which node lives in,
3366+ * @expr: string representing the expression,
3367+ * @job_in_error: if not NULL, points to the appropriate portion of
3368+ * @expr where the erroneous job is,
3369+ * @event_in_error: if not NULL, points to the appropriate portion of
3370+ * @expr where the erroneous event is,
3371+ * @value: Truth value of this node (and its children, if any).
3372+ *
3373+ * Node representing an expression.
3374+ *
3375+ * Notes:
3376+ *
3377+ * @expr can be one of:
3378+ *
3379+ * - operator:
3380+ * - IS_OP_AND()
3381+ * - IS_OP_OR()
3382+ * - operand:
3383+ * - "<event>"
3384+ * - "<event> <job>"
3385+ *
3386+ * @value can be:
3387+ *
3388+ * - 0 denoting node (and its children) are in error.
3389+ * - 1 denoting no errors in this node or its children.
3390+ * - -1 denoting an uninitialized value.
3391+ */
3392+typedef struct expression_node {
3393+ NihTree node;
3394+
3395+ char *expr;
3396+ const char *job_in_error;
3397+ const char *event_in_error;
3398+ int value;
3399+} ExprNode;
3400+
3401+
3402+/**
3403+ * MAKE_EXPR_NODE:
3404+ *
3405+ * @parent: parent object,
3406+ * @entry: pointer to ExprNode to initialize,
3407+ * @str: string expression which will be copied into @entry.
3408+ *
3409+ * Allocate storage for an ExprNode pointer and initialize.
3410+ **/
3411+#define MAKE_EXPR_NODE(parent, entry, str) \
3412+ entry = NIH_MUST (nih_new (parent, ExprNode)); \
3413+ nih_tree_init (&(entry)->node); \
3414+ (entry)->expr = (str) \
3415+ ? NIH_MUST (nih_strdup (entry, (str))) \
3416+ : NULL; \
3417+ (entry)->job_in_error = NULL; \
3418+ (entry)->event_in_error = NULL; \
3419+ (entry)->value = -1
3420+
3421+/**
3422+ * MAKE_JOB_CONDITION:
3423+ *
3424+ * @parent: parent object,
3425+ * @entry: pointer to JobCondition to initialize,
3426+ * @str: string expression which will be copied into @entry.
3427+ *
3428+ * Allocate storage for an JobCondition pointer and initialize.
3429+ **/
3430+#define MAKE_JOB_CONDITION(parent, entry, str) \
3431+ entry = NIH_MUST (nih_new (parent, JobCondition)); \
3432+ nih_list_init (&(entry)->list); \
3433+ (entry)->job_class = NIH_MUST (nih_strdup (entry, str)); \
3434+ (entry)->start_on = NIH_MUST (nih_list_new (entry)); \
3435+ (entry)->stop_on = NIH_MUST (nih_list_new (entry))
3436+
3437+#endif /* INITCTL_H */
3438
3439=== modified file 'util/man/initctl.8'
3440--- util/man/initctl.8 2010-02-04 19:08:07 +0000
3441+++ util/man/initctl.8 2011-06-06 16:54:17 +0000
3442@@ -1,4 +1,4 @@
3443-.TH initctl 8 2010-02-04 "Upstart"
3444+.TH initctl 8 2011-06-01 "Upstart"
3445 .\"
3446 .SH NAME
3447 initctl \- init daemon control tool
3448@@ -33,6 +33,12 @@
3449 .\"
3450 .SH OPTIONS
3451 .TP
3452+.B --session
3453+Connect to
3454+.BR init (8)
3455+daemon using the D-Bus session bus (for testing purposes only).
3456+.\"
3457+.TP
3458 .B --system
3459 Communication with the
3460 .BR init (8)
3461@@ -339,6 +345,161 @@
3462 .BR init (8)
3463 daemon will log and ouputs to standard output.
3464 .\"
3465+.TP
3466+.B show-config
3467+.RI [ OPTIONS "] [" CONF "]"
3468+
3469+Display emits, start on and stop on job configuration details (in that
3470+order) for specified job configuration, \fICONF\fP. If \fICONF\fP is not
3471+specified, list information for all valid job configurations.
3472+
3473+Note that a job configuration is the name of a job configuration file,
3474+without the extension. Note too that this information is static: it
3475+does not refer to any running job.
3476+
3477+For each event emitted, a separate line is displayed beginning with two
3478+space characters followed by, \'emits \fIevent\fP\' where \'\fIevent\fP\'
3479+denotes a single emitted event.
3480+
3481+The \fBstart on\fP and \fBstop on\fP conditions
3482+are listed on separate lines beginning with two space characters and
3483+followed by \'start on\' and \'stop on\' respectively and ending with
3484+the appropriate condition.
3485+
3486+If a job configuration has no emits, start on, or stop on conditions,
3487+the name of the job configuration will be displayed with no further
3488+details.
3489+
3490+Note that the \fBstart on\fP and \fBstop on\fP conditions will be fully
3491+bracketed, regardless of whether they appear like this in the job
3492+configuration file. This is useful to see how the
3493+.BR init (8)
3494+daemon perceives the condition.
3495+
3496+Example output:
3497+
3498+.nf
3499+foo
3500+ emits boing
3501+ emits blip
3502+ start on (starting A and (B or C var=2))
3503+ stop on (bar HELLO=world testing=123 or stopping wibble)
3504+.fi
3505+
3506+.B OPTIONS
3507+.RS
3508+.IP "\fB\-e\fP, \fB\-\-enumerate\fP"
3509+
3510+If specified, rather than listing the precise \fBstart on\fP and \fBstop
3511+on\fP conditions, outputs the emits lines along with one line for each
3512+event or job the \fICONF\fP in question \fImay\fP be started or stopped
3513+by if it were to become a job. If the start on condition specifies a
3514+non-job event, this will be listed verbatim, whereas for a job event,
3515+the name of the \fIjob\fP as opposed to the event the job emits will be
3516+listed.
3517+
3518+The type of entity, its triggering event (if appropriate) and its full
3519+environment is displayed in brackets following its name for clarity.
3520+
3521+This option is useful for tools which generate graphs of relationships
3522+between jobs and events. It is also instructive since it shows how the
3523+.BR init (8)
3524+daemon has parsed the job configuration file.
3525+
3526+Example output (an analog of the default output format above):
3527+
3528+.nf
3529+foo
3530+ emits boing
3531+ emits blip
3532+ start on starting (job: A, env:)
3533+ start on B (job:, env:)
3534+ start on C (job:, env: var=2)
3535+ stop on bar (job:, env: HELLO=world testing=123)
3536+ stop on stopping (job: wibble, event: stopping, env:)
3537+.fi
3538+.RE
3539+.\"
3540+.TP
3541+.B check-config
3542+.RI [ OPTIONS "] [" CONF "]"
3543+
3544+Considers all job configurations looking for jobs that cannot be started
3545+or stopped, given the currently available job configurations. This is
3546+achieved by considering the start on, stop on and emits stanzas for each
3547+job configuration and identifying unreachable scenarios.
3548+
3549+This option is useful for determining the impact of adding or removing
3550+job configuration files.
3551+
3552+Note that to use this command, it is necessary to ensure that all job
3553+configuration files advertise the events they emit correctly.
3554+
3555+If errors are identified, the name of the job configuration will be
3556+displayed. Subsequent lines will show the failed conditions for the job
3557+configuration, one per line. Condition lines begin with two spaces and
3558+are followed with either "start on: " or "stop on: ", the word
3559+"unknown", the type of entity that is not known and finally its name.
3560+
3561+Note that only job configurations that are logically in error (those
3562+with unsatisfiable conditions) will be displayed. Note too that job
3563+configurations that are syntactically invalid may trigger an error if
3564+they would cause a condition to be in error.
3565+
3566+Assuming job configuration file \fI/etc/init/foo.conf\fP contains the
3567+following:
3568+
3569+.nf
3570+ start on starting grape
3571+ stop on peach
3572+.fi
3573+
3574+The check-config command might display:
3575+
3576+.nf
3577+ foo
3578+ start on: unknown job grape
3579+ stop on: unknown event peach
3580+.fi
3581+
3582+If any errors are detected, the exit code will be 1 (one). If all checks pass,
3583+the exit code will be 0 (zero).
3584+
3585+Note that for complex start on and stop on conditions, this command may
3586+give what appears to be misleading output when an error condition is
3587+found since all expressions in the failing condition that are in error
3588+will generate error output. For example, if job configuration
3589+\fI/etc/init/bar.conf\fP contains the following:
3590+
3591+.nf
3592+ start on (A and (started B or (starting C or D)))
3593+.fi
3594+
3595+And only event A can be satisfied, the output will be:
3596+
3597+.nf
3598+ bar
3599+ start on: unknown job B
3600+ start on: unknown job C
3601+ start on: unknown event D
3602+.fi
3603+
3604+.B OPTIONS
3605+.RS
3606+.IP "\fB-i\fP \fI[EVENTS]\fP, \fB\-\-ignore\-events\fP \fI[EVENTS]\fP"
3607+
3608+If specified, the argument should be a list of comma-separated events to
3609+ignore when checking the job configuration files.
3610+
3611+This option may be useful to ignore errors if a particular job
3612+configuration file does not advertise it emits an event.
3613+
3614+Note that internal events (such as \fBstartup\fP(7) and
3615+\fBstarting\fP(7)) are automatically ignored.
3616+.IP "\fB-w\fP, \fB\-\-warn\fP"
3617+If specified, treat \fIany\fP unknown jobs and events as errors.
3618+
3619+.\"
3620 .SH AUTHOR
3621 Written by Scott James Remnant
3622 .RB < scott@netsplit.com >
3623@@ -348,7 +509,7 @@
3624 .RB < https://launchpad.net/upstart/+bugs >
3625 .\"
3626 .SH COPYRIGHT
3627-Copyright \(co 2010 Canonical Ltd.
3628+Copyright \(co 2010-2011 Canonical Ltd.
3629 .br
3630 This is free software; see the source for copying conditions. There is NO
3631 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
3632
3633=== modified file 'util/reboot.c'
3634--- util/reboot.c 2011-03-16 22:18:22 +0000
3635+++ util/reboot.c 2011-06-06 16:54:17 +0000
3636@@ -1,6 +1,5 @@
3637 /* upstart
3638 *
3639- * Copyright © 2011 Google Inc.
3640 * Copyright © 2010 Canonical Ltd.
3641 * Author: Scott James Remnant <scott@netsplit.com>.
3642 *
3643
3644=== modified file 'util/tests/test_initctl.c'
3645--- util/tests/test_initctl.c 2010-02-04 20:43:33 +0000
3646+++ util/tests/test_initctl.c 2011-06-06 16:54:17 +0000
3647@@ -3,7 +3,8 @@
3648 * test_initctl.c - test suite for util/initctl.c
3649 *
3650 * Copyright © 2010 Canonical Ltd.
3651- * Author: Scott James Remnant <scott@netsplit.com>.
3652+ * Authors: Scott James Remnant <scott@netsplit.com>,
3653+ * James Hunt <james.hunt@canonical.com>.
3654 *
3655 * This program is free software; you can redistribute it and/or modify
3656 * it under the terms of the GNU General Public License version 2, as
3657@@ -27,6 +28,8 @@
3658 #include <stdio.h>
3659 #include <signal.h>
3660 #include <unistd.h>
3661+#include <sys/types.h>
3662+#include <sys/stat.h>
3663
3664 #include <nih-dbus/dbus_error.h>
3665 #include <nih-dbus/dbus_connection.h>
3666@@ -40,11 +43,192 @@
3667 #include <nih/main.h>
3668 #include <nih/command.h>
3669 #include <nih/error.h>
3670+#include <nih/string.h>
3671
3672 #include "dbus/upstart.h"
3673
3674-
3675-extern int system_bus;
3676+/* remember we run from the 'util' directory */
3677+#define UPSTART_BINARY "../init/init"
3678+#define INITCTL_BINARY "./initctl --session"
3679+
3680+#define BUFFER_SIZE 1024
3681+
3682+/**
3683+ * START_UPSTART:
3684+ *
3685+ * @pid: pid_t that will contain pid of running instance on success.
3686+ *
3687+ * Start an instance of Upstart. Fork errors are fatal, but after
3688+ * a successful fork, waits for up to a somewhat arbitrary (but
3689+ * more than adequate!) amount of time for Upstart to initialize.
3690+ **/
3691+#define START_UPSTART(pid) \
3692+{ \
3693+ nih_local NihDBusProxy *upstart = NULL; \
3694+ DBusConnection *connection; \
3695+ DBusError dbus_error; \
3696+ \
3697+ /* XXX: arbitrary value */ \
3698+ int attempts = 10; \
3699+ \
3700+ \
3701+ TEST_NE (pid = fork (), -1); \
3702+ \
3703+ if (pid == 0) \
3704+ execlp (UPSTART_BINARY, UPSTART_BINARY, \
3705+ "--session", \
3706+ "--no-startup-event", NULL); \
3707+ \
3708+ while (attempts) { \
3709+ attempts--; \
3710+ sleep (1); \
3711+ dbus_error_init (&dbus_error); \
3712+ connection = dbus_bus_get (DBUS_BUS_SESSION, \
3713+ &dbus_error); \
3714+ \
3715+ if (! connection) { \
3716+ dbus_error_free (&dbus_error); \
3717+ continue; \
3718+ } \
3719+ dbus_error_free (&dbus_error); \
3720+ \
3721+ upstart = nih_dbus_proxy_new (NULL, connection, \
3722+ DBUS_SERVICE_UPSTART, \
3723+ DBUS_PATH_UPSTART, \
3724+ NULL, NULL); \
3725+ \
3726+ if (! upstart) { \
3727+ NihError *err; \
3728+ err = nih_error_get (); \
3729+ nih_free (err); \
3730+ dbus_connection_unref (connection); \
3731+ } \
3732+ else { \
3733+ break; \
3734+ } \
3735+ } \
3736+}
3737+
3738+/**
3739+ * STOP_UPSTART:
3740+ *
3741+ * @pid: pid of upstart to kill.
3742+ *
3743+ * Stop upstart process @pid.
3744+ **/
3745+#define STOP_UPSTART(pid) \
3746+{ \
3747+ assert (pid); \
3748+ \
3749+ if (kill (pid, 0) == 0) { \
3750+ kill (pid, SIGTERM); \
3751+ sleep (1); \
3752+ } \
3753+ \
3754+ if (kill (pid, 0) == 0) { \
3755+ kill (pid, SIGKILL); \
3756+ } \
3757+}
3758+
3759+/**
3760+ * RUN_COMMAND:
3761+ *
3762+ * @parent: pointer to parent object,
3763+ * @cmd: string representing command to run,
3764+ * @result: "char ***" pointer which will contain an array of string
3765+ * values corresponding to lines of standard output generated by @cmd,
3766+ * @len: size_t pointer which will be set to length of @result.
3767+ *
3768+ * Run a command and return its standard output. It is the callers
3769+ * responsibility to free @result. Errors from running @cmd are fatal.
3770+ **/
3771+#define RUN_COMMAND(parent, cmd, result, len) \
3772+{ \
3773+ FILE *f; \
3774+ char buffer[BUFFER_SIZE]; \
3775+ char **ret; \
3776+ \
3777+ assert (cmd[0]); \
3778+ \
3779+ *(result) = nih_str_array_new (parent); \
3780+ TEST_NE_P (*result, NULL); \
3781+ *(len) = 0; \
3782+ \
3783+ f = popen (cmd, "r"); \
3784+ TEST_NE_P (f, NULL); \
3785+ \
3786+ while (fgets (buffer, BUFFER_SIZE, f)) { \
3787+ size_t l = strlen (buffer)-1; \
3788+ \
3789+ if ( buffer[l] == '\n') \
3790+ buffer[l] = '\0'; \
3791+ ret = nih_str_array_add (result, parent, len, \
3792+ buffer); \
3793+ TEST_NE_P (ret, NULL); \
3794+ } \
3795+ \
3796+ TEST_NE ( pclose (f), -1); \
3797+}
3798+
3799+/**
3800+ * CREATE_FILE:
3801+ *
3802+ * @dirname: directory name (assumed to already exist),
3803+ * @name: name of file to create (no leading slash),
3804+ * @contents: string contents of @name.
3805+ *
3806+ * Create a file in the specified directory with the specified
3807+ * contents.
3808+ *
3809+ * Notes: A newline character is added in the case where @contents does
3810+ * not end with one.
3811+ **/
3812+#define CREATE_FILE(dirname, name, contents) \
3813+{ \
3814+ FILE *f; \
3815+ char filename[PATH_MAX]; \
3816+ \
3817+ assert (dirname[0]); \
3818+ assert (name[0]); \
3819+ \
3820+ strcpy (filename, dirname); \
3821+ if ( name[0] != '/' ) \
3822+ strcat (filename, "/"); \
3823+ strcat (filename, name); \
3824+ f = fopen (filename, "w"); \
3825+ TEST_NE_P (f, NULL); \
3826+ fprintf (f, contents); \
3827+ if ( contents[strlen(contents)-1] != '\n') \
3828+ fprintf (f, "\n"); \
3829+ fclose (f); \
3830+}
3831+
3832+/**
3833+ * DELETE_FILE:
3834+ *
3835+ * @dirname: directory in which file to delete exists,
3836+ * @name: name of file in @dirname to delete.
3837+ *
3838+ * Delete specified file.
3839+ *
3840+ **/
3841+#define DELETE_FILE(dirname, name) \
3842+{ \
3843+ char filename[PATH_MAX]; \
3844+ \
3845+ assert (dirname[0]); \
3846+ assert (name[0]); \
3847+ \
3848+ strcpy (filename, dirname); \
3849+ if ( name[0] != '/' ) \
3850+ strcat (filename, "/"); \
3851+ strcat (filename, name); \
3852+ \
3853+ TEST_EQ (unlink (filename), 0); \
3854+}
3855+
3856+extern int use_dbus;
3857+extern int dbus_bus_type;
3858 extern char *dest_name;
3859 extern const char *dest_address;
3860 extern int no_wait;
3861@@ -103,7 +287,7 @@
3862 */
3863 TEST_FEATURE ("with private connection");
3864 TEST_ALLOC_FAIL {
3865- system_bus = FALSE;
3866+ use_dbus = FALSE;
3867 dest_name = NULL;
3868 dest_address = "unix:abstract=/com/ubuntu/upstart/test";
3869
3870@@ -179,7 +363,8 @@
3871 */
3872 TEST_FEATURE ("with system bus connection");
3873 TEST_ALLOC_FAIL {
3874- system_bus = TRUE;
3875+ use_dbus = TRUE;
3876+ dbus_bus_type = DBUS_BUS_SYSTEM;
3877 dest_name = NULL;
3878 dest_address = DBUS_ADDRESS_UPSTART;
3879
3880@@ -240,7 +425,8 @@
3881 */
3882 TEST_FEATURE ("with system bus connection and different name");
3883 TEST_ALLOC_FAIL {
3884- system_bus = TRUE;
3885+ use_dbus = TRUE;
3886+ dbus_bus_type = DBUS_BUS_SYSTEM;
3887 dest_name = "com.ubuntu.UpstartTest";
3888 dest_address = DBUS_ADDRESS_UPSTART;
3889
3890@@ -302,7 +488,7 @@
3891 */
3892 TEST_FEATURE ("with non-listening private connection");
3893 TEST_ALLOC_FAIL {
3894- system_bus = FALSE;
3895+ use_dbus = FALSE;
3896 dest_name = NULL;
3897 dest_address = "unix:abstract=/com/ubuntu/upstart/test";
3898
3899@@ -328,7 +514,8 @@
3900 */
3901 TEST_FEATURE ("with non-listening system bus");
3902 TEST_ALLOC_FAIL {
3903- system_bus = TRUE;
3904+ use_dbus = TRUE;
3905+ dbus_bus_type = DBUS_BUS_SYSTEM;
3906 dest_name = NULL;
3907 dest_address = DBUS_ADDRESS_UPSTART;
3908
3909@@ -360,7 +547,7 @@
3910 */
3911 TEST_FEATURE ("with --dest but without --system");
3912 TEST_ALLOC_FAIL {
3913- system_bus = FALSE;
3914+ use_dbus = FALSE;
3915 dest_name = "com.ubuntu.Upstart";
3916 dest_address = DBUS_ADDRESS_UPSTART;
3917
3918@@ -3229,7 +3416,8 @@
3919 "NameAcquired"));
3920 dbus_message_unref (method_call);
3921
3922- system_bus = TRUE;
3923+ use_dbus = TRUE;
3924+ dbus_bus_type = DBUS_BUS_SYSTEM;
3925 dest_name = DBUS_SERVICE_UPSTART;
3926 dest_address = DBUS_ADDRESS_UPSTART;
3927
3928@@ -4999,7 +5187,8 @@
3929 "NameAcquired"));
3930 dbus_message_unref (method_call);
3931
3932- system_bus = TRUE;
3933+ use_dbus = TRUE;
3934+ dbus_bus_type = DBUS_BUS_SYSTEM;
3935 dest_name = DBUS_SERVICE_UPSTART;
3936 dest_address = DBUS_ADDRESS_UPSTART;
3937
3938@@ -6584,7 +6773,8 @@
3939 "NameAcquired"));
3940 dbus_message_unref (method_call);
3941
3942- system_bus = TRUE;
3943+ use_dbus = TRUE;
3944+ dbus_bus_type = DBUS_BUS_SYSTEM;
3945 dest_name = DBUS_SERVICE_UPSTART;
3946 dest_address = DBUS_ADDRESS_UPSTART;
3947
3948@@ -8357,7 +8547,8 @@
3949 "NameAcquired"));
3950 dbus_message_unref (method_call);
3951
3952- system_bus = TRUE;
3953+ use_dbus = TRUE;
3954+ dbus_bus_type = DBUS_BUS_SYSTEM;
3955 dest_name = DBUS_SERVICE_UPSTART;
3956 dest_address = DBUS_ADDRESS_UPSTART;
3957
3958@@ -9239,7 +9430,8 @@
3959 "NameAcquired"));
3960 dbus_message_unref (method_call);
3961
3962- system_bus = TRUE;
3963+ use_dbus = TRUE;
3964+ dbus_bus_type = DBUS_BUS_SYSTEM;
3965 dest_name = DBUS_SERVICE_UPSTART;
3966 dest_address = DBUS_ADDRESS_UPSTART;
3967
3968@@ -10730,6 +10922,919 @@
3969 dbus_shutdown ();
3970 }
3971
3972+void
3973+test_show_config (void)
3974+{
3975+ char dirname[PATH_MAX];
3976+ nih_local char *cmd;
3977+ pid_t upstart_pid;
3978+ char **output;
3979+ size_t lines;
3980+ char expected_output[] = "foo";
3981+
3982+ TEST_GROUP ("show_config");
3983+
3984+ TEST_FILENAME (dirname);
3985+ TEST_EQ (mkdir (dirname, 0755), 0);
3986+
3987+ /* Use the "secret" interface */
3988+ TEST_EQ (setenv ("UPSTART_CONFDIR", dirname, 1), 0);
3989+
3990+ START_UPSTART (upstart_pid);
3991+
3992+ TEST_FEATURE ("no emits, no start on, no stop on");
3993+ CREATE_FILE (dirname, "foo.conf",
3994+ "author \"foo\"\n"
3995+ "description \"wibble\"");
3996+
3997+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
3998+ TEST_NE_P (cmd, NULL);
3999+ RUN_COMMAND (NULL, cmd, &output, &lines);
4000+ TEST_EQ_STR (output[0], expected_output);
4001+ TEST_EQ (lines, 1);
4002+ nih_free (output);
4003+
4004+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4005+ TEST_NE_P (cmd, NULL);
4006+ RUN_COMMAND (NULL, cmd, &output, &lines);
4007+ TEST_EQ_STR (output[0], expected_output);
4008+ TEST_EQ (lines, 1);
4009+ nih_free (output);
4010+
4011+ DELETE_FILE (dirname, "foo.conf");
4012+
4013+ /*******************************************************************/
4014+
4015+ TEST_FEATURE ("1 emits, no start on, no stop on");
4016+
4017+ CREATE_FILE (dirname, "foo.conf",
4018+ "author \"foo\"\n"
4019+ "emits \"thing\"\n"
4020+ "description \"wibble\"");
4021+
4022+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4023+ TEST_NE_P (cmd, NULL);
4024+ RUN_COMMAND (NULL, cmd, &output, &lines);
4025+ TEST_EQ_STR (output[0], expected_output);
4026+ TEST_EQ_STR (output[1], " emits thing");
4027+ TEST_EQ (lines, 2);
4028+ nih_free (output);
4029+
4030+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4031+ TEST_NE_P (cmd, NULL);
4032+ RUN_COMMAND (NULL, cmd, &output, &lines);
4033+ TEST_EQ_STR (output[0], expected_output);
4034+ TEST_EQ_STR (output[1], " emits thing");
4035+ TEST_EQ (lines, 2);
4036+ nih_free (output);
4037+
4038+ DELETE_FILE (dirname, "foo.conf");
4039+
4040+ /*******************************************************************/
4041+
4042+ TEST_FEATURE ("2 emits, no start on, no stop on");
4043+
4044+ CREATE_FILE (dirname, "foo.conf",
4045+ "author \"foo\"\n"
4046+ "emits \"thing\"\n"
4047+ "emits \"thong\"\n"
4048+ "description \"wibble\"");
4049+
4050+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4051+ TEST_NE_P (cmd, NULL);
4052+ RUN_COMMAND (NULL, cmd, &output, &lines);
4053+ TEST_EQ_STR (output[0], expected_output);
4054+ TEST_EQ_STR (output[1], " emits thing");
4055+ TEST_EQ_STR (output[2], " emits thong");
4056+ TEST_EQ (lines, 3);
4057+ nih_free (output);
4058+
4059+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4060+ TEST_NE_P (cmd, NULL);
4061+ RUN_COMMAND (NULL, cmd, &output, &lines);
4062+ TEST_EQ_STR (output[0], expected_output);
4063+ TEST_EQ_STR (output[1], " emits thing");
4064+ TEST_EQ_STR (output[2], " emits thong");
4065+ TEST_EQ (lines, 3);
4066+ nih_free (output);
4067+
4068+ DELETE_FILE (dirname, "foo.conf");
4069+
4070+ /*******************************************************************/
4071+
4072+ TEST_FEATURE ("no emits, start on, no stop on");
4073+
4074+ CREATE_FILE (dirname, "foo.conf",
4075+ "author \"foo\"\n"
4076+ "start on (A and B)\n"
4077+ "description \"wibble\"");
4078+
4079+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4080+ TEST_NE_P (cmd, NULL);
4081+ RUN_COMMAND (NULL, cmd, &output, &lines);
4082+ TEST_EQ_STR (output[0], expected_output);
4083+ TEST_EQ_STR (output[1], " start on (A and B)");
4084+ TEST_EQ (lines, 2);
4085+ nih_free (output);
4086+
4087+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4088+ TEST_NE_P (cmd, NULL);
4089+ RUN_COMMAND (NULL, cmd, &output, &lines);
4090+ TEST_EQ_STR (output[0], expected_output);
4091+ TEST_EQ_STR (output[1], " start on A (job:, env:)");
4092+ TEST_EQ_STR (output[2], " start on B (job:, env:)");
4093+ TEST_EQ (lines, 3);
4094+ nih_free (output);
4095+
4096+ DELETE_FILE (dirname, "foo.conf");
4097+
4098+ /*******************************************************************/
4099+
4100+ TEST_FEATURE ("1 emits, start on, no stop on");
4101+
4102+ CREATE_FILE (dirname, "foo.conf",
4103+ "author \"foo\"\n"
4104+ "emits \"bong\"\n"
4105+ "start on (A and B)\n"
4106+ "description \"wibble\"");
4107+
4108+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4109+ TEST_NE_P (cmd, NULL);
4110+ RUN_COMMAND (NULL, cmd, &output, &lines);
4111+ TEST_EQ_STR (output[0], expected_output);
4112+ TEST_EQ_STR (output[1], " emits bong");
4113+ TEST_EQ_STR (output[2], " start on (A and B)");
4114+ TEST_EQ (lines, 3);
4115+ nih_free (output);
4116+
4117+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4118+ TEST_NE_P (cmd, NULL);
4119+ RUN_COMMAND (NULL, cmd, &output, &lines);
4120+ TEST_EQ_STR (output[0], expected_output);
4121+ TEST_EQ_STR (output[1], " emits bong");
4122+ TEST_EQ_STR (output[2], " start on A (job:, env:)");
4123+ TEST_EQ_STR (output[3], " start on B (job:, env:)");
4124+ TEST_EQ (lines, 4);
4125+ nih_free (output);
4126+
4127+ DELETE_FILE (dirname, "foo.conf");
4128+
4129+ /*******************************************************************/
4130+
4131+ TEST_FEATURE ("2 emits, start on, no stop on");
4132+
4133+ CREATE_FILE (dirname, "foo.conf",
4134+ "emits \"bong\"\n"
4135+ "author \"foo\"\n"
4136+ "start on (A and B)\n"
4137+ "emits \"stime\"\n"
4138+ "description \"wibble\"");
4139+
4140+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4141+ TEST_NE_P (cmd, NULL);
4142+ RUN_COMMAND (NULL, cmd, &output, &lines);
4143+ TEST_EQ_STR (output[0], expected_output);
4144+ TEST_EQ_STR (output[1], " emits bong");
4145+ TEST_EQ_STR (output[2], " emits stime");
4146+ TEST_EQ_STR (output[3], " start on (A and B)");
4147+ TEST_EQ (lines, 4);
4148+ nih_free (output);
4149+
4150+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4151+ TEST_NE_P (cmd, NULL);
4152+ RUN_COMMAND (NULL, cmd, &output, &lines);
4153+ TEST_EQ_STR (output[0], expected_output);
4154+ TEST_EQ_STR (output[1], " emits bong");
4155+ TEST_EQ_STR (output[2], " emits stime");
4156+ TEST_EQ_STR (output[3], " start on A (job:, env:)");
4157+ TEST_EQ_STR (output[4], " start on B (job:, env:)");
4158+ TEST_EQ (lines, 5);
4159+ nih_free (output);
4160+
4161+ DELETE_FILE (dirname, "foo.conf");
4162+
4163+ /*******************************************************************/
4164+
4165+ TEST_FEATURE ("no emits, no start on, stop on");
4166+
4167+ CREATE_FILE (dirname, "foo.conf",
4168+ "author \"foo\"\n"
4169+ "stop on (A or B)\n"
4170+ "description \"wibble\"");
4171+
4172+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4173+ TEST_NE_P (cmd, NULL);
4174+ RUN_COMMAND (NULL, cmd, &output, &lines);
4175+ TEST_EQ_STR (output[0], expected_output);
4176+ TEST_EQ_STR (output[1], " stop on (A or B)");
4177+ TEST_EQ (lines, 2);
4178+ nih_free (output);
4179+
4180+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4181+ TEST_NE_P (cmd, NULL);
4182+ RUN_COMMAND (NULL, cmd, &output, &lines);
4183+ TEST_EQ_STR (output[0], expected_output);
4184+ TEST_EQ_STR (output[1], " stop on A (job:, env:)");
4185+ TEST_EQ_STR (output[2], " stop on B (job:, env:)");
4186+ TEST_EQ (lines, 3);
4187+ nih_free (output);
4188+
4189+ DELETE_FILE (dirname, "foo.conf");
4190+
4191+ /*******************************************************************/
4192+
4193+ TEST_FEATURE ("1 emits, no start on, stop on");
4194+
4195+ CREATE_FILE (dirname, "foo.conf",
4196+ "author \"foo\"\n"
4197+ "emits \"bong\"\n"
4198+ "stop on (A or B)\n"
4199+ "description \"wibble\"");
4200+
4201+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4202+ TEST_NE_P (cmd, NULL);
4203+ RUN_COMMAND (NULL, cmd, &output, &lines);
4204+ TEST_EQ_STR (output[0], expected_output);
4205+ TEST_EQ_STR (output[1], " emits bong");
4206+ TEST_EQ_STR (output[2], " stop on (A or B)");
4207+ TEST_EQ (lines, 3);
4208+ nih_free (output);
4209+
4210+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4211+ TEST_NE_P (cmd, NULL);
4212+ RUN_COMMAND (NULL, cmd, &output, &lines);
4213+ TEST_EQ_STR (output[0], expected_output);
4214+ TEST_EQ_STR (output[1], " emits bong");
4215+ TEST_EQ_STR (output[2], " stop on A (job:, env:)");
4216+ TEST_EQ_STR (output[3], " stop on B (job:, env:)");
4217+ TEST_EQ (lines, 4);
4218+ nih_free (output);
4219+
4220+ DELETE_FILE (dirname, "foo.conf");
4221+
4222+ /*******************************************************************/
4223+
4224+ TEST_FEATURE ("2 emits, no start on, stop on");
4225+
4226+ CREATE_FILE (dirname, "foo.conf",
4227+ "emits \"bong\"\n"
4228+ "author \"foo\"\n"
4229+ "stop on (A or B)\n"
4230+ "emits \"stime\"\n"
4231+ "description \"wibble\"");
4232+
4233+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4234+ TEST_NE_P (cmd, NULL);
4235+ RUN_COMMAND (NULL, cmd, &output, &lines);
4236+ TEST_EQ_STR (output[0], expected_output);
4237+ TEST_EQ_STR (output[1], " emits bong");
4238+ TEST_EQ_STR (output[2], " emits stime");
4239+ TEST_EQ_STR (output[3], " stop on (A or B)");
4240+ TEST_EQ (lines, 4);
4241+ nih_free (output);
4242+
4243+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4244+ TEST_NE_P (cmd, NULL);
4245+ RUN_COMMAND (NULL, cmd, &output, &lines);
4246+ TEST_EQ_STR (output[0], expected_output);
4247+ TEST_EQ_STR (output[1], " emits bong");
4248+ TEST_EQ_STR (output[2], " emits stime");
4249+ TEST_EQ_STR (output[3], " stop on A (job:, env:)");
4250+ TEST_EQ_STR (output[4], " stop on B (job:, env:)");
4251+ TEST_EQ (lines, 5);
4252+ nih_free (output);
4253+
4254+ DELETE_FILE (dirname, "foo.conf");
4255+
4256+ /*******************************************************************/
4257+
4258+ TEST_FEATURE ("2 emits, start on with only initial JOB, stop on with JOB at end of env");
4259+
4260+ CREATE_FILE (dirname, "foo.conf",
4261+ "emits \"bong\"\n"
4262+ "author \"foo\"\n"
4263+ "stop on (A or stopping c=d e=f g=h JOB=\"bang\")\n"
4264+ "emits \"stime\"\n"
4265+ "start on (starting JOB=\"boo\" or B x=y)\n"
4266+ "description \"wibble\"");
4267+
4268+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4269+ TEST_NE_P (cmd, NULL);
4270+ RUN_COMMAND (NULL, cmd, &output, &lines);
4271+ TEST_EQ_STR (output[0], expected_output);
4272+ TEST_EQ_STR (output[1], " emits bong");
4273+ TEST_EQ_STR (output[2], " emits stime");
4274+ TEST_EQ_STR (output[3], " start on (starting JOB=boo or B x=y)");
4275+ TEST_EQ_STR (output[4], " stop on (A or stopping c=d e=f g=h JOB=bang)");
4276+ TEST_EQ (lines, 5);
4277+ nih_free (output);
4278+
4279+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4280+ TEST_NE_P (cmd, NULL);
4281+ RUN_COMMAND (NULL, cmd, &output, &lines);
4282+ TEST_EQ_STR (output[0], expected_output);
4283+ TEST_EQ_STR (output[1], " emits bong");
4284+ TEST_EQ_STR (output[2], " emits stime");
4285+ TEST_EQ_STR (output[3], " start on starting (job: boo, env:)");
4286+ TEST_EQ_STR (output[4], " start on B (job:, env: x=y)");
4287+ TEST_EQ_STR (output[5], " stop on A (job:, env:)");
4288+ TEST_EQ_STR (output[6], " stop on stopping (job: bang, env: c=d e=f g=h)");
4289+ TEST_EQ (lines, 7);
4290+ nih_free (output);
4291+
4292+ DELETE_FILE (dirname, "foo.conf");
4293+
4294+ /*******************************************************************/
4295+
4296+ TEST_FEATURE ("2 emits, start on with initial JOB+env, stop on with JOB at end of env");
4297+
4298+ CREATE_FILE (dirname, "foo.conf",
4299+ "emits \"bong\"\n"
4300+ "author \"foo\"\n"
4301+ "stop on (A or stopping c=d e=f g=h JOB=\"bang\")\n"
4302+ "emits \"stime\"\n"
4303+ "start on (starting JOB=\"boo\" P=Q c=sea or B x=y)\n"
4304+ "description \"wibble\"");
4305+
4306+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4307+ TEST_NE_P (cmd, NULL);
4308+ RUN_COMMAND (NULL, cmd, &output, &lines);
4309+ TEST_EQ_STR (output[0], expected_output);
4310+ TEST_EQ_STR (output[1], " emits bong");
4311+ TEST_EQ_STR (output[2], " emits stime");
4312+ TEST_EQ_STR (output[3], " start on (starting JOB=boo P=Q c=sea or B x=y)");
4313+ TEST_EQ_STR (output[4], " stop on (A or stopping c=d e=f g=h JOB=bang)");
4314+ TEST_EQ (lines, 5);
4315+ nih_free (output);
4316+
4317+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4318+ TEST_NE_P (cmd, NULL);
4319+ RUN_COMMAND (NULL, cmd, &output, &lines);
4320+ TEST_EQ_STR (output[0], expected_output);
4321+ TEST_EQ_STR (output[1], " emits bong");
4322+ TEST_EQ_STR (output[2], " emits stime");
4323+ TEST_EQ_STR (output[3], " start on starting (job: boo, env: P=Q c=sea)");
4324+ TEST_EQ_STR (output[4], " start on B (job:, env: x=y)");
4325+ TEST_EQ_STR (output[5], " stop on A (job:, env:)");
4326+ TEST_EQ_STR (output[6], " stop on stopping (job: bang, env: c=d e=f g=h)");
4327+ TEST_EQ (lines, 7);
4328+ nih_free (output);
4329+
4330+ DELETE_FILE (dirname, "foo.conf");
4331+
4332+ /*******************************************************************/
4333+
4334+ TEST_FEATURE ("3 emits, start on (with env), stop on (with env)");
4335+
4336+ CREATE_FILE (dirname, "foo.conf",
4337+ "emits \"bong\"\n"
4338+ "stop on starting D and (stopping E or F hello=world)\n"
4339+ "author \"foo\"\n"
4340+ "emits \"bar\"\n"
4341+ "emits \"stime\"\n"
4342+ "start on A and (B FOO=BAR or starting C x=y)\n"
4343+ "description \"wibble\"");
4344+
4345+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4346+ TEST_NE_P (cmd, NULL);
4347+ RUN_COMMAND (NULL, cmd, &output, &lines);
4348+ TEST_EQ_STR (output[0], expected_output);
4349+ TEST_EQ_STR (output[1], " emits bong");
4350+ TEST_EQ_STR (output[2], " emits bar");
4351+ TEST_EQ_STR (output[3], " emits stime");
4352+ /* note the extra brackets! */
4353+ TEST_EQ_STR (output[4], " start on (A and (B FOO=BAR or starting C x=y))");
4354+ /* note the extra brackets! */
4355+ TEST_EQ_STR (output[5], " stop on (starting D and (stopping E or F hello=world))");
4356+ TEST_EQ (lines, 6);
4357+ nih_free (output);
4358+
4359+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4360+ TEST_NE_P (cmd, NULL);
4361+ RUN_COMMAND (NULL, cmd, &output, &lines);
4362+ TEST_EQ_STR (output[0], expected_output);
4363+ TEST_EQ_STR (output[1], " emits bong");
4364+ TEST_EQ_STR (output[2], " emits bar");
4365+ TEST_EQ_STR (output[3], " emits stime");
4366+ TEST_EQ_STR (output[4], " start on A (job:, env:)");
4367+ TEST_EQ_STR (output[5], " start on B (job:, env: FOO=BAR)");
4368+ TEST_EQ_STR (output[6], " start on starting (job: C, env: x=y)");
4369+ TEST_EQ_STR (output[7], " stop on starting (job: D, env:)");
4370+ TEST_EQ_STR (output[8], " stop on stopping (job: E, env:)");
4371+ TEST_EQ_STR (output[9], " stop on F (job:, env: hello=world)");
4372+ TEST_EQ (lines, 10);
4373+ nih_free (output);
4374+
4375+ DELETE_FILE (dirname, "foo.conf");
4376+
4377+ /*******************************************************************/
4378+
4379+ TEST_FEATURE ("3 emits, complex start on (with env), complex stop on (with env)");
4380+
4381+ CREATE_FILE (dirname, "foo.conf",
4382+ "emits \"bong\"\n"
4383+ "stop on runlevel [!2345] colour=blue or starting rocket\n"
4384+ "author \"foo\"\n"
4385+ "emits \"bar\"\n"
4386+ "emits \"stime\"\n"
4387+ "start on (starting mountall or (runlevel [016] and "
4388+ "(stopped gdm or stopped kdm or stopped xdm A=B or stopping lxdm)))\n"
4389+ "description \"wibble\"");
4390+
4391+ cmd = nih_sprintf (NULL, "%s show-config foo 2>&1", INITCTL_BINARY);
4392+ TEST_NE_P (cmd, NULL);
4393+ RUN_COMMAND (NULL, cmd, &output, &lines);
4394+ TEST_EQ_STR (output[0], expected_output);
4395+ TEST_EQ_STR (output[1], " emits bong");
4396+ TEST_EQ_STR (output[2], " emits bar");
4397+ TEST_EQ_STR (output[3], " emits stime");
4398+ /* note the extra brackets! */
4399+ TEST_EQ_STR (output[4], " start on (starting mountall or (runlevel [016] and "
4400+ "(((stopped gdm or stopped kdm) or stopped xdm A=B) or stopping lxdm)))");
4401+ /* note the extra brackets! */
4402+ TEST_EQ_STR (output[5], " stop on (runlevel [!2345] colour=blue or starting rocket)");
4403+ TEST_EQ (lines, 6);
4404+ nih_free (output);
4405+
4406+ cmd = nih_sprintf (NULL, "%s show-config -e foo 2>&1", INITCTL_BINARY);
4407+ TEST_NE_P (cmd, NULL);
4408+ RUN_COMMAND (NULL, cmd, &output, &lines);
4409+ TEST_EQ_STR (output[0], expected_output);
4410+ TEST_EQ_STR (output[1], " emits bong");
4411+ TEST_EQ_STR (output[2], " emits bar");
4412+ TEST_EQ_STR (output[3], " emits stime");
4413+ TEST_EQ_STR (output[4], " start on starting (job: mountall, env:)");
4414+ TEST_EQ_STR (output[5], " start on runlevel (job:, env: [016])");
4415+ TEST_EQ_STR (output[6], " start on stopped (job: gdm, env:)");
4416+ TEST_EQ_STR (output[7], " start on stopped (job: kdm, env:)");
4417+ TEST_EQ_STR (output[8], " start on stopped (job: xdm, env: A=B)");
4418+ TEST_EQ_STR (output[9], " start on stopping (job: lxdm, env:)");
4419+ TEST_EQ_STR (output[10], " stop on runlevel (job:, env: [!2345] colour=blue)");
4420+ TEST_EQ_STR (output[11], " stop on starting (job: rocket, env:)");
4421+ TEST_EQ (lines, 12);
4422+ nih_free (output);
4423+
4424+ DELETE_FILE (dirname, "foo.conf");
4425+
4426+ /*******************************************************************/
4427+
4428+ STOP_UPSTART (upstart_pid);
4429+ TEST_EQ (unsetenv ("UPSTART_CONFDIR"), 0);
4430+}
4431+
4432+void
4433+test_check_config (void)
4434+{
4435+ char dirname[PATH_MAX];
4436+ nih_local char *cmd;
4437+ pid_t upstart_pid;
4438+ char **output;
4439+ size_t lines;
4440+
4441+ TEST_GROUP ("check_config");
4442+
4443+ TEST_FILENAME (dirname);
4444+ TEST_EQ (mkdir (dirname, 0755), 0);
4445+
4446+ /* Use the "secret" interface */
4447+ TEST_EQ (setenv ("UPSTART_CONFDIR", dirname, 1), 0);
4448+
4449+ START_UPSTART (upstart_pid);
4450+
4451+ /*******************************************************************/
4452+
4453+ TEST_FEATURE ("no unreachable jobs/events (satisfied by job or event)");
4454+
4455+ CREATE_FILE (dirname, "foo.conf",
4456+ "start on (starting bar or wibble)");
4457+
4458+ CREATE_FILE (dirname, "bar.conf",
4459+ "task\n"
4460+ "exec true");
4461+
4462+ CREATE_FILE (dirname, "baz.conf",
4463+ "emits wibble");
4464+
4465+ cmd = nih_sprintf (NULL, "%s check-config 2>&1", INITCTL_BINARY);
4466+ TEST_NE_P (cmd, NULL);
4467+ RUN_COMMAND (NULL, cmd, &output, &lines);
4468+ TEST_EQ (lines, 0);
4469+
4470+ DELETE_FILE (dirname, "foo.conf");
4471+ DELETE_FILE (dirname, "bar.conf");
4472+ DELETE_FILE (dirname, "baz.conf");
4473+
4474+ /*******************************************************************/
4475+
4476+ TEST_FEATURE ("no unreachable jobs/events (satisfied by job)");
4477+
4478+ CREATE_FILE (dirname, "foo.conf",
4479+ "start on (starting bar or wibble)");
4480+
4481+ CREATE_FILE (dirname, "bar.conf",
4482+ "task\n"
4483+ "exec true");
4484+
4485+ cmd = nih_sprintf (NULL, "%s check-config 2>&1", INITCTL_BINARY);
4486+ TEST_NE_P (cmd, NULL);
4487+ RUN_COMMAND (NULL, cmd, &output, &lines);
4488+ TEST_EQ (lines, 0);
4489+
4490+ DELETE_FILE (dirname, "foo.conf");
4491+ DELETE_FILE (dirname, "bar.conf");
4492+
4493+ /*******************************************************************/
4494+
4495+ TEST_FEATURE ("no unreachable jobs/events (satisfied by event)");
4496+
4497+ CREATE_FILE (dirname, "foo.conf",
4498+ "start on (starting bar or wibble)");
4499+
4500+ CREATE_FILE (dirname, "baz.conf",
4501+ "emits wibble");
4502+
4503+ cmd = nih_sprintf (NULL, "%s check-config 2>&1", INITCTL_BINARY);
4504+ TEST_NE_P (cmd, NULL);
4505+ RUN_COMMAND (NULL, cmd, &output, &lines);
4506+ TEST_EQ (lines, 0);
4507+
4508+ DELETE_FILE (dirname, "foo.conf");
4509+ DELETE_FILE (dirname, "baz.conf");
4510+
4511+ /*******************************************************************/
4512+
4513+ TEST_FEATURE ("unreachable event");
4514+
4515+ CREATE_FILE (dirname, "foo.conf",
4516+ "start on (starting bar and wibble)");
4517+
4518+ CREATE_FILE (dirname, "bar.conf",
4519+ "task\n"
4520+ "exec true");
4521+
4522+ cmd = nih_sprintf (NULL, "%s check-config 2>&1", INITCTL_BINARY);
4523+ TEST_NE_P (cmd, NULL);
4524+ RUN_COMMAND (NULL, cmd, &output, &lines);
4525+ TEST_EQ_STR (output[0], "foo");
4526+ TEST_EQ_STR (output[1], " start on: unknown event wibble");
4527+ TEST_EQ (lines, 2);
4528+
4529+ DELETE_FILE (dirname, "foo.conf");
4530+ DELETE_FILE (dirname, "bar.conf");
4531+
4532+ /*******************************************************************/
4533+
4534+ TEST_FEATURE ("unreachable job");
4535+
4536+ CREATE_FILE (dirname, "foo.conf",
4537+ "start on (starting bar and wibble)");
4538+
4539+ CREATE_FILE (dirname, "baz.conf",
4540+ "emits wibble");
4541+
4542+ cmd = nih_sprintf (NULL, "%s check-config 2>&1", INITCTL_BINARY);
4543+ TEST_NE_P (cmd, NULL);
4544+ RUN_COMMAND (NULL, cmd, &output, &lines);
4545+ TEST_EQ_STR (output[0], "foo");
4546+ TEST_EQ_STR (output[1], " start on: unknown job bar");
4547+ TEST_EQ (lines, 2);
4548+
4549+ DELETE_FILE (dirname, "foo.conf");
4550+ DELETE_FILE (dirname, "baz.conf");
4551+
4552+ /*******************************************************************/
4553+
4554+ TEST_FEATURE ("unreachable event with forced ignore");
4555+
4556+ CREATE_FILE (dirname, "foo.conf",
4557+ "start on (starting bar and wibble)");
4558+
4559+ CREATE_FILE (dirname, "bar.conf",
4560+ "task\n"
4561+ "exec true");
4562+
4563+ cmd = nih_sprintf (NULL, "%s check-config --ignore-events=wibble 2>&1",
4564+ INITCTL_BINARY);
4565+ TEST_NE_P (cmd, NULL);
4566+ RUN_COMMAND (NULL, cmd, &output, &lines);
4567+ TEST_EQ (lines, 0);
4568+
4569+ DELETE_FILE (dirname, "bar.conf");
4570+
4571+ /*******************************************************************/
4572+
4573+ TEST_FEATURE ("unreachable events with forced ignores");
4574+
4575+ CREATE_FILE (dirname, "foo.conf",
4576+ "start on (fred and wilma)");
4577+
4578+ cmd = nih_sprintf (NULL, "%s check-config --ignore-events=wilma,foo,fred 2>&1",
4579+ INITCTL_BINARY);
4580+ TEST_NE_P (cmd, NULL);
4581+ RUN_COMMAND (NULL, cmd, &output, &lines);
4582+ TEST_EQ (lines, 0);
4583+
4584+ DELETE_FILE (dirname, "foo.conf");
4585+
4586+ /*******************************************************************/
4587+
4588+ TEST_FEATURE ("satisfiable complex start on");
4589+
4590+ /* Yes folks, it's the classic */
4591+ CREATE_FILE (dirname, "plymouth.conf",
4592+ "start on (starting mountall\n"
4593+ " or (runlevel [016]\n"
4594+ " and (stopped gdm\n"
4595+ " or stopped kdm\n"
4596+ " or stopped xdm\n"
4597+ " or stopped lxdm)))");
4598+
4599+ CREATE_FILE (dirname, "mountall.conf", "exec true");
4600+ CREATE_FILE (dirname, "gdm.conf" , "exec true");
4601+
4602+ cmd = nih_sprintf (NULL, "%s check-config --ignore-events=runlevel 2>&1",
4603+ INITCTL_BINARY);
4604+ TEST_NE_P (cmd, NULL);
4605+ RUN_COMMAND (NULL, cmd, &output, &lines);
4606+ TEST_EQ (lines, 0);
4607+
4608+ DELETE_FILE (dirname, "plymouth.conf");
4609+ DELETE_FILE (dirname, "mountall.conf");
4610+ DELETE_FILE (dirname, "gdm.conf");
4611+
4612+ /*******************************************************************/
4613+
4614+ TEST_FEATURE ("unsatisfiable complex start on");
4615+
4616+ CREATE_FILE (dirname, "plymouth.conf",
4617+ "start on (starting mountall\n"
4618+ " or (runlevel [016]\n"
4619+ " and (stopped gdm\n"
4620+ " or stopped kdm\n"
4621+ " or stopped xdm\n"
4622+ " or stopped lxdm)))");
4623+
4624+ CREATE_FILE (dirname, "mountall.conf", "exec true");
4625+
4626+ cmd = nih_sprintf (NULL, "%s check-config --ignore-events=runlevel 2>&1",
4627+ INITCTL_BINARY);
4628+ TEST_NE_P (cmd, NULL);
4629+ RUN_COMMAND (NULL, cmd, &output, &lines);
4630+
4631+ TEST_EQ_STR (output[0], "plymouth");
4632+ TEST_EQ_STR (output[1], " start on: unknown job lxdm");
4633+ TEST_EQ_STR (output[2], " start on: unknown job xdm");
4634+ TEST_EQ_STR (output[3], " start on: unknown job kdm");
4635+ TEST_EQ_STR (output[4], " start on: unknown job gdm");
4636+ TEST_EQ (lines, 5);
4637+
4638+ DELETE_FILE (dirname, "plymouth.conf");
4639+ DELETE_FILE (dirname, "mountall.conf");
4640+
4641+ /*******************************************************************/
4642+
4643+ TEST_FEATURE ("satisfiable complex stop on");
4644+
4645+ /* Yes folks, it's the classic */
4646+ CREATE_FILE (dirname, "plymouth.conf",
4647+ "stop on (starting mountall\n"
4648+ " or (runlevel [016]\n"
4649+ " and (stopped gdm\n"
4650+ " or stopped kdm\n"
4651+ " or stopped xdm\n"
4652+ " or stopped lxdm)))");
4653+
4654+ CREATE_FILE (dirname, "mountall.conf", "exec true");
4655+ CREATE_FILE (dirname, "gdm.conf" , "exec true");
4656+
4657+ cmd = nih_sprintf (NULL, "%s check-config --ignore-events=runlevel 2>&1",
4658+ INITCTL_BINARY);
4659+ TEST_NE_P (cmd, NULL);
4660+ RUN_COMMAND (NULL, cmd, &output, &lines);
4661+ TEST_EQ (lines, 0);
4662+
4663+ DELETE_FILE (dirname, "plymouth.conf");
4664+ DELETE_FILE (dirname, "mountall.conf");
4665+ DELETE_FILE (dirname, "gdm.conf");
4666+
4667+ /*******************************************************************/
4668+
4669+ TEST_FEATURE ("unsatisfiable complex stop on");
4670+
4671+ CREATE_FILE (dirname, "plymouth.conf",
4672+ "stop on (starting mountall\n"
4673+ " or (runlevel [016]\n"
4674+ " and (stopped gdm\n"
4675+ " or stopped kdm\n"
4676+ " or stopped xdm\n"
4677+ " or stopped lxdm)))");
4678+
4679+ CREATE_FILE (dirname, "mountall.conf", "exec true");
4680+
4681+ cmd = nih_sprintf (NULL, "%s check-config --ignore-events=runlevel 2>&1",
4682+ INITCTL_BINARY);
4683+ TEST_NE_P (cmd, NULL);
4684+ RUN_COMMAND (NULL, cmd, &output, &lines);
4685+
4686+ TEST_EQ_STR (output[0], "plymouth");
4687+ TEST_EQ_STR (output[1], " stop on: unknown job lxdm");
4688+ TEST_EQ_STR (output[2], " stop on: unknown job xdm");
4689+ TEST_EQ_STR (output[3], " stop on: unknown job kdm");
4690+ TEST_EQ_STR (output[4], " stop on: unknown job gdm");
4691+ TEST_EQ (lines, 5);
4692+
4693+ DELETE_FILE (dirname, "plymouth.conf");
4694+ DELETE_FILE (dirname, "mountall.conf");
4695+
4696+ /*******************************************************************/
4697+
4698+ TEST_FEATURE ("unsatisfiable complex stop on, satisfiable complex start on");
4699+
4700+ CREATE_FILE (dirname, "plymouth.conf",
4701+ "stop on (starting mountall\n"
4702+ " or (runlevel [016]\n"
4703+ " and (stopped gdm\n"
4704+ " or stopped kdm\n"
4705+ " or stopped xdm\n"
4706+ " or stopped lxdm)))\n"
4707+ "start on (stopping portmap\n"
4708+ " or (runlevel [06] or starting beano))\n");
4709+
4710+ CREATE_FILE (dirname, "mountall.conf", "exec true");
4711+ CREATE_FILE (dirname, "portmap.conf", "exec true");
4712+ CREATE_FILE (dirname, "beano.conf", "exec true");
4713+
4714+ cmd = nih_sprintf (NULL, "%s check-config --ignore-events=runlevel 2>&1",
4715+ INITCTL_BINARY);
4716+ TEST_NE_P (cmd, NULL);
4717+ RUN_COMMAND (NULL, cmd, &output, &lines);
4718+
4719+ TEST_EQ_STR (output[0], "plymouth");
4720+ TEST_EQ_STR (output[1], " stop on: unknown job lxdm");
4721+ TEST_EQ_STR (output[2], " stop on: unknown job xdm");
4722+ TEST_EQ_STR (output[3], " stop on: unknown job kdm");
4723+ TEST_EQ_STR (output[4], " stop on: unknown job gdm");
4724+ TEST_EQ (lines, 5);
4725+
4726+ DELETE_FILE (dirname, "plymouth.conf");
4727+ DELETE_FILE (dirname, "mountall.conf");
4728+ DELETE_FILE (dirname, "portmap.conf");
4729+ DELETE_FILE (dirname, "beano.conf");
4730+
4731+ /*******************************************************************/
4732+
4733+ TEST_FEATURE ("satisfiable complex start on, unsatisfiable complex stop on");
4734+
4735+ CREATE_FILE (dirname, "plymouth.conf",
4736+ "start on (starting mountall\n"
4737+ " or (hello\n"
4738+ " and (stopped gdm\n"
4739+ " or stopped kdm\n"
4740+ " or stopped xdm\n"
4741+ " or stopped lxdm)))\n"
4742+ "stop on (stopping portmap\n"
4743+ " or (wibble or starting beano))\n");
4744+
4745+ CREATE_FILE (dirname, "mountall.conf", "exec true");
4746+ CREATE_FILE (dirname, "portmap.conf",
4747+ "exec true\n"
4748+ "emits hello");
4749+ CREATE_FILE (dirname, "gdm.conf", "exec true");
4750+
4751+ cmd = nih_sprintf (NULL, "%s check-config >&1",
4752+ INITCTL_BINARY);
4753+ TEST_NE_P (cmd, NULL);
4754+ RUN_COMMAND (NULL, cmd, &output, &lines);
4755+
4756+ TEST_EQ_STR (output[0], "plymouth");
4757+ TEST_EQ_STR (output[1], " stop on: unknown job beano");
4758+ TEST_EQ_STR (output[2], " stop on: unknown event wibble");
4759+ TEST_EQ (lines, 3);
4760+
4761+ DELETE_FILE (dirname, "plymouth.conf");
4762+ DELETE_FILE (dirname, "mountall.conf");
4763+ DELETE_FILE (dirname, "portmap.conf");
4764+ DELETE_FILE (dirname, "gdm.conf");
4765+
4766+ /*******************************************************************/
4767+
4768+ TEST_FEATURE ("unsatisfiable complex start on, unsatisfiable complex stop on");
4769+
4770+ CREATE_FILE (dirname, "plymouth.conf",
4771+ "start on (starting mountall\n"
4772+ " or (hello\n"
4773+ " and (stopped gdm\n"
4774+ " or stopped kdm\n"
4775+ " or stopped xdm\n"
4776+ " or stopped lxdm)))\n"
4777+ "stop on (stopping portmap\n"
4778+ " or (wibble or starting beano))\n");
4779+
4780+ CREATE_FILE (dirname, "mountall.conf", "exec true");
4781+ CREATE_FILE (dirname, "portmap.conf", "exec true");
4782+
4783+ cmd = nih_sprintf (NULL, "%s check-config 2>&1",
4784+ INITCTL_BINARY);
4785+ TEST_NE_P (cmd, NULL);
4786+ RUN_COMMAND (NULL, cmd, &output, &lines);
4787+
4788+ TEST_EQ_STR (output[0], "plymouth");
4789+ TEST_EQ_STR (output[1], " start on: unknown job lxdm");
4790+ TEST_EQ_STR (output[2], " start on: unknown job xdm");
4791+ TEST_EQ_STR (output[3], " start on: unknown job kdm");
4792+ TEST_EQ_STR (output[4], " start on: unknown job gdm");
4793+ TEST_EQ_STR (output[5], " start on: unknown event hello");
4794+ TEST_EQ_STR (output[6], " stop on: unknown job beano");
4795+ TEST_EQ_STR (output[7], " stop on: unknown event wibble");
4796+ TEST_EQ (lines, 8);
4797+
4798+ DELETE_FILE (dirname, "plymouth.conf");
4799+ DELETE_FILE (dirname, "mountall.conf");
4800+ DELETE_FILE (dirname, "portmap.conf");
4801+
4802+ /*******************************************************************/
4803+
4804+ TEST_FEATURE ("satisfiable complex start on, satisfiable complex stop on");
4805+
4806+ CREATE_FILE (dirname, "plymouth.conf",
4807+ "start on (starting mountall\n"
4808+ " or (hello\n"
4809+ " and (stopped gdm\n"
4810+ " or (stopped kdm\n"
4811+ " or (stopped xdm\n"
4812+ " or stopped lxdm)))))\n"
4813+ "stop on (stopping portmap\n"
4814+ " or (wibble or starting beano))\n");
4815+
4816+ CREATE_FILE (dirname, "mountall.conf", "exec true\n");
4817+ CREATE_FILE (dirname, "portmap.conf",
4818+ "exec true\n"
4819+ "emits hello");
4820+ CREATE_FILE (dirname, "lxdm.conf", "exec true");
4821+ CREATE_FILE (dirname, "wibble.conf", "emits wibble");
4822+ CREATE_FILE (dirname, "beano.conf", "exec true");
4823+
4824+ cmd = nih_sprintf (NULL, "%s check-config 2>&1",
4825+ INITCTL_BINARY);
4826+ TEST_NE_P (cmd, NULL);
4827+ RUN_COMMAND (NULL, cmd, &output, &lines);
4828+
4829+ TEST_EQ (lines, 0);
4830+
4831+ DELETE_FILE (dirname, "plymouth.conf");
4832+ DELETE_FILE (dirname, "mountall.conf");
4833+ DELETE_FILE (dirname, "portmap.conf");
4834+ DELETE_FILE (dirname, "lxdm.conf");
4835+ DELETE_FILE (dirname, "beano.conf");
4836+ DELETE_FILE (dirname, "wibble.conf");
4837+
4838+ /*******************************************************************/
4839+
4840+ TEST_FEATURE (
4841+ "satisfiable complex start on, satisfiable complex stop on with warnings");
4842+
4843+ CREATE_FILE (dirname, "plymouth.conf",
4844+ "start on (starting mountall\n"
4845+ " or (hello\n"
4846+ " and (stopped gdm\n"
4847+ " or (stopped kdm\n"
4848+ " or (stopped xdm\n"
4849+ " or stopped lxdm)))))\n"
4850+ "stop on (stopping portmap\n"
4851+ " or (wibble or starting beano))\n");
4852+
4853+ CREATE_FILE (dirname, "mountall.conf", "exec true\n");
4854+ CREATE_FILE (dirname, "portmap.conf",
4855+ "exec true\n"
4856+ "emits hello");
4857+ CREATE_FILE (dirname, "lxdm.conf", "exec true");
4858+ CREATE_FILE (dirname, "wibble.conf", "emits wibble");
4859+ CREATE_FILE (dirname, "beano.conf", "exec true");
4860+
4861+ cmd = nih_sprintf (NULL, "%s check-config --warn 2>&1",
4862+ INITCTL_BINARY);
4863+ TEST_NE_P (cmd, NULL);
4864+ RUN_COMMAND (NULL, cmd, &output, &lines);
4865+
4866+ TEST_EQ_STR (output[0], "plymouth");
4867+ TEST_EQ_STR (output[1], " start on: unknown job xdm");
4868+ TEST_EQ_STR (output[2], " start on: unknown job kdm");
4869+ TEST_EQ_STR (output[3], " start on: unknown job gdm");
4870+ TEST_EQ (lines, 4);
4871+
4872+ DELETE_FILE (dirname, "plymouth.conf");
4873+ DELETE_FILE (dirname, "mountall.conf");
4874+ DELETE_FILE (dirname, "portmap.conf");
4875+ DELETE_FILE (dirname, "lxdm.conf");
4876+ DELETE_FILE (dirname, "beano.conf");
4877+ DELETE_FILE (dirname, "wibble.conf");
4878+
4879+ /*******************************************************************/
4880+
4881+ STOP_UPSTART (upstart_pid);
4882+ TEST_EQ (unsetenv ("UPSTART_CONFDIR"), 0);
4883+}
4884+
4885
4886 void
4887 test_list_action (void)
4888@@ -10769,7 +11874,7 @@
4889 "NameAcquired"));
4890 dbus_message_unref (method_call);
4891
4892- system_bus = TRUE;
4893+ dbus_bus_type = DBUS_BUS_SYSTEM;
4894 dest_name = DBUS_SERVICE_UPSTART;
4895 dest_address = DBUS_ADDRESS_UPSTART;
4896
4897@@ -12060,7 +13165,7 @@
4898 "NameAcquired"));
4899 dbus_message_unref (method_call);
4900
4901- system_bus = TRUE;
4902+ dbus_bus_type = DBUS_BUS_SYSTEM;
4903 dest_name = DBUS_SERVICE_UPSTART;
4904 dest_address = DBUS_ADDRESS_UPSTART;
4905
4906@@ -12494,7 +13599,7 @@
4907 "NameAcquired"));
4908 dbus_message_unref (method_call);
4909
4910- system_bus = TRUE;
4911+ dbus_bus_type = DBUS_BUS_SYSTEM;
4912 dest_name = DBUS_SERVICE_UPSTART;
4913 dest_address = DBUS_ADDRESS_UPSTART;
4914
4915@@ -12684,7 +13789,7 @@
4916 "NameAcquired"));
4917 dbus_message_unref (method_call);
4918
4919- system_bus = TRUE;
4920+ dbus_bus_type = DBUS_BUS_SYSTEM;
4921 dest_name = DBUS_SERVICE_UPSTART;
4922 dest_address = DBUS_ADDRESS_UPSTART;
4923
4924@@ -12904,7 +14009,7 @@
4925 "NameAcquired"));
4926 dbus_message_unref (method_call);
4927
4928- system_bus = TRUE;
4929+ dbus_bus_type = DBUS_BUS_SYSTEM;
4930 dest_name = DBUS_SERVICE_UPSTART;
4931 dest_address = DBUS_ADDRESS_UPSTART;
4932
4933@@ -13283,6 +14388,52 @@
4934 }
4935
4936
4937+/**
4938+ * in_chroot:
4939+ *
4940+ * Determine if running inside a chroot environment.
4941+ *
4942+ * Failures are fatal.
4943+ *
4944+ * Returns TRUE if within a chroot, else FALSE.
4945+ **/
4946+int
4947+in_chroot (void)
4948+{
4949+ struct stat st;
4950+ int i;
4951+ char dir[] = "/";
4952+
4953+ i = stat(dir, &st);
4954+
4955+ if ( i != 0 ) {
4956+ fprintf (stderr, "ERROR: cannot stat '%s'\n", dir);
4957+ exit (EXIT_FAILURE);
4958+ }
4959+
4960+ if ( st.st_ino == 2 )
4961+ return FALSE;
4962+
4963+ return TRUE;
4964+}
4965+
4966+/**
4967+ * dbus_configured
4968+ *
4969+ * Determine if D-Bus has been configured (with dbus-uuidgen).
4970+ *
4971+ * Returns TRUE if D-Bus appears to have been configured,
4972+ * else FALSE.
4973+ **/
4974+int
4975+dbus_configured (void)
4976+{
4977+ struct stat st;
4978+ char path[] = "/var/lib/dbus/machine-id";
4979+
4980+ return !stat (path, &st);
4981+}
4982+
4983 int
4984 main (int argc,
4985 char *argv[])
4986@@ -13308,5 +14459,16 @@
4987 test_version_action ();
4988 test_log_priority_action ();
4989
4990+ if (in_chroot () && !dbus_configured ()) {
4991+ fprintf(stderr, "\n\n"
4992+ "WARNING: not running show-config "
4993+ "and check-config tests within chroot "
4994+ "as no D-Bus, or D-Bus not configured (lp:#728988)"
4995+ "\n\n");
4996+ } else {
4997+ test_show_config ();
4998+ test_check_config ();
4999+ }
5000+
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches