Merge lp:upstart into lp:~daniel-sonck/upstart/cron-replacement

Proposed by Daniël Sonck on 2011-10-14
Status: Merged
Approved by: Daniël Sonck on 2011-10-14
Approved revision: 1329
Merged at revision: 1284
Proposed branch: lp:upstart
Merge into: lp:~daniel-sonck/upstart/cron-replacement
Diff against target: 18561 lines (+11017/-1183) (has conflicts)
73 files modified
.bzrignore (+4/-0)
ChangeLog (+365/-0)
Makefile.am (+1/-1)
NEWS (+35/-1)
TESTING.sessions (+289/-0)
conf/rc-sysinit.conf (+2/-0)
configure.ac (+7/-3)
contrib/bash_completion/upstart (+96/-36)
contrib/vim/syntax/upstart.vim (+61/-27)
dbus/Upstart.conf (+6/-36)
dbus/com.ubuntu.Upstart.xml (+7/-0)
dbus/upstart.h (+1/-1)
extra/Makefile.am (+125/-0)
extra/conf/upstart-socket-bridge.conf (+16/-0)
extra/conf/upstart-udev-bridge.conf (+16/-0)
extra/man/socket-event.7 (+92/-0)
extra/man/upstart-socket-bridge.8 (+47/-0)
extra/man/upstart-udev-bridge.8 (+57/-0)
extra/upstart-socket-bridge.c (+644/-0)
extra/upstart-udev-bridge.c (+310/-0)
init/Makefile.am (+12/-0)
init/conf.c (+504/-55)
init/conf.h (+45/-2)
init/control.c (+111/-19)
init/control.h (+19/-1)
init/environ.c (+0/-1)
init/environ.h (+0/-1)
init/event.c (+22/-0)
init/event.h (+6/-1)
init/event_operator.c (+59/-0)
init/event_operator.h (+8/-0)
init/job.c (+47/-3)
init/job.h (+4/-1)
init/job_class.c (+115/-67)
init/job_class.h (+62/-2)
init/job_process.c (+122/-14)
init/job_process.h (+5/-3)
init/main.c (+297/-134)
init/man/init.5 (+236/-48)
init/man/init.8 (+32/-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 (+320/-0)
init/session.h (+89/-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/POTFILES.in (+2/-0)
po/upstart.pot (+352/-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 (+207/-34)
util/reboot.c (+0/-1)
util/tests/test_initctl.c (+1189/-19)
util/tests/test_user_sessions.sh (+553/-0)
Text conflict in init/main.c
To merge this branch: bzr merge lp:upstart
Reviewer Review Type Date Requested Status
Daniël Sonck Approve on 2011-10-14
Review via email: mp+79391@code.launchpad.net
To post a comment you must log in.
review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches