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