Merge lp:~jamesodhunt/upstart/upstream-override-support into lp:upstart

Proposed by James Hunt
Status: Merged
Merged at revision: 1303
Proposed branch: lp:~jamesodhunt/upstart/upstream-override-support
Merge into: lp:upstart
Diff against target: 15469 lines (+8378/-1087)
57 files modified
ChangeLog (+219/-0)
Makefile.am (+1/-1)
TESTING.sessions (+252/-0)
conf/rc-sysinit.conf (+2/-0)
configure.ac (+3/-2)
contrib/bash_completion/upstart (+96/-36)
dbus/Upstart.conf (+6/-36)
dbus/upstart.h (+1/-1)
init/Makefile.am (+12/-0)
init/conf.c (+505/-55)
init/conf.h (+45/-2)
init/control.c (+73/-10)
init/control.h (+15/-1)
init/environ.c (+0/-1)
init/environ.h (+0/-1)
init/event.c (+10/-0)
init/event.h (+5/-1)
init/job.c (+44/-3)
init/job.h (+1/-1)
init/job_class.c (+114/-24)
init/job_class.h (+5/-1)
init/job_process.c (+59/-1)
init/job_process.h (+4/-3)
init/main.c (+234/-136)
init/man/init.5 (+170/-44)
init/man/init.8 (+30/-4)
init/parse_conf.c (+6/-0)
init/parse_job.c (+21/-7)
init/parse_job.h (+4/-2)
init/paths.h (+75/-2)
init/session.c (+279/-0)
init/session.h (+87/-0)
init/tests/test_blocked.c (+4/-1)
init/tests/test_conf.c (+1310/-15)
init/tests/test_control.c (+15/-11)
init/tests/test_environ.c (+3/-1)
init/tests/test_event.c (+28/-25)
init/tests/test_event_operator.c (+3/-0)
init/tests/test_job.c (+71/-76)
init/tests/test_job_class.c (+56/-65)
init/tests/test_job_process.c (+34/-30)
init/tests/test_parse_conf.c (+3/-0)
init/tests/test_parse_job.c (+302/-260)
init/tests/test_process.c (+3/-0)
init/tests/test_system.c (+3/-0)
po/upstart.pot (+307/-155)
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 (+200/-35)
util/reboot.c (+0/-1)
util/tests/test_initctl.c (+1181/-19)
To merge this branch: bzr merge lp:~jamesodhunt/upstart/upstream-override-support
Reviewer Review Type Date Requested Status
Upstart Developers Pending
Review via email: mp+63569@code.launchpad.net

Description of the change

Add override file support.

* init/conf.c:
  - conf_reload_path(): Now takes an extra override_path parameter.
  - is_conf_file() / is_conf_file_std() / is_conf_file_override(): New
    functions to determine type of given file path.
  - toggle_conf_name(): New function which convert a conf file
    name to an override name and vice versa.
  - majority of remaining functions updated to handle override
    files.
* init/conf.h: Prototypes.
* init/job_class.c: Whitespace.
* init/man/init.5: Updated to document override file support.
* init/man/init.8: Added reference to control-alt-delete(7) man page.
* init/paths.h: New macros CONF_EXT_OVERRIDE, CONF_EXT_STD,
  IS_CONF_FILE_OVERRIDE and IS_CONF_FILE_STD.
* init/parse_conf.c: Added assertion to remind us forcibly to add
  override-handling code for directories if we ever allow content in
  'init.conf'.
* init/parse_job.c (parse_job): Additional parameter 'update' to
  allow override files to replace existing Job details.
* init/parse_job.h: Updated parse_job() prototype.
* init/test_conf.c
  - New macros TEST_ENSURE_CLEAN_ENV() and
    TEST_FORCE_WATCH_UPDATE().
  - test_override(): New function.
  - test_toggle_conf_name(): New function.
* init/test_parse_job.c:
  - Updated for extra parse_job() parameter.
  - added a test feature to test_parse_job() to exercise new
    parameter to parse_job().
* util/man/initctl.8: Clarified what it means to restart a job.

To post a comment you must log in.

Preview Diff

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

Subscribers

People subscribed via source and target branches