Merge lp:~jamesodhunt/upstart/upstream-add-init-checkconf into lp:upstart

Proposed by James Hunt
Status: Superseded
Proposed branch: lp:~jamesodhunt/upstart/upstream-add-init-checkconf
Merge into: lp:upstart
Diff against target: 6057 lines (+4544/-337)
33 files modified
ChangeLog (+131/-0)
Makefile.am (+1/-1)
conf/rc-sysinit.conf (+2/-0)
configure.ac (+3/-2)
dbus/upstart.h (+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 (+1/-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)
scripts/Makefile.am (+25/-0)
scripts/init-checkconf.sh (+248/-0)
scripts/initctl2dot.py (+571/-0)
scripts/man/init-checkconf.8 (+73/-0)
scripts/man/initctl2dot.8 (+87/-0)
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-add-init-checkconf
Reviewer Review Type Date Requested Status
Upstart Developers Pending
Review via email: mp+63352@code.launchpad.net

This proposal has been superseded by a proposal from 2011-06-06.

Description of the change

Addition of init-checkconf script.

* Changelog: Updated.
* scripts/init-checkconf.sh: Script to determine if specified job
  config file is valid or not.
* scripts/man/init-checkconf.8: Man page for init-checkconf.sh.
* scripts/Makefile.am: Added init-checkconf script and man page.

To post a comment you must log in.

Unmerged revisions

Preview Diff

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

Subscribers

People subscribed via source and target branches