Merge lp:~upstart-devel/upstart/stateful-reexec into lp:upstart
- stateful-reexec
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 1382 |
Proposed branch: | lp:~upstart-devel/upstart/stateful-reexec |
Merge into: | lp:upstart |
Diff against target: |
15453 lines (+11010/-675) 41 files modified
ChangeLog (+16/-0) configure.ac (+33/-0) extra/man/upstart-udev-bridge.8 (+4/-1) init/Makefile.am (+64/-33) init/blocked.c (+53/-0) init/blocked.h (+8/-0) init/conf.c (+93/-35) init/conf.h (+12/-4) init/control.c (+145/-2) init/control.h (+15/-0) init/event.c (+331/-0) init/event.h (+22/-0) init/event_operator.c (+175/-7) init/event_operator.h (+7/-4) init/job.c (+726/-7) init/job.h (+14/-2) init/job_class.c (+712/-3) init/job_class.h (+36/-1) init/job_process.c (+44/-7) init/job_process.h (+6/-0) init/log.c (+213/-15) init/log.h (+13/-5) init/main.c (+114/-26) init/parse_job.c (+42/-1) init/parse_job.h (+5/-0) init/process.c (+223/-0) init/process.h (+38/-3) init/session.c (+298/-33) init/session.h (+19/-6) init/state.c (+2008/-0) init/state.h (+1226/-0) init/tests/test_conf.c (+1/-43) init/tests/test_event_operator.c (+92/-1) init/tests/test_job.c (+215/-60) init/tests/test_job_process.c (+508/-291) init/tests/test_log.c (+19/-35) init/tests/test_state.c (+2637/-0) init/tests/test_util.c (+27/-0) init/tests/test_util.h (+304/-0) util/man/telinit.8 (+4/-3) util/tests/test_initctl.c (+488/-47) |
To merge this branch: | bzr merge lp:~upstart-devel/upstart/stateful-reexec |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Steve Langasek | Approve | ||
Review via email: mp+133275@code.launchpad.net |
This proposal supersedes a proposal from 2012-11-07.
Commit message
Description of the change
* Added stateful re-exec support such that when Upstart is asked
to restart itself using 'telinit u', the new instance of PID 1
will retain knowledge of all system jobs and running instances.
See also: https:/
- 1474. By James Hunt
-
Sync with lp:~upstart-devel/upstart/stateful-reexec.
- 1475. By James Hunt
-
* init/job_class.c: Formatting.
* init/log.c: Corrected documentation for log_unflushed_files.
* init/state.h: Explained why log_unflushed_files is not currently
serialised. - 1476. By James Hunt
-
* extra/man/
upstart- udev-bridge. 8: Corrected explanation of actions in
udev events (thanks to Stefan Bader for pointing out this problem).
* init/blocked.c: blocked_type_str_ to_enum( ): Assert argument.
* init/job_class.c: job_class_expect_ type_str_ to_enum( ): Assert
argument.
* init/job_process.c: job_process_run(): Reverted unnecessary check on 'p'.
* init/log.c: log_deserialise(): Added cast and explanation why return code
from state_toggle_cloexec( ) not checked.
* init/process.c: process_type_str_ to_enum( ): Assert argument.
* init/session.c:
- session_create_ conf_source( ): Removed redundant getenv() call.
- session_deserialise( ): Removed incorrect uid_t initialisation.
* init/state.h:
- state_get_json_str_ array_generic( ): Actually use env parameter.
- Remove warn_unused_result from state_toggle_cloexec( ) to avoid compile
failure with strict flags.
* init/tests/test_state. c:
- Use rlim_t.
- test_enums():
- Don't call *_str_to_enum() if previous *_enum_to_str() failed.
- Add additional checks for invalid enum values.
- main(): Remove redundant setenv().
* init/tests/test_util. c: string_check(): Make initial test less
cryptic.
James Hunt (jamesodhunt) wrote : | # |
> Hi James,
>
> Reviewing just the parts of the diff that are new since the last time I looked at the branch.
>
> This part of init/session.c looks strange:
>
> + if (getenv ("UPSTART_TESTS"))
> + return;
> +
> if (conf_source_reload (source) < 0) {
> NihError *err;
>
> The commit message only says:
> - session_
> testing mode.
> without explaining why.
I've now removed this: it was required for an earlier iteration of the test code, but is now redundant.
>
> In init/tests/
>
> + * Returns 0 if strings are identical or both NULL, else 1.
> + **/
> +int
> +string_check (const char *a, const char *b)
> +{
> + if ((a == b) && !a)
> + return 0;
> +
>
> This '&& !a' seems unnecessary - surely if a == b and are /not/ null, there's no need to call strcmp to confirm that the strings are equal.
That test is a just a more compact way of saying:
if (a == NULL && b == NULL)
... such that -- considered with the following test -- we never pass NULL to strcmp(). However, it is slightly cryptic, so for symmetry with the test that follows it I've changed the test to:
if (!a && !b)
>
>
> === modified file 'init/blocked.c'
> --- init/blocked.c 2012-07-30 14:07:37 +0000
> +++ init/blocked.c 2012-11-13 06:01:51 +0000
> @@ -131,6 +131,9 @@ blocked_
> BlockedType
> blocked_
> {
> + if (! type)
> + goto error;
> +
> state_str_to_enum (BLOCKED_JOB, type);
> state_str_to_enum (BLOCKED_EVENT, type);
> state_str_to_enum (BLOCKED_
> @@ -141,5 +144,6 @@ blocked_
> state_str_to_enum (BLOCKED_
> state_str_to_enum (BLOCKED_
>
> +error:
> return -1;
> }
>
>
> The above is documented as "Made resilient to error conditions". But this doesn't appear to be a real-world error condition at all; the only place in the code that blocked_
>
> if (! state_get_
> goto error;
>
> blocked_type = blocked_
>
> This means there's no possibility of the function ever being called with a null argument (and likewise for the other str_to_enum functions), so this must be here just for the test suite. I don't think it's a good idea to complicate either the code or the test suite by handling errors that the code is written to never allow! I've pushed a branch to address this at
> lp:~vorlon/upstart/stateful-reexec-dont-shape-code-to-impossible-tests and raised an MP for your consideration.
Thanks. I've changed this and the other *_str_to_enum() functions to assert their argument and updated the tests accordingly.
>
> === modified file 'init/job_
> --- init/job_process.c 2012-08-25 08:53:37 +0000
> +++ init/job_process.c 2012-11-13 10:47:50 +0000
> @@ -211,7 +211,7 @@ job_process_run (...
Preview Diff
1 | === modified file 'ChangeLog' |
2 | --- ChangeLog 2012-10-22 13:54:50 +0000 |
3 | +++ ChangeLog 2012-11-14 14:49:20 +0000 |
4 | @@ -1,3 +1,19 @@ |
5 | +2012-11-07 James Hunt <james.hunt@ubuntu.com> |
6 | + |
7 | + * Added stateful re-exec support such that when Upstart is asked |
8 | + to restart itself using 'telinit u', the new instance of PID 1 |
9 | + will retain knowledge of all system jobs and running instances. |
10 | + |
11 | +2012-09-10 James Hunt <james.hunt@ubuntu.com> |
12 | + |
13 | + * Merged lp:~jconti/upstart/fix_empty_chroot. |
14 | + |
15 | +2012-05-23 James Hunt <james.hunt@ubuntu.com> |
16 | + |
17 | + * init/main.c: Add in "bare" re-exec handling from Ubuntu |
18 | + branch. |
19 | + * init/main.c: Unhide "restart" option. |
20 | + |
21 | 2012-10-22 James Hunt <james.hunt@ubuntu.com> |
22 | |
23 | * init/parse_job.c: stanza_kill(): Actually save parsed |
24 | |
25 | === modified file 'configure.ac' |
26 | --- configure.ac 2012-03-22 11:37:20 +0000 |
27 | +++ configure.ac 2012-11-14 14:49:20 +0000 |
28 | @@ -32,6 +32,24 @@ |
29 | PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.2.16]) |
30 | PKG_CHECK_MODULES([UDEV], [libudev >= 146], [have_udev=yes], [have_udev=no]) |
31 | |
32 | +# Reasons for requiring this library version: |
33 | +# |
34 | +# 1) RFC 4627, the JSON "memo" (it is *NOT* a standard!) helpfully fails |
35 | +# to specify the maximum size of an integer type. And yet, JSON is |
36 | +# supposedly a subset of ECMA-262, which specifies a Number type to |
37 | +# be 64-bits. |
38 | +# |
39 | +# The loose JSON "memo" might expalain why older versions of JSON-C encode |
40 | +# a JSON Number in sizoef(int) bytes which is only 32-bits on 32-bit |
41 | +# systems. This is not acceptable for Upstart which needs to be able |
42 | +# to encode 'size_t' and 'unsigned long' types which can be larger |
43 | +# than 'int'. |
44 | +# |
45 | +# 2) New json_tokener_parse_verbose() function required to detect invalid |
46 | +# JSON (!) |
47 | +# |
48 | +PKG_CHECK_MODULES([JSON], [json >= 0.10]) |
49 | + |
50 | AM_CONDITIONAL([HAVE_UDEV], [test "$have_udev" = yes]) |
51 | |
52 | # Checks for header files. |
53 | @@ -42,6 +60,21 @@ |
54 | AM_PROG_CC_C_O |
55 | NIH_C_THREAD |
56 | |
57 | +AC_CHECK_SIZEOF(int) |
58 | +AC_CHECK_SIZEOF(uid_t) |
59 | +AC_CHECK_SIZEOF(pid_t) |
60 | +AC_CHECK_SIZEOF(time_t) |
61 | +AC_CHECK_SIZEOF(mode_t) |
62 | +AC_CHECK_SIZEOF(size_t) |
63 | +AC_CHECK_SIZEOF(ssize_t) |
64 | + |
65 | +# Unlikely to hit this limit for a while, but it pays to be safe. |
66 | +for type in int uid_t pid_t time_t mode_t size_t ssize_t |
67 | +do |
68 | + eval value="\$ac_cv_sizeof_${type}" |
69 | + test "$value" -gt 8 && AC_MSG_ERROR([type $type is $value bytes, larger than JSON-C can represent]) |
70 | +done |
71 | + |
72 | # Checks for library functions. |
73 | |
74 | # Other checks |
75 | |
76 | === modified file 'extra/man/upstart-udev-bridge.8' |
77 | --- extra/man/upstart-udev-bridge.8 2011-12-15 16:11:14 +0000 |
78 | +++ extra/man/upstart-udev-bridge.8 2012-11-14 14:49:20 +0000 |
79 | @@ -16,7 +16,10 @@ |
80 | events for them. |
81 | |
82 | It emits events which match the pattern "\fIS\fP\-device\-\fIA\fP" where |
83 | -\(aqS\(aq is the udev \fIsubsystem\fP and \(aqA\(aq is the udev \fIaction\fP. |
84 | +\(aqS\(aq is the udev \fIsubsystem\fP and \(aqA\(aq is one of |
85 | +\fIadded\fR, \fIchanged\fR or \fIremoved\fR, |
86 | +which correspond to the past tense of the udev \(aqaction\(aq. |
87 | + |
88 | See \fBudev\fP(7) and for further details. |
89 | |
90 | Assuming \fI/sys\fP is mounted, possible values for \fIsubsystem\fP for |
91 | |
92 | === modified file 'init/Makefile.am' |
93 | --- init/Makefile.am 2012-10-22 13:54:50 +0000 |
94 | +++ init/Makefile.am 2012-11-14 14:49:20 +0000 |
95 | @@ -5,7 +5,8 @@ |
96 | AM_CFLAGS = \ |
97 | $(NIH_CFLAGS) \ |
98 | $(NIH_DBUS_CFLAGS) \ |
99 | - $(DBUS_CFLAGS) |
100 | + $(DBUS_CFLAGS) \ |
101 | + $(JSON_CFLAGS) |
102 | |
103 | AM_CPPFLAGS = \ |
104 | -DLOCALEDIR="\"$(localedir)\"" \ |
105 | @@ -41,6 +42,7 @@ |
106 | environ.c environ.h \ |
107 | process.c process.h \ |
108 | session.c session.h \ |
109 | + state.c state.h \ |
110 | job_class.c job_class.h \ |
111 | job_process.c job_process.h \ |
112 | job.c job.h \ |
113 | @@ -62,6 +64,7 @@ |
114 | $(NIH_LIBS) \ |
115 | $(NIH_DBUS_LIBS) \ |
116 | $(DBUS_LIBS) \ |
117 | + $(JSON_LIBS) \ |
118 | -lrt |
119 | |
120 | |
121 | @@ -109,7 +112,6 @@ |
122 | --default-interface=com.ubuntu.Upstart0_6.Instance \ |
123 | --output=$@ $< |
124 | |
125 | - |
126 | # These have to be built sources because we can't compile object files |
127 | # without the header file existing first |
128 | BUILT_SOURCES = \ |
129 | @@ -125,6 +127,8 @@ |
130 | |
131 | EXTRA_DIST = init.supp |
132 | |
133 | +test_util_SOURCES = \ |
134 | + tests/test_util.c tests/test_util.h |
135 | |
136 | TESTS = \ |
137 | test_system \ |
138 | @@ -134,6 +138,7 @@ |
139 | test_job_process \ |
140 | test_job \ |
141 | test_log \ |
142 | + test_state \ |
143 | test_event \ |
144 | test_event_operator \ |
145 | test_blocked \ |
146 | @@ -156,41 +161,44 @@ |
147 | environ.o \ |
148 | $(NIH_LIBS) |
149 | |
150 | -test_process_SOURCES = tests/test_process.c |
151 | +test_process_SOURCES = tests/test_process.c $(test_util_SOURCES) |
152 | test_process_LDADD = \ |
153 | system.o environ.o process.o \ |
154 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
155 | parse_job.o parse_conf.o conf.o control.o \ |
156 | - session.o log.o \ |
157 | + session.o log.o state.o \ |
158 | com.ubuntu.Upstart.o \ |
159 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
160 | $(NIH_LIBS) \ |
161 | $(NIH_DBUS_LIBS) \ |
162 | - $(DBUS_LIBS) |
163 | + $(DBUS_LIBS) \ |
164 | + $(JSON_LIBS) |
165 | |
166 | test_job_class_SOURCES = tests/test_job_class.c |
167 | test_job_class_LDADD = \ |
168 | system.o environ.o process.o \ |
169 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
170 | parse_job.o parse_conf.o conf.o control.o \ |
171 | - session.o log.o \ |
172 | + session.o log.o state.o \ |
173 | com.ubuntu.Upstart.o \ |
174 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
175 | $(NIH_LIBS) \ |
176 | $(NIH_DBUS_LIBS) \ |
177 | - $(DBUS_LIBS) |
178 | + $(DBUS_LIBS) \ |
179 | + $(JSON_LIBS) |
180 | |
181 | -test_job_process_SOURCES = tests/test_job_process.c |
182 | +test_job_process_SOURCES = tests/test_job_process.c $(test_util_SOURCES) |
183 | test_job_process_LDADD = \ |
184 | system.o environ.o process.o \ |
185 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
186 | parse_job.o parse_conf.o conf.o control.o \ |
187 | - session.o log.o \ |
188 | + session.o log.o state.o \ |
189 | com.ubuntu.Upstart.o \ |
190 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
191 | $(NIH_LIBS) \ |
192 | $(NIH_DBUS_LIBS) \ |
193 | $(DBUS_LIBS) \ |
194 | + $(JSON_LIBS) \ |
195 | -lutil |
196 | |
197 | test_job_SOURCES = tests/test_job.c |
198 | @@ -198,24 +206,40 @@ |
199 | system.o environ.o process.o \ |
200 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
201 | parse_job.o parse_conf.o conf.o control.o \ |
202 | - session.o log.o \ |
203 | + session.o log.o state.o \ |
204 | com.ubuntu.Upstart.o \ |
205 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
206 | $(NIH_LIBS) \ |
207 | $(NIH_DBUS_LIBS) \ |
208 | - $(DBUS_LIBS) |
209 | + $(DBUS_LIBS) \ |
210 | + $(JSON_LIBS) |
211 | |
212 | -test_log_SOURCES = tests/test_log.c |
213 | +test_log_SOURCES = tests/test_log.c $(test_util_SOURCES) |
214 | test_log_LDADD = \ |
215 | system.o environ.o process.o \ |
216 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
217 | parse_job.o parse_conf.o conf.o control.o \ |
218 | - session.o log.o \ |
219 | - com.ubuntu.Upstart.o \ |
220 | - com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
221 | - $(NIH_LIBS) \ |
222 | - $(NIH_DBUS_LIBS) \ |
223 | - $(DBUS_LIBS) \ |
224 | + session.o log.o state.o \ |
225 | + com.ubuntu.Upstart.o \ |
226 | + com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
227 | + $(NIH_LIBS) \ |
228 | + $(NIH_DBUS_LIBS) \ |
229 | + $(DBUS_LIBS) \ |
230 | + $(JSON_LIBS) \ |
231 | + -lutil |
232 | + |
233 | +test_state_SOURCES = tests/test_state.c $(test_util_SOURCES) |
234 | +test_state_LDADD = \ |
235 | + system.o environ.o process.o \ |
236 | + job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
237 | + parse_job.o parse_conf.o conf.o control.o \ |
238 | + session.o log.o state.o \ |
239 | + com.ubuntu.Upstart.o \ |
240 | + com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
241 | + $(NIH_LIBS) \ |
242 | + $(NIH_DBUS_LIBS) \ |
243 | + $(DBUS_LIBS) \ |
244 | + $(JSON_LIBS) \ |
245 | -lutil |
246 | |
247 | test_event_SOURCES = tests/test_event.c |
248 | @@ -223,84 +247,91 @@ |
249 | system.o environ.o process.o \ |
250 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
251 | parse_job.o parse_conf.o conf.o control.o \ |
252 | - session.o log.o \ |
253 | + session.o log.o state.o \ |
254 | com.ubuntu.Upstart.o \ |
255 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
256 | $(NIH_LIBS) \ |
257 | $(NIH_DBUS_LIBS) \ |
258 | - $(DBUS_LIBS) |
259 | + $(DBUS_LIBS) \ |
260 | + $(JSON_LIBS) |
261 | |
262 | test_event_operator_SOURCES = tests/test_event_operator.c |
263 | test_event_operator_LDADD = \ |
264 | system.o environ.o process.o \ |
265 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
266 | parse_job.o parse_conf.o conf.o control.o \ |
267 | - session.o log.o \ |
268 | + session.o log.o state.o \ |
269 | com.ubuntu.Upstart.o \ |
270 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
271 | $(NIH_LIBS) \ |
272 | $(NIH_DBUS_LIBS) \ |
273 | - $(DBUS_LIBS) |
274 | + $(DBUS_LIBS) \ |
275 | + $(JSON_LIBS) |
276 | |
277 | test_blocked_SOURCES = tests/test_blocked.c |
278 | test_blocked_LDADD = \ |
279 | system.o environ.o process.o \ |
280 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
281 | parse_job.o parse_conf.o conf.o control.o \ |
282 | - session.o log.o \ |
283 | + session.o log.o state.o \ |
284 | com.ubuntu.Upstart.o \ |
285 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
286 | $(NIH_LIBS) \ |
287 | $(NIH_DBUS_LIBS) \ |
288 | - $(DBUS_LIBS) |
289 | + $(DBUS_LIBS) \ |
290 | + $(JSON_LIBS) |
291 | |
292 | test_parse_job_SOURCES = tests/test_parse_job.c |
293 | test_parse_job_LDADD = \ |
294 | system.o environ.o process.o \ |
295 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
296 | parse_job.o parse_conf.o conf.o control.o \ |
297 | - session.o log.o \ |
298 | + session.o log.o state.o \ |
299 | com.ubuntu.Upstart.o \ |
300 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
301 | $(NIH_LIBS) \ |
302 | $(NIH_DBUS_LIBS) \ |
303 | - $(DBUS_LIBS) |
304 | + $(DBUS_LIBS) \ |
305 | + $(JSON_LIBS) |
306 | |
307 | test_parse_conf_SOURCES = tests/test_parse_conf.c |
308 | test_parse_conf_LDADD = \ |
309 | system.o environ.o process.o \ |
310 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
311 | parse_job.o parse_conf.o conf.o control.o \ |
312 | - session.o log.o \ |
313 | + session.o log.o state.o \ |
314 | com.ubuntu.Upstart.o \ |
315 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
316 | $(NIH_LIBS) \ |
317 | $(NIH_DBUS_LIBS) \ |
318 | - $(DBUS_LIBS) |
319 | + $(DBUS_LIBS) \ |
320 | + $(JSON_LIBS) |
321 | |
322 | -test_conf_SOURCES = tests/test_conf.c |
323 | +test_conf_SOURCES = tests/test_conf.c $(test_util_SOURCES) |
324 | test_conf_LDADD = \ |
325 | system.o environ.o process.o \ |
326 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
327 | parse_job.o parse_conf.o conf.o control.o \ |
328 | - session.o log.o \ |
329 | + session.o log.o state.o \ |
330 | com.ubuntu.Upstart.o \ |
331 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
332 | $(NIH_LIBS) \ |
333 | $(NIH_DBUS_LIBS) \ |
334 | - $(DBUS_LIBS) |
335 | + $(DBUS_LIBS) \ |
336 | + $(JSON_LIBS) |
337 | |
338 | test_control_SOURCES = tests/test_control.c |
339 | test_control_LDADD = \ |
340 | system.o environ.o process.o \ |
341 | job_class.o job_process.o job.o event.o event_operator.o blocked.o \ |
342 | parse_job.o parse_conf.o conf.o control.o \ |
343 | - session.o log.o \ |
344 | + session.o log.o state.o \ |
345 | com.ubuntu.Upstart.o \ |
346 | com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \ |
347 | $(NIH_LIBS) \ |
348 | $(NIH_DBUS_LIBS) \ |
349 | - $(DBUS_LIBS) |
350 | + $(DBUS_LIBS) \ |
351 | + $(JSON_LIBS) |
352 | |
353 | |
354 | install-data-local: |
355 | |
356 | === modified file 'init/blocked.c' |
357 | --- init/blocked.c 2009-06-23 09:29:35 +0000 |
358 | +++ init/blocked.c 2012-11-14 14:49:20 +0000 |
359 | @@ -92,3 +92,56 @@ |
360 | |
361 | return blocked; |
362 | } |
363 | + |
364 | + |
365 | +/** |
366 | + * blocked_enum_to_str: |
367 | + * |
368 | + * @type: BlockedType. |
369 | + * |
370 | + * Convert Blocked to a string representation. |
371 | + * |
372 | + * Returns: string representation of @type, or NULL if not known. |
373 | + **/ |
374 | +const char * |
375 | +blocked_type_enum_to_str (BlockedType type) |
376 | +{ |
377 | + state_enum_to_str (BLOCKED_JOB, type); |
378 | + state_enum_to_str (BLOCKED_EVENT, type); |
379 | + state_enum_to_str (BLOCKED_EMIT_METHOD, type); |
380 | + state_enum_to_str (BLOCKED_JOB_START_METHOD, type); |
381 | + state_enum_to_str (BLOCKED_JOB_STOP_METHOD, type); |
382 | + state_enum_to_str (BLOCKED_JOB_RESTART_METHOD, type); |
383 | + state_enum_to_str (BLOCKED_INSTANCE_START_METHOD, type); |
384 | + state_enum_to_str (BLOCKED_INSTANCE_STOP_METHOD, type); |
385 | + state_enum_to_str (BLOCKED_INSTANCE_RESTART_METHOD, type); |
386 | + |
387 | + return NULL; |
388 | +} |
389 | + |
390 | +/** |
391 | + * blocked_type_str_to_enum: |
392 | + * |
393 | + * @type: string BlockedType value. |
394 | + * |
395 | + * Convert @type back into enum value. |
396 | + * |
397 | + * Returns: BlockedType representation of @type, or -1 if not known. |
398 | + **/ |
399 | +BlockedType |
400 | +blocked_type_str_to_enum (const char *type) |
401 | +{ |
402 | + nih_assert (type); |
403 | + |
404 | + state_str_to_enum (BLOCKED_JOB, type); |
405 | + state_str_to_enum (BLOCKED_EVENT, type); |
406 | + state_str_to_enum (BLOCKED_EMIT_METHOD, type); |
407 | + state_str_to_enum (BLOCKED_JOB_START_METHOD, type); |
408 | + state_str_to_enum (BLOCKED_JOB_STOP_METHOD, type); |
409 | + state_str_to_enum (BLOCKED_JOB_RESTART_METHOD, type); |
410 | + state_str_to_enum (BLOCKED_INSTANCE_START_METHOD, type); |
411 | + state_str_to_enum (BLOCKED_INSTANCE_STOP_METHOD, type); |
412 | + state_str_to_enum (BLOCKED_INSTANCE_RESTART_METHOD, type); |
413 | + |
414 | + return -1; |
415 | +} |
416 | |
417 | === modified file 'init/blocked.h' |
418 | --- init/blocked.h 2009-06-23 09:29:35 +0000 |
419 | +++ init/blocked.h 2012-11-14 14:49:20 +0000 |
420 | @@ -79,6 +79,14 @@ |
421 | Blocked *blocked_new (const void *parent, BlockedType type, void *data) |
422 | __attribute__ ((warn_unused_result, malloc)); |
423 | |
424 | +const char * |
425 | +blocked_type_enum_to_str (BlockedType type) |
426 | + __attribute__ ((warn_unused_result)); |
427 | + |
428 | +BlockedType |
429 | +blocked_type_str_to_enum (const char *type) |
430 | + __attribute__ ((warn_unused_result)); |
431 | + |
432 | NIH_END_EXTERN |
433 | |
434 | #endif /* INIT_BLOCKED_H */ |
435 | |
436 | === modified file 'init/conf.c' |
437 | --- init/conf.c 2011-06-06 17:05:11 +0000 |
438 | +++ init/conf.c 2012-11-14 14:49:20 +0000 |
439 | @@ -952,8 +952,8 @@ |
440 | **/ |
441 | static int |
442 | conf_reload_path (ConfSource *source, |
443 | - const char *path, |
444 | - const char *override_path) |
445 | + const char *path, |
446 | + const char *override_path) |
447 | { |
448 | ConfFile *file = NULL; |
449 | nih_local char *buf = NULL; |
450 | @@ -1209,52 +1209,58 @@ |
451 | } |
452 | |
453 | void |
454 | -debug_show_job_class (const JobClass *job) |
455 | +debug_show_job_class (const JobClass *class) |
456 | { |
457 | - int i; |
458 | - char **env = (char **)job->env; |
459 | - char **export = (char **)job->export; |
460 | + nih_local char *start_on = NULL; |
461 | + nih_local char *stop_on = NULL; |
462 | |
463 | - nih_assert (job); |
464 | + nih_assert (class); |
465 | |
466 | nih_debug ("JobClass %p: name='%s', path='%s', task=%d, " |
467 | "respawn=%d, console=%x, deleted=%d, debug=%d", |
468 | - job, job->name, job->path, job->task, |
469 | - job->respawn, job->console, job->deleted, job->debug); |
470 | - |
471 | - nih_debug ("\tstart_on=%p, stop_on=%p, emits=%p, process=%p", |
472 | - job->start_on, job->stop_on, job->emits, job->process); |
473 | + class, class->name, class->path, class->task, |
474 | + class->respawn, class->console, class->deleted, class->debug); |
475 | + |
476 | + if (class->start_on) start_on = event_operator_collapse (class->start_on); |
477 | + if (class->stop_on) stop_on = event_operator_collapse (class->stop_on); |
478 | + |
479 | + nih_debug ("\tstart_on=%p (%s), stop_on=%p (%s), emits=%p, process=%p", |
480 | + class->start_on, start_on ? start_on : "", |
481 | + class->stop_on, stop_on ? stop_on : "", |
482 | + class->emits, class->process); |
483 | + |
484 | + for (int i = 0; i < PROCESS_LAST; i++) { |
485 | + if (class->process[i]) { |
486 | + nih_debug ("Process[%d]=%p: script=%d, cmd='%s'", |
487 | + i, class->process[i], |
488 | + class->process[i]->script, |
489 | + class->process[i]->command); |
490 | + } else { |
491 | + nih_debug ("Process[%d]=%p", |
492 | + i, class->process[i]); |
493 | + } |
494 | + |
495 | + |
496 | + } |
497 | |
498 | nih_debug ("\tauthor='%s', description='%s'", |
499 | - job->author, job->description); |
500 | + class->author, class->description); |
501 | |
502 | - if (env && *env) { |
503 | - nih_debug ("\tenv:"); |
504 | - i = 0; |
505 | - while ( *env ) { |
506 | - nih_debug ("\t\tenv[%d]='%s' (len=%u+1)", |
507 | - i, *env, strlen (*env)); |
508 | - env++; |
509 | - ++i; |
510 | - } |
511 | + if (class->env && *class->env) { |
512 | + nih_local char *env = state_collapse_env ((const char **)class->env); |
513 | + nih_debug ("\tenv:%s", env); |
514 | } else { |
515 | nih_debug ("\tenv: none."); |
516 | } |
517 | |
518 | - |
519 | - if (export && *export) { |
520 | - nih_debug ("\texport:"); |
521 | - i = 0; |
522 | - while ( *export ) { |
523 | - nih_debug ("\t\tenv[%d]='%s' (len=%u+1)", |
524 | - i, *export, strlen (*export)); |
525 | - export++; |
526 | - ++i; |
527 | - } |
528 | - } |
529 | - else { |
530 | + if (class->export && *class->export) { |
531 | + nih_local char *export = state_collapse_env ((const char **)class->export); |
532 | + nih_debug ("\texport:%s", export); |
533 | + } else { |
534 | nih_debug ("\texport: none"); |
535 | } |
536 | + |
537 | + debug_show_jobs (class->instances); |
538 | } |
539 | |
540 | void |
541 | @@ -1269,6 +1275,58 @@ |
542 | } |
543 | |
544 | void |
545 | +debug_show_job (const Job *job) |
546 | +{ |
547 | + nih_local char *env = NULL; |
548 | + nih_local char *start_env = NULL; |
549 | + nih_local char *stop_env = NULL; |
550 | + nih_local char *stop_on = NULL; |
551 | + |
552 | + nih_assert (job); |
553 | + |
554 | + if (job->env) |
555 | + env = state_collapse_env ((const char **)job->env); |
556 | + |
557 | + if (job->start_env) |
558 | + start_env = state_collapse_env ((const char **)job->start_env); |
559 | + |
560 | + if (job->stop_env) |
561 | + stop_env = state_collapse_env ((const char **)job->stop_env); |
562 | + |
563 | + if (job->stop_on) |
564 | + stop_on = event_operator_collapse (job->stop_on); |
565 | + |
566 | + nih_debug ("Job %p: name=%s, class=%p (%s), path=%s, env='%s'" |
567 | + "start_env='%s', stop_env='%s', stop_on='%s', " |
568 | + "goal=%d, state=%d, failed=%d, exit_status=%d", |
569 | + job, |
570 | + job->name ? job->name : "", |
571 | + job->class, job->class->name, |
572 | + job->path, |
573 | + env ? env : "", |
574 | + start_env ? start_env : "", |
575 | + stop_env ? stop_env : "", |
576 | + stop_on ? stop_on : "", |
577 | + job->goal, |
578 | + job->state, |
579 | + job->failed, |
580 | + job->exit_status); |
581 | +} |
582 | + |
583 | +void |
584 | +debug_show_jobs (const NihHash *instances) |
585 | +{ |
586 | + nih_assert (instances); |
587 | + |
588 | + nih_debug ("jobs:"); |
589 | + |
590 | + NIH_HASH_FOREACH (instances, iter) { |
591 | + Job *job = (Job *)iter; |
592 | + debug_show_job (job); |
593 | + } |
594 | +} |
595 | + |
596 | +void |
597 | debug_show_event (const Event *event) |
598 | { |
599 | nih_assert (event); |
600 | @@ -1308,7 +1366,7 @@ |
601 | source, source->path, source->type, source->flag); |
602 | |
603 | nih_debug ("ConfSource %p files (%d):", source, |
604 | - debug_count_hash_entries (source->files)); |
605 | + (int)debug_count_hash_entries (source->files)); |
606 | |
607 | NIH_HASH_FOREACH (source->files, file_iter) { |
608 | ConfFile *file = (ConfFile *)file_iter; |
609 | |
610 | === modified file 'init/conf.h' |
611 | --- init/conf.h 2011-06-06 12:52:08 +0000 |
612 | +++ init/conf.h 2012-11-14 14:49:20 +0000 |
613 | @@ -39,9 +39,9 @@ |
614 | * what they define within themselves. |
615 | **/ |
616 | typedef enum conf_source_type { |
617 | - CONF_FILE, |
618 | - CONF_DIR, |
619 | - CONF_JOB_DIR, |
620 | + CONF_FILE, /* solitary file */ |
621 | + CONF_DIR, /* FIXME: */ |
622 | + CONF_JOB_DIR, /* directory tree of jobs */ |
623 | } ConfSourceType; |
624 | |
625 | |
626 | @@ -133,6 +133,7 @@ |
627 | #ifdef DEBUG |
628 | |
629 | /* used for debugging only */ |
630 | +#include "job.h" |
631 | |
632 | size_t |
633 | debug_count_hash_entries (const NihHash *hash); |
634 | @@ -142,7 +143,7 @@ |
635 | __attribute__ ((unused)); |
636 | |
637 | void |
638 | -debug_show_job_class (const JobClass *job) |
639 | +debug_show_job_class (const JobClass *class) |
640 | __attribute__ ((unused)); |
641 | |
642 | void |
643 | @@ -150,6 +151,13 @@ |
644 | __attribute__ ((unused)); |
645 | |
646 | void |
647 | +debug_show_job (const Job *job) |
648 | + __attribute__ ((unused)); |
649 | + |
650 | +void |
651 | +debug_show_jobs (const NihHash *instances) |
652 | + __attribute__ ((unused)); |
653 | +void |
654 | debug_show_event (const Event *event) |
655 | __attribute__ ((unused)); |
656 | |
657 | |
658 | === modified file 'init/control.c' |
659 | --- init/control.c 2012-03-16 21:02:13 +0000 |
660 | +++ init/control.c 2012-11-14 14:49:20 +0000 |
661 | @@ -54,6 +54,7 @@ |
662 | #include "conf.h" |
663 | #include "control.h" |
664 | #include "errors.h" |
665 | +#include "state.h" |
666 | |
667 | #include "com.ubuntu.Upstart.h" |
668 | |
669 | @@ -62,6 +63,8 @@ |
670 | static void control_disconnected (DBusConnection *conn); |
671 | static void control_register_all (DBusConnection *conn); |
672 | |
673 | +static void control_bus_flush (void); |
674 | + |
675 | /** |
676 | * use_session_bus: |
677 | * |
678 | @@ -188,7 +191,8 @@ |
679 | void |
680 | control_server_close (void) |
681 | { |
682 | - nih_assert (control_server != NULL); |
683 | + if (! control_server) |
684 | + return; |
685 | |
686 | dbus_server_disconnect (control_server); |
687 | dbus_server_unref (control_server); |
688 | @@ -292,7 +296,7 @@ |
689 | * |
690 | * This function is called when the connection to the D-Bus system bus, |
691 | * or a client connection to our D-Bus server, is dropped and our reference |
692 | - * is about to be list. We clear the connection from our current list |
693 | + * is about to be lost. We clear the connection from our current list |
694 | * and drop the control_bus global if relevant. |
695 | **/ |
696 | static void |
697 | @@ -301,6 +305,10 @@ |
698 | nih_assert (conn != NULL); |
699 | |
700 | if (conn == control_bus) { |
701 | + DBusError error; |
702 | + |
703 | + dbus_error_init (&error); |
704 | + |
705 | nih_warn (_("Disconnected from system bus")); |
706 | |
707 | control_bus = NULL; |
708 | @@ -368,6 +376,8 @@ |
709 | nih_assert (message != NULL); |
710 | |
711 | nih_info (_("Reloading configuration")); |
712 | + |
713 | + /* This can only be called after deserialisation */ |
714 | conf_reload (); |
715 | |
716 | return 0; |
717 | @@ -806,3 +816,136 @@ |
718 | |
719 | return 0; |
720 | } |
721 | + |
722 | +/** |
723 | + * control_bus_flush: |
724 | + * |
725 | + * Drain any remaining messages in the D-Bus queue. |
726 | + **/ |
727 | +static void |
728 | +control_bus_flush (void) |
729 | +{ |
730 | + control_init (); |
731 | + |
732 | + if (! control_bus) |
733 | + return; |
734 | + |
735 | + while (dbus_connection_dispatch (control_bus) == DBUS_DISPATCH_DATA_REMAINS) |
736 | + ; |
737 | +} |
738 | + |
739 | +/** |
740 | + * control_prepare_reexec: |
741 | + * |
742 | + * Prepare for a re-exec by allowing the bus connection to be retained |
743 | + * over re-exec and clearing all queued messages. |
744 | + **/ |
745 | +void |
746 | +control_prepare_reexec (void) |
747 | +{ |
748 | + control_init (); |
749 | + |
750 | + /* Necessary to disallow further commands but also to allow the |
751 | + * new instance to open the control server. |
752 | + */ |
753 | + if (control_server) |
754 | + control_server_close (); |
755 | + |
756 | + control_bus_flush (); |
757 | + |
758 | +} |
759 | + |
760 | + |
761 | +/** |
762 | + * control_conn_to_index: |
763 | + * |
764 | + * @event: event. |
765 | + * |
766 | + * Convert a control (DBusConnection) connection to an index number |
767 | + * the list of control connections. |
768 | + * |
769 | + * Returns: connection index, or -1 on error. |
770 | + **/ |
771 | +int |
772 | +control_conn_to_index (const DBusConnection *connection) |
773 | +{ |
774 | + int conn_index = 0; |
775 | + int found = FALSE; |
776 | + |
777 | + nih_assert (connection); |
778 | + |
779 | + NIH_LIST_FOREACH (control_conns, iter) { |
780 | + NihListEntry *entry = (NihListEntry *)iter; |
781 | + DBusConnection *conn = (DBusConnection *)entry->data; |
782 | + |
783 | + if (connection == conn) { |
784 | + found = TRUE; |
785 | + break; |
786 | + } |
787 | + |
788 | + conn_index++; |
789 | + } |
790 | + if (! found) |
791 | + return -1; |
792 | + |
793 | + return conn_index; |
794 | +} |
795 | + |
796 | +/** |
797 | + * control_conn_from_index: |
798 | + * |
799 | + * @conn_index: control connection index number. |
800 | + * |
801 | + * Lookup control connection based on index number. |
802 | + * |
803 | + * Returns: existing connection on success, or NULL if connection |
804 | + * not found. |
805 | + **/ |
806 | +DBusConnection * |
807 | +control_conn_from_index (int conn_index) |
808 | +{ |
809 | + int i = 0; |
810 | + |
811 | + nih_assert (conn_index >= 0); |
812 | + nih_assert (control_conns); |
813 | + |
814 | + NIH_LIST_FOREACH (control_conns, iter) { |
815 | + NihListEntry *entry = (NihListEntry *)iter; |
816 | + DBusConnection *conn = (DBusConnection *)entry->data; |
817 | + |
818 | + if (i == conn_index) |
819 | + return conn; |
820 | + i++; |
821 | + } |
822 | + |
823 | + return NULL; |
824 | +} |
825 | + |
826 | +/** |
827 | + * control_bus_release_name: |
828 | + * |
829 | + * Unregister well-known D-Bus name. |
830 | + * |
831 | + * Returns: 0 on success, -1 on raised error. |
832 | + **/ |
833 | +int |
834 | +control_bus_release_name (void) |
835 | +{ |
836 | + DBusError error; |
837 | + int ret; |
838 | + |
839 | + if (! control_bus) |
840 | + return 0; |
841 | + |
842 | + dbus_error_init (&error); |
843 | + ret = dbus_bus_release_name (control_bus, |
844 | + DBUS_SERVICE_UPSTART, |
845 | + &error); |
846 | + if (ret < 0) { |
847 | + nih_dbus_error_raise (error.name, error.message); |
848 | + dbus_error_free (&error); |
849 | + return -1; |
850 | + } |
851 | + |
852 | + return 0; |
853 | +} |
854 | |
855 | === modified file 'init/control.h' |
856 | --- init/control.h 2011-06-06 17:05:11 +0000 |
857 | +++ init/control.h 2012-11-14 14:49:20 +0000 |
858 | @@ -23,10 +23,13 @@ |
859 | #include <dbus/dbus.h> |
860 | |
861 | #include <nih/macros.h> |
862 | +#include <nih/list.h> |
863 | |
864 | #include <nih-dbus/dbus_connection.h> |
865 | #include <nih-dbus/dbus_message.h> |
866 | |
867 | +#include <json.h> |
868 | + |
869 | /** |
870 | * USE_SESSION_BUS_ENV: |
871 | * |
872 | @@ -90,6 +93,18 @@ |
873 | |
874 | void control_handle_bus_type (void); |
875 | |
876 | +void control_prepare_reexec (void); |
877 | + |
878 | +int control_conn_to_index (const DBusConnection *connection) |
879 | + __attribute__ ((warn_unused_result)); |
880 | + |
881 | +DBusConnection * |
882 | +control_conn_from_index (int conn_index) |
883 | + __attribute__ ((warn_unused_result)); |
884 | + |
885 | +int control_bus_release_name (void) |
886 | + __attribute__ ((warn_unused_result)); |
887 | + |
888 | NIH_END_EXTERN |
889 | |
890 | #endif /* INIT_CONTROL_H */ |
891 | |
892 | === modified file 'init/event.c' |
893 | --- init/event.c 2011-06-06 17:05:11 +0000 |
894 | +++ init/event.c 2012-11-14 14:49:20 +0000 |
895 | @@ -52,6 +52,14 @@ |
896 | static void event_pending_handle_jobs (Event *event); |
897 | static void event_finished (Event *event); |
898 | |
899 | +static const char * event_progress_enum_to_str (EventProgress progress) |
900 | + __attribute__ ((warn_unused_result)); |
901 | + |
902 | +static EventProgress |
903 | +event_progress_str_to_enum (const char *name) |
904 | + __attribute__ ((warn_unused_result)); |
905 | + |
906 | +extern json_object *json_events; |
907 | |
908 | /** |
909 | * events: |
910 | @@ -501,3 +509,326 @@ |
911 | |
912 | nih_free (event); |
913 | } |
914 | + |
915 | +/** |
916 | + * event_serialise: |
917 | + * @event: event to serialise. |
918 | + * |
919 | + * Convert @event into a JSON representation for serialisation. |
920 | + * Caller must free returned value using json_object_put(). |
921 | + * |
922 | + * Note that event->blocking is NOT serialised. Instead, those objects |
923 | + * which event->blocking refers to encode the fact that they are blocked |
924 | + * on the event (index in the JSON) in question. Deserialisation |
925 | + * restores event->blocking via a 2-pass technique. event->blockers is |
926 | + * recorded to allow a double-check. |
927 | + * |
928 | + * Returns: JSON-serialised Event object, or NULL on error. |
929 | + **/ |
930 | +json_object * |
931 | +event_serialise (const Event *event) |
932 | +{ |
933 | + json_object *json; |
934 | + int session_index; |
935 | + |
936 | + nih_assert (event); |
937 | + nih_assert (event->name); |
938 | + |
939 | + json = json_object_new_object (); |
940 | + if (! json) |
941 | + return NULL; |
942 | + |
943 | + session_index = session_get_index (event->session); |
944 | + if (session_index < 0) |
945 | + goto error; |
946 | + |
947 | + if (! state_set_json_int_var (json, "session", session_index)) |
948 | + goto error; |
949 | + |
950 | + if (! state_set_json_string_var_from_obj (json, event, name)) |
951 | + goto error; |
952 | + |
953 | + if (event->env) { |
954 | + if (! state_set_json_str_array_from_obj (json, event, env)) |
955 | + goto error; |
956 | + } |
957 | + |
958 | + if (! state_set_json_int_var_from_obj (json, event, fd)) |
959 | + goto error; |
960 | + |
961 | + if (! state_set_json_enum_var (json, |
962 | + event_progress_enum_to_str, |
963 | + "progress", event->progress)) |
964 | + goto error; |
965 | + |
966 | + if (! state_set_json_int_var_from_obj (json, event, failed)) |
967 | + goto error; |
968 | + |
969 | + if (! state_set_json_int_var_from_obj (json, event, blockers)) |
970 | + goto error; |
971 | + |
972 | + if (! NIH_LIST_EMPTY (&event->blocking)) { |
973 | + json_object *json_blocking; |
974 | + |
975 | + json_blocking = state_serialise_blocking (&event->blocking); |
976 | + if (! json_blocking) |
977 | + goto error; |
978 | + |
979 | + json_object_object_add (json, "blocking", json_blocking); |
980 | + } |
981 | + |
982 | + return json; |
983 | + |
984 | +error: |
985 | + json_object_put (json); |
986 | + return NULL; |
987 | +} |
988 | + |
989 | +/** |
990 | + * event_serialise_all: |
991 | + * |
992 | + * Convert existing Event objects to JSON representation. |
993 | + * |
994 | + * Returns: JSON object containing array of Events, or NULL on error. |
995 | + **/ |
996 | +json_object * |
997 | +event_serialise_all (void) |
998 | +{ |
999 | + json_object *json; |
1000 | + |
1001 | + event_init (); |
1002 | + |
1003 | + json = json_object_new_array (); |
1004 | + if (! json) |
1005 | + return NULL; |
1006 | + |
1007 | + NIH_LIST_FOREACH (events, iter) { |
1008 | + Event *event = (Event *)iter; |
1009 | + json_object *json_event; |
1010 | + |
1011 | + json_event = event_serialise (event); |
1012 | + |
1013 | + if (! json_event) |
1014 | + goto error; |
1015 | + |
1016 | + json_object_array_add (json, json_event); |
1017 | + } |
1018 | + |
1019 | + return json; |
1020 | + |
1021 | +error: |
1022 | + json_object_put (json); |
1023 | + return NULL; |
1024 | +} |
1025 | + |
1026 | +/** |
1027 | + * event_deserialise: |
1028 | + * @json: JSON-serialised Event object to deserialise. |
1029 | + * |
1030 | + * Convert @json into an Event object. |
1031 | + * |
1032 | + * Returns: Event object, or NULL on error. |
1033 | + **/ |
1034 | +Event * |
1035 | +event_deserialise (json_object *json) |
1036 | +{ |
1037 | + json_object *json_env; |
1038 | + Event *event = NULL; |
1039 | + nih_local char *name = NULL; |
1040 | + char **env = NULL; |
1041 | + int session_index = -1; |
1042 | + |
1043 | + nih_assert (json); |
1044 | + |
1045 | + if (! state_check_json_type (json, object)) |
1046 | + return NULL; |
1047 | + |
1048 | + if (! state_get_json_string_var_strict (json, "name", NULL, name)) |
1049 | + goto error; |
1050 | + |
1051 | + if (json_object_object_get (json, "env")) { |
1052 | + if (! state_get_json_var_full (json, "env", array, json_env)) |
1053 | + goto error; |
1054 | + |
1055 | + if (! state_deserialise_str_array (NULL, json_env, &env)) |
1056 | + goto error; |
1057 | + } |
1058 | + |
1059 | + event = event_new (NULL, name, env); |
1060 | + if (! event) |
1061 | + return NULL; |
1062 | + |
1063 | + if (! state_get_json_int_var_to_obj (json, event, fd)) |
1064 | + goto error; |
1065 | + |
1066 | + if (! state_get_json_int_var (json, "session", session_index)) |
1067 | + goto error; |
1068 | + |
1069 | + /* can't check return value here (as all values are legitimate) */ |
1070 | + event->session = session_from_index (session_index); |
1071 | + |
1072 | + if (! state_get_json_enum_var (json, |
1073 | + event_progress_str_to_enum, |
1074 | + "progress", event->progress)) |
1075 | + goto error; |
1076 | + |
1077 | + if (! state_set_json_int_var_from_obj (json, event, failed)) |
1078 | + goto error; |
1079 | + |
1080 | + return event; |
1081 | + |
1082 | +error: |
1083 | + nih_free (event); |
1084 | + return NULL; |
1085 | +} |
1086 | + |
1087 | +/** |
1088 | + * event_deserialise_all: |
1089 | + * |
1090 | + * @json: root of JSON-serialised state. |
1091 | + * |
1092 | + * Convert JSON representation of events back into Event objects. |
1093 | + * |
1094 | + * Returns: 0 on success, -1 on error. |
1095 | + **/ |
1096 | +int |
1097 | +event_deserialise_all (json_object *json) |
1098 | +{ |
1099 | + Event *event; |
1100 | + |
1101 | + nih_assert (json); |
1102 | + |
1103 | + event_init (); |
1104 | + |
1105 | + nih_assert (NIH_LIST_EMPTY (events)); |
1106 | + json_events = json_object_object_get (json, "events"); |
1107 | + |
1108 | + if (! json_events) |
1109 | + goto error; |
1110 | + |
1111 | + if (! state_check_json_type (json_events, array)) |
1112 | + goto error; |
1113 | + |
1114 | + for (int i = 0; i < json_object_array_length (json_events); i++) { |
1115 | + json_object *json_event; |
1116 | + |
1117 | + json_event = json_object_array_get_idx (json_events, i); |
1118 | + if (! json_event) |
1119 | + goto error; |
1120 | + |
1121 | + if (! state_check_json_type (json_event, object)) |
1122 | + goto error; |
1123 | + |
1124 | + event = event_deserialise (json_event); |
1125 | + if (! event) |
1126 | + goto error; |
1127 | + } |
1128 | + |
1129 | + return 0; |
1130 | + |
1131 | +error: |
1132 | + return -1; |
1133 | +} |
1134 | + |
1135 | +/** |
1136 | + * event_progress_enum_to_str: |
1137 | + * |
1138 | + * @progress: event progress. |
1139 | + * |
1140 | + * Convert EventProgress to a string representation. |
1141 | + * |
1142 | + * Returns: string representation of @progress, or NULL if not known. |
1143 | + **/ |
1144 | +static const char * |
1145 | +event_progress_enum_to_str (EventProgress progress) |
1146 | +{ |
1147 | + state_enum_to_str (EVENT_PENDING, progress); |
1148 | + state_enum_to_str (EVENT_HANDLING, progress); |
1149 | + state_enum_to_str (EVENT_FINISHED, progress); |
1150 | + |
1151 | + return NULL; |
1152 | +} |
1153 | + |
1154 | +/** |
1155 | + * event_progress_str_to_enum: |
1156 | + * |
1157 | + * @: name of EventOperator value. |
1158 | + * |
1159 | + * Convert string representation of EventProgress into a |
1160 | + * real EventProgress value. |
1161 | + * |
1162 | + * Returns: EventProgress representing @progress, or -1 if not known. |
1163 | + **/ |
1164 | +static EventProgress |
1165 | +event_progress_str_to_enum (const char *progress) |
1166 | +{ |
1167 | + state_str_to_enum (EVENT_PENDING, progress); |
1168 | + state_str_to_enum (EVENT_HANDLING, progress); |
1169 | + state_str_to_enum (EVENT_FINISHED, progress); |
1170 | + |
1171 | + return -1; |
1172 | +} |
1173 | + |
1174 | +/** |
1175 | + * event_to_index: |
1176 | + * |
1177 | + * @event: event. |
1178 | + * |
1179 | + * Convert an Event to an index number within |
1180 | + * the list of events. |
1181 | + * |
1182 | + * Returns: event index, or -1 on error. |
1183 | + **/ |
1184 | +int |
1185 | +event_to_index (const Event *event) |
1186 | +{ |
1187 | + int event_index = 0; |
1188 | + int found = FALSE; |
1189 | + |
1190 | + nih_assert (event); |
1191 | + event_init (); |
1192 | + |
1193 | + NIH_LIST_FOREACH (events, iter) { |
1194 | + Event *tmp = (Event *)iter; |
1195 | + |
1196 | + if (tmp == event) { |
1197 | + found = TRUE; |
1198 | + break; |
1199 | + } |
1200 | + |
1201 | + event_index++; |
1202 | + } |
1203 | + |
1204 | + if (! found) |
1205 | + return -1; |
1206 | + |
1207 | + return event_index; |
1208 | +} |
1209 | + |
1210 | +/** |
1211 | + * event_from_index: |
1212 | + * |
1213 | + * @event_index: event index number. |
1214 | + * |
1215 | + * Lookup Event based on index number. |
1216 | + * |
1217 | + * Returns: existing Event on success, or NULL if event not found. |
1218 | + **/ |
1219 | +Event * |
1220 | +event_from_index (int event_index) |
1221 | +{ |
1222 | + int i = 0; |
1223 | + |
1224 | + nih_assert (event_index >= 0); |
1225 | + event_init (); |
1226 | + |
1227 | + NIH_LIST_FOREACH (events, iter) { |
1228 | + Event *event = (Event *)iter; |
1229 | + |
1230 | + if (i == event_index) |
1231 | + return event; |
1232 | + i++; |
1233 | + } |
1234 | + |
1235 | + return NULL; |
1236 | +} |
1237 | |
1238 | === modified file 'init/event.h' |
1239 | --- init/event.h 2011-06-06 17:05:11 +0000 |
1240 | +++ init/event.h 2012-11-14 14:49:20 +0000 |
1241 | @@ -24,7 +24,9 @@ |
1242 | #include <nih/list.h> |
1243 | |
1244 | #include "session.h" |
1245 | +#include "state.h" |
1246 | |
1247 | +#include <json.h> |
1248 | |
1249 | /** |
1250 | * EventProgress: |
1251 | @@ -45,6 +47,8 @@ |
1252 | * @session: session the event is attached to, |
1253 | * @name: string name of the event, |
1254 | * @env: NULL-terminated array of environment variables, |
1255 | + * @fd: open file descriptor associated with a particular |
1256 | + * socket-bridge socket (see socket-event(8)), |
1257 | * @progress: progress of event, |
1258 | * @failed: whether this event has failed, |
1259 | * @blockers: number of blockers for finishing, |
1260 | @@ -93,6 +97,24 @@ |
1261 | |
1262 | void event_poll (void); |
1263 | |
1264 | +json_object *event_serialise (const Event *event) |
1265 | + __attribute__ ((malloc, warn_unused_result)); |
1266 | + |
1267 | +Event *event_deserialise (json_object *json) |
1268 | + __attribute__ ((malloc, warn_unused_result)); |
1269 | + |
1270 | +json_object * event_serialise_all (void) |
1271 | + __attribute__ ((malloc, warn_unused_result)); |
1272 | + |
1273 | +int event_deserialise_all (json_object *json) |
1274 | + __attribute__ ((warn_unused_result)); |
1275 | + |
1276 | +int event_to_index (const Event *event) |
1277 | + __attribute__ ((warn_unused_result)); |
1278 | + |
1279 | +Event * event_from_index (int event_index) |
1280 | + __attribute__ ((warn_unused_result)); |
1281 | + |
1282 | NIH_END_EXTERN |
1283 | |
1284 | #endif /* INIT_EVENT_H */ |
1285 | |
1286 | === modified file 'init/event_operator.c' |
1287 | --- init/event_operator.c 2010-12-10 07:18:34 +0000 |
1288 | +++ init/event_operator.c 2012-11-14 14:49:20 +0000 |
1289 | @@ -552,14 +552,31 @@ |
1290 | return *env; |
1291 | } |
1292 | |
1293 | +/** |
1294 | + * event_operator_fds: |
1295 | + * @root: operator tree to update, |
1296 | + * @parent: parent object for new array, |
1297 | + * @fds: output location for array of ints |
1298 | + * @num_fds: number of elements in @fds, |
1299 | + * @env: NULL-terminated array of environment variables to add to, |
1300 | + * @len: length of @env, |
1301 | + * @key: key of variable to contain event names. |
1302 | + * |
1303 | + * Iterate over tree rooted at @root adding all file descriptor values found |
1304 | + * to the dynamically allocated @fds array. In addition, all file |
1305 | + * descriptors found are also added to @env will contain a new entry with key @key |
1306 | + * whose value is a space-separated list of file descriptor numbers. |
1307 | + * |
1308 | + * Returns: 1 on success, NULL on failure. |
1309 | + **/ |
1310 | int * |
1311 | -event_operator_fds (EventOperator *root, |
1312 | - const void *parent, |
1313 | - int **fds, |
1314 | - size_t *num_fds, |
1315 | - char ***env, |
1316 | - size_t *len, |
1317 | - const char *key) |
1318 | +event_operator_fds (EventOperator *root, |
1319 | + const void *parent, |
1320 | + int **fds, |
1321 | + size_t *num_fds, |
1322 | + char ***env, |
1323 | + size_t *len, |
1324 | + const char *key) |
1325 | { |
1326 | nih_local char *evlist = NULL; |
1327 | |
1328 | @@ -696,3 +713,154 @@ |
1329 | } |
1330 | } |
1331 | } |
1332 | + |
1333 | +/** |
1334 | + * event_operator_collapse: |
1335 | + * |
1336 | + * @condition: start on/stop on condition. |
1337 | + * |
1338 | + * Collapsed condition will be fully bracketed. Note that as such it may |
1339 | + * not be lexicographically identical to the original expression that |
1340 | + * resulted in @condition, but it will be logically identical. |
1341 | + * |
1342 | + * The condition is reconstructed from the EventOperator tree by using |
1343 | + * a post-order traversal (since this allows the tree to be traversed |
1344 | + * bottom-to-top). Leaf nodes (EVENT_MATCH) are ignored when visited, |
1345 | + * allowing non-leaf nodes (EVENT_AND and EVENT_OR) to simply grab the |
1346 | + * value of their children, construct a bracketed expression and add it |
1347 | + * to a stack. If a child is a leaf node, the value can be read |
1348 | + * directly. If a child is a non-leaf node, the value is obtained by |
1349 | + * popping the stack before adding the new value back onto the stack. |
1350 | + * When finally the root node is visited, the final expression can be |
1351 | + * removed from the stack and returned. A single-node tree (comprising a |
1352 | + * lone EVENT_MATCH at the root) is special-cased. |
1353 | + * |
1354 | + * Returns: newly-allocated flattened string representing @condition |
1355 | + * on success, or NULL on error. |
1356 | + **/ |
1357 | +char * |
1358 | +event_operator_collapse (EventOperator *condition) |
1359 | +{ |
1360 | + nih_local NihList *stack = NULL; |
1361 | + NihListEntry *latest = NULL; |
1362 | + NihTree *root; |
1363 | + |
1364 | + nih_assert (condition); |
1365 | + |
1366 | + root = &condition->node; |
1367 | + |
1368 | + stack = NIH_MUST (nih_list_new (NULL)); |
1369 | + |
1370 | + NIH_TREE_FOREACH_POST (root, iter) { |
1371 | + EventOperator *oper = (EventOperator *)iter; |
1372 | + EventOperator *left; |
1373 | + EventOperator *right; |
1374 | + NihListEntry *expr; |
1375 | + NihTree *tree; |
1376 | + nih_local char *left_expr = NULL; |
1377 | + nih_local char *right_expr = NULL; |
1378 | + |
1379 | + tree = &oper->node; |
1380 | + |
1381 | + left = (EventOperator *)tree->left; |
1382 | + right = (EventOperator *)tree->right; |
1383 | + |
1384 | + if (oper->type == EVENT_MATCH) { |
1385 | + /* Entire expression comprises a single event, |
1386 | + * so push it and leave. |
1387 | + */ |
1388 | + if (tree == root) { |
1389 | + nih_local char *env = NULL; |
1390 | + |
1391 | + if (oper->env) |
1392 | + env = NIH_MUST (state_collapse_env ((const char **)oper->env)); |
1393 | + |
1394 | + expr = NIH_MUST (nih_list_entry_new (stack)); |
1395 | + expr->str = NIH_MUST (nih_sprintf (expr, "%s%s%s", |
1396 | + oper->name, |
1397 | + env ? " " : "", |
1398 | + env ? env : "")); |
1399 | + nih_list_add_after (stack, &expr->entry); |
1400 | + break; |
1401 | + } |
1402 | + else { |
1403 | + /* We build the expression from visiting the logical |
1404 | + * operators (and their children) only. |
1405 | + */ |
1406 | + continue; |
1407 | + } |
1408 | + } |
1409 | + |
1410 | + /* oper cannot now be a leaf node, so must have children */ |
1411 | + nih_assert (left); |
1412 | + nih_assert (right); |
1413 | + |
1414 | + expr = NIH_MUST (nih_list_entry_new (stack)); |
1415 | + |
1416 | + /* If a child is an EVENT_MATCH, expand its event |
1417 | + * details and push onto the stack. |
1418 | + * If a child is not an EVENT_MATCH, to obtains it |
1419 | + * value, pop the stack. |
1420 | + * |
1421 | + * Having obtained the child values, construct a new |
1422 | + * bracketed expression and push onto the stack. |
1423 | + * |
1424 | + * Note that we must consider the right child first. |
1425 | + * This is because since the tree is traversed |
1426 | + * left-child first, any value pushed onto the stack by |
1427 | + * a right child is at the top so must be removed before |
1428 | + * any left child value. Failure to do this results in |
1429 | + * tree reflection which although logically equivalent |
1430 | + * to the original could confuse as the resultant |
1431 | + * expression will look rather different. |
1432 | + */ |
1433 | + if (right->type != EVENT_MATCH) { |
1434 | + nih_assert (! NIH_LIST_EMPTY (stack)); |
1435 | + |
1436 | + latest = (NihListEntry *)nih_list_remove (stack->next); |
1437 | + right_expr = NIH_MUST (nih_strdup (NULL, latest->str)); |
1438 | + } else { |
1439 | + nih_local char *env = NULL; |
1440 | + |
1441 | + if (right->env) |
1442 | + env = NIH_MUST (state_collapse_env ((const char **)right->env)); |
1443 | + |
1444 | + right_expr = NIH_MUST (nih_sprintf (NULL, "%s%s%s", |
1445 | + right->name, |
1446 | + env ? " " : "", |
1447 | + env ? env : "")); |
1448 | + } |
1449 | + |
1450 | + if (left->type != EVENT_MATCH) { |
1451 | + nih_assert (! NIH_LIST_EMPTY (stack)); |
1452 | + |
1453 | + latest = (NihListEntry *)nih_list_remove (stack->next); |
1454 | + left_expr = NIH_MUST (nih_strdup (NULL, latest->str)); |
1455 | + } else { |
1456 | + nih_local char *env = NULL; |
1457 | + |
1458 | + if (left->env) |
1459 | + env = NIH_MUST (state_collapse_env ((const char **)left->env)); |
1460 | + |
1461 | + left_expr = NIH_MUST (nih_sprintf (NULL, "%s%s%s", |
1462 | + left->name, |
1463 | + env ? " " : "", |
1464 | + env ? env : "")); |
1465 | + } |
1466 | + |
1467 | + expr->str = NIH_MUST (nih_sprintf (expr, "(%s %s %s)", |
1468 | + left_expr, |
1469 | + oper->type == EVENT_OR ? "or" : "and", |
1470 | + right_expr)); |
1471 | + |
1472 | + nih_list_add_after (stack, &expr->entry); |
1473 | + } |
1474 | + |
1475 | + nih_assert (! NIH_LIST_EMPTY (stack)); |
1476 | + |
1477 | + latest = (NihListEntry *)nih_list_remove (stack->next); |
1478 | + |
1479 | + nih_assert (NIH_LIST_EMPTY (stack)); |
1480 | + |
1481 | + return NIH_MUST (nih_strdup (NULL, latest->str)); |
1482 | +} |
1483 | |
1484 | === modified file 'init/event_operator.h' |
1485 | --- init/event_operator.h 2010-12-10 07:18:34 +0000 |
1486 | +++ init/event_operator.h 2012-11-14 14:49:20 +0000 |
1487 | @@ -96,10 +96,10 @@ |
1488 | const void *parent, size_t *len, |
1489 | const char *key); |
1490 | int * |
1491 | -event_operator_fds (EventOperator *root, |
1492 | - const void *parent, |
1493 | - int **fds, |
1494 | - size_t *num_fds, |
1495 | +event_operator_fds (EventOperator *root, |
1496 | + const void *parent, |
1497 | + int **fds, |
1498 | + size_t *num_fds, |
1499 | char ***env, |
1500 | size_t *len, |
1501 | const char *key); |
1502 | @@ -108,6 +108,9 @@ |
1503 | |
1504 | void event_operator_reset (EventOperator *root); |
1505 | |
1506 | +char *event_operator_collapse (EventOperator *condition) |
1507 | + __attribute__ ((malloc, warn_unused_result)); |
1508 | + |
1509 | NIH_END_EXTERN |
1510 | |
1511 | #endif /* INIT_EVENT_OPERATOR_H */ |
1512 | |
1513 | === modified file 'init/job.c' |
1514 | --- init/job.c 2012-03-07 12:13:28 +0000 |
1515 | +++ init/job.c 2012-11-14 14:49:20 +0000 |
1516 | @@ -23,7 +23,6 @@ |
1517 | # include <config.h> |
1518 | #endif /* HAVE_CONFIG_H */ |
1519 | |
1520 | - |
1521 | #include <sys/types.h> |
1522 | |
1523 | #include <errno.h> |
1524 | @@ -55,10 +54,43 @@ |
1525 | #include "event_operator.h" |
1526 | #include "blocked.h" |
1527 | #include "control.h" |
1528 | +#include "parse_job.h" |
1529 | +#include "state.h" |
1530 | |
1531 | #include "com.ubuntu.Upstart.Job.h" |
1532 | #include "com.ubuntu.Upstart.Instance.h" |
1533 | |
1534 | +/* Prototypes for static functions */ |
1535 | +static const char * |
1536 | +job_goal_enum_to_str (JobGoal goal) |
1537 | + __attribute__ ((warn_unused_result)); |
1538 | + |
1539 | +static JobGoal job_goal_str_to_enum (const char *goal) |
1540 | + __attribute__ ((warn_unused_result)); |
1541 | + |
1542 | +static const char * |
1543 | +job_state_enum_to_str (JobState state) |
1544 | + __attribute__ ((warn_unused_result)); |
1545 | + |
1546 | +static JobState |
1547 | +job_state_str_to_enum (const char *state) |
1548 | + __attribute__ ((warn_unused_result)); |
1549 | + |
1550 | +static const char * |
1551 | +job_trace_state_enum_to_str (TraceState state) |
1552 | + __attribute__ ((warn_unused_result)); |
1553 | + |
1554 | +static TraceState |
1555 | +job_trace_state_str_to_enum (const char *state) |
1556 | + __attribute__ ((warn_unused_result)); |
1557 | + |
1558 | +static json_object * |
1559 | +job_serialise_kill_timer (NihTimer *timer) |
1560 | + __attribute__ ((malloc, warn_unused_result)); |
1561 | + |
1562 | +static NihTimer * |
1563 | +job_deserialise_kill_timer (json_object *json) |
1564 | + __attribute__ ((malloc, warn_unused_result)); |
1565 | |
1566 | /** |
1567 | * job_new: |
1568 | @@ -119,6 +151,7 @@ |
1569 | job->stop_env = NULL; |
1570 | |
1571 | job->stop_on = NULL; |
1572 | + |
1573 | if (class->stop_on) { |
1574 | job->stop_on = event_operator_copy (job, class->stop_on); |
1575 | if (! job->stop_on) |
1576 | @@ -152,10 +185,10 @@ |
1577 | nih_list_init (&job->blocking); |
1578 | |
1579 | job->kill_timer = NULL; |
1580 | - job->kill_process = -1; |
1581 | + job->kill_process = PROCESS_INVALID; |
1582 | |
1583 | job->failed = FALSE; |
1584 | - job->failed_process = -1; |
1585 | + job->failed_process = PROCESS_INVALID; |
1586 | job->exit_status = 0; |
1587 | |
1588 | job->respawn_time = 0; |
1589 | @@ -262,7 +295,7 @@ |
1590 | * will finish naturally, so all we need do is change the goal and |
1591 | * we'll change direction through the state machine at that point. |
1592 | * |
1593 | - * The exceptions are the natural rest sates of waiting and a |
1594 | + * The exceptions are the natural rest states of waiting and a |
1595 | * running process; these need induction to get them moving. |
1596 | */ |
1597 | switch (goal) { |
1598 | @@ -358,7 +391,7 @@ |
1599 | |
1600 | /* Clear any old failed information */ |
1601 | job->failed = FALSE; |
1602 | - job->failed_process = -1; |
1603 | + job->failed_process = PROCESS_INVALID; |
1604 | job->exit_status = 0; |
1605 | |
1606 | job->blocker = job_emit_event (job); |
1607 | @@ -882,7 +915,7 @@ |
1608 | * if it was a respawn failure, we use the special "respawn" |
1609 | * argument instead of the process name, |
1610 | */ |
1611 | - if ((job->failed_process != (ProcessType)-1) |
1612 | + if ((job->failed_process != PROCESS_INVALID) |
1613 | && (job->exit_status != -1)) { |
1614 | NIH_MUST (environ_set (&env, NULL, &len, TRUE, |
1615 | "PROCESS=%s", |
1616 | @@ -907,7 +940,7 @@ |
1617 | NIH_MUST (environ_set (&env, NULL, &len, TRUE, |
1618 | "EXIT_STATUS=%d", job->exit_status)); |
1619 | } |
1620 | - } else if (job->failed_process != (ProcessType)-1) { |
1621 | + } else if (job->failed_process != PROCESS_INVALID) { |
1622 | NIH_MUST (environ_set (&env, NULL, &len, TRUE, |
1623 | "PROCESS=%s", |
1624 | process_name (job->failed_process))); |
1625 | @@ -1492,3 +1525,689 @@ |
1626 | |
1627 | return 0; |
1628 | } |
1629 | + |
1630 | +/** |
1631 | + * job_serialise: |
1632 | + * @job: job serialise. |
1633 | + * |
1634 | + * Convert @job into a JSON representation for serialisation. |
1635 | + * Caller must free returned value using json_object_put(). |
1636 | + * |
1637 | + * Note that the 'class' element is not encoded - it is assumed the |
1638 | + * caller will encode the returned JSON Job object as a child of the |
1639 | + * parent JSON-encoded JobClass object so a reference is not required. |
1640 | + * |
1641 | + * Returns: JSON-serialised Job object, or NULL on error. |
1642 | + **/ |
1643 | +json_object * |
1644 | +job_serialise (const Job *job) |
1645 | +{ |
1646 | + json_object *json; |
1647 | + json_object *json_pid; |
1648 | + json_object *json_fds; |
1649 | + json_object *json_logs; |
1650 | + nih_local char *stop_on = NULL; |
1651 | + |
1652 | + nih_assert (job); |
1653 | + |
1654 | + json = json_object_new_object (); |
1655 | + if (! json) |
1656 | + return NULL; |
1657 | + |
1658 | + if (! state_set_json_string_var_from_obj (json, job, name)) |
1659 | + goto error; |
1660 | + |
1661 | + if (! state_set_json_string_var_from_obj (json, job, path)) |
1662 | + goto error; |
1663 | + |
1664 | + if (! state_set_json_enum_var (json, |
1665 | + job_goal_enum_to_str, |
1666 | + "goal", job->goal)) |
1667 | + goto error; |
1668 | + |
1669 | + if (! state_set_json_enum_var (json, |
1670 | + job_state_enum_to_str, |
1671 | + "state", job->state)) |
1672 | + goto error; |
1673 | + |
1674 | + if (! state_set_json_str_array_from_obj (json, job, env)) |
1675 | + goto error; |
1676 | + |
1677 | + if (! state_set_json_str_array_from_obj (json, job, start_env)) |
1678 | + goto error; |
1679 | + |
1680 | + if (! state_set_json_str_array_from_obj (json, job, stop_env)) |
1681 | + goto error; |
1682 | + |
1683 | + if (job->stop_on) { |
1684 | + stop_on = event_operator_collapse (job->stop_on); |
1685 | + if (! stop_on) |
1686 | + goto error; |
1687 | + |
1688 | + if (! state_set_json_string_var (json, "stop_on", stop_on)) |
1689 | + goto error; |
1690 | + } |
1691 | + |
1692 | + json_fds = state_serialise_int_array (int, job->fds, job->num_fds); |
1693 | + if (! json_fds) |
1694 | + goto error; |
1695 | + |
1696 | + json_object_object_add (json, "fds", json_fds); |
1697 | + |
1698 | + json_pid = state_serialise_int_array (pid_t, job->pid, |
1699 | + PROCESS_LAST); |
1700 | + if (! json_pid) |
1701 | + goto error; |
1702 | + |
1703 | + json_object_object_add (json, "pid", json_pid); |
1704 | + |
1705 | + /* Encode the blocking event as an index number which represents |
1706 | + * the event's position in the JSON events array. |
1707 | + */ |
1708 | + if (job->blocker) { |
1709 | + int event_index; |
1710 | + |
1711 | + event_index = event_to_index (job->blocker); |
1712 | + if (event_index < 0) |
1713 | + goto error; |
1714 | + |
1715 | + /* For consistency, it would be preferable to encode the |
1716 | + * event name, but the index is actually better since it is |
1717 | + * simple and unambiguous - encoding the name would also require |
1718 | + * us to encode the env to make the event unique. |
1719 | + */ |
1720 | + if (! state_set_json_int_var (json, "blocker", event_index)) |
1721 | + goto error; |
1722 | + |
1723 | + } |
1724 | + |
1725 | + if (! NIH_LIST_EMPTY (&job->blocking)) { |
1726 | + json_object *json_blocking; |
1727 | + |
1728 | + json_blocking = state_serialise_blocking (&job->blocking); |
1729 | + if (! json_blocking) |
1730 | + goto error; |
1731 | + |
1732 | + json_object_object_add (json, "blocking", json_blocking); |
1733 | + } |
1734 | + |
1735 | + /* conditionally encode kill timer */ |
1736 | + if (job->kill_timer) { |
1737 | + json_object *kill_timer; |
1738 | + |
1739 | + kill_timer = job_serialise_kill_timer (job->kill_timer); |
1740 | + |
1741 | + if (! kill_timer) |
1742 | + goto error; |
1743 | + |
1744 | + json_object_object_add (json, "kill_timer", kill_timer); |
1745 | + } |
1746 | + |
1747 | + if (! state_set_json_enum_var (json, |
1748 | + process_type_enum_to_str, |
1749 | + "kill_process", job->kill_process)) |
1750 | + goto error; |
1751 | + |
1752 | + if (! state_set_json_int_var_from_obj (json, job, failed)) |
1753 | + goto error; |
1754 | + |
1755 | + if (! state_set_json_enum_var (json, |
1756 | + process_type_enum_to_str, |
1757 | + "failed_process", job->failed_process)) |
1758 | + goto error; |
1759 | + |
1760 | + if (! state_set_json_int_var_from_obj (json, job, exit_status)) |
1761 | + goto error; |
1762 | + |
1763 | + if (! state_set_json_int_var_from_obj (json, job, respawn_time)) |
1764 | + goto error; |
1765 | + |
1766 | + if (! state_set_json_int_var_from_obj (json, job, respawn_count)) |
1767 | + goto error; |
1768 | + |
1769 | + if (! state_set_json_int_var_from_obj (json, job, trace_forks)) |
1770 | + goto error; |
1771 | + |
1772 | + if (! state_set_json_enum_var (json, |
1773 | + job_trace_state_enum_to_str, |
1774 | + "trace_state", job->trace_state)) |
1775 | + goto error; |
1776 | + |
1777 | + json_logs = json_object_new_array (); |
1778 | + |
1779 | + if (! json_logs) |
1780 | + return json; |
1781 | + |
1782 | + for (int process = 0; process < PROCESS_LAST; process++) { |
1783 | + json_object *json_log; |
1784 | + |
1785 | + json_log = log_serialise (job->log[process]); |
1786 | + if (! json_log) |
1787 | + goto error; |
1788 | + |
1789 | + if (json_object_array_add (json_logs, json_log) < 0) |
1790 | + goto error; |
1791 | + } |
1792 | + |
1793 | + json_object_object_add (json, "log", json_logs); |
1794 | + |
1795 | + return json; |
1796 | + |
1797 | +error: |
1798 | + json_object_put (json); |
1799 | + return NULL; |
1800 | +} |
1801 | + |
1802 | +/** |
1803 | + * job_serialise_all: |
1804 | + * |
1805 | + * Convert existing Session objects to JSON representation. |
1806 | + * |
1807 | + * Returns: JSON object containing array of Job objects, or NULL on error. |
1808 | + **/ |
1809 | +json_object * |
1810 | +job_serialise_all (const NihHash *jobs) |
1811 | +{ |
1812 | + int count = 0; |
1813 | + json_object *json; |
1814 | + |
1815 | + nih_assert (jobs); |
1816 | + |
1817 | + json = json_object_new_array (); |
1818 | + if (! json) |
1819 | + return NULL; |
1820 | + |
1821 | + NIH_HASH_FOREACH (jobs, iter) { |
1822 | + json_object *json_job; |
1823 | + Job *job = (Job *)iter; |
1824 | + |
1825 | + count++; |
1826 | + json_job = job_serialise (job); |
1827 | + |
1828 | + if (! json_job) |
1829 | + goto error; |
1830 | + |
1831 | + json_object_array_add (json, json_job); |
1832 | + } |
1833 | + |
1834 | + /* Raise an error to avoid serialising job classes with |
1835 | + * no associated jobs. |
1836 | + */ |
1837 | + if (! count) |
1838 | + goto error; |
1839 | + |
1840 | + return json; |
1841 | + |
1842 | +error: |
1843 | + json_object_put (json); |
1844 | + return NULL; |
1845 | +} |
1846 | + |
1847 | +/** |
1848 | + * job_deserialise: |
1849 | + * @parent: job class for JSON-encoded jobs, |
1850 | + * @json: JSON-serialised Job object to deserialise. |
1851 | + * |
1852 | + * XXX: All events must have been deserialised prior to this function |
1853 | + * XXX: being called. |
1854 | + * |
1855 | + * Returns: Job object, or NULL on error. |
1856 | + **/ |
1857 | +Job * |
1858 | +job_deserialise (JobClass *parent, json_object *json) |
1859 | +{ |
1860 | + nih_local char *name = NULL; |
1861 | + Job *job = NULL; |
1862 | + json_object *json_kill_timer; |
1863 | + json_object *blocker; |
1864 | + json_object *json_fds; |
1865 | + json_object *json_pid; |
1866 | + json_object *json_logs; |
1867 | + size_t len; |
1868 | + int ret; |
1869 | + |
1870 | + nih_assert (parent); |
1871 | + nih_assert (json); |
1872 | + |
1873 | + if (! state_check_json_type (json, object)) |
1874 | + return NULL; |
1875 | + |
1876 | + if (! state_get_json_string_var_strict (json, "name", NULL, name)) |
1877 | + goto error; |
1878 | + |
1879 | + job = NIH_MUST (job_new (parent, name)); |
1880 | + |
1881 | + if (! job) |
1882 | + return NULL; |
1883 | + |
1884 | + if (! state_get_json_string_var_to_obj_strict (json, job, path)) |
1885 | + goto error; |
1886 | + |
1887 | + if (! state_get_json_enum_var (json, |
1888 | + job_goal_str_to_enum, |
1889 | + "goal", job->goal)) |
1890 | + goto error; |
1891 | + |
1892 | + if (! state_get_json_enum_var (json, |
1893 | + job_state_str_to_enum, |
1894 | + "state", job->state)) |
1895 | + goto error; |
1896 | + |
1897 | + if (! state_get_json_env_array_to_obj (json, job, env)) |
1898 | + goto error; |
1899 | + |
1900 | + if (! state_get_json_env_array_to_obj (json, job, start_env)) |
1901 | + goto error; |
1902 | + |
1903 | + if (! state_get_json_env_array_to_obj (json, job, stop_env)) |
1904 | + goto error; |
1905 | + |
1906 | + if (json_object_object_get (json, "stop_on")) { |
1907 | + nih_local char *stop_on = NULL; |
1908 | + |
1909 | + if (! state_get_json_string_var_strict (json, "stop_on", NULL, stop_on)) |
1910 | + goto error; |
1911 | + |
1912 | + if (*stop_on) { |
1913 | + nih_local JobClass *tmp = NULL; |
1914 | + |
1915 | + tmp = NIH_MUST (job_class_new (NULL, "tmp", NULL)); |
1916 | + |
1917 | + tmp->stop_on = parse_on_simple (tmp, "stop", stop_on); |
1918 | + if (! tmp->stop_on) { |
1919 | + NihError *err; |
1920 | + |
1921 | + err = nih_error_get (); |
1922 | + |
1923 | + nih_error ("%s %s: %s", |
1924 | + _("BUG"), |
1925 | + _("instance 'stop on' parse error"), |
1926 | + err->message); |
1927 | + |
1928 | + nih_free (err); |
1929 | + |
1930 | + goto error; |
1931 | + } |
1932 | + |
1933 | + nih_free (job->stop_on); |
1934 | + job->stop_on = event_operator_copy (job, tmp->stop_on); |
1935 | + if (! job->stop_on) |
1936 | + goto error; |
1937 | + } |
1938 | + } |
1939 | + |
1940 | + /* fds and num_fds handled by caller */ |
1941 | + /* pid handled by caller */ |
1942 | + |
1943 | + /* blocking is handled by state_deserialise_blocking() */ |
1944 | + |
1945 | + blocker = json_object_object_get (json, "blocker"); |
1946 | + |
1947 | + if (blocker) { |
1948 | + int event_index = -1; |
1949 | + |
1950 | + if (! state_get_json_int_var (json, "blocker", event_index)) |
1951 | + goto error; |
1952 | + job->blocker = event_from_index (event_index); |
1953 | + |
1954 | + if (! job->blocker) |
1955 | + goto error; |
1956 | + } |
1957 | + |
1958 | + if (! state_get_json_enum_var (json, |
1959 | + process_type_str_to_enum, |
1960 | + "kill_process", job->kill_process)) |
1961 | + goto error; |
1962 | + |
1963 | + /* Check to see if a kill timer exists first since we do not |
1964 | + * want to end up creating a real but empty timer. |
1965 | + */ |
1966 | + json_kill_timer = json_object_object_get (json, "kill_timer"); |
1967 | + |
1968 | + if (json_kill_timer) { |
1969 | + /* Found a partial kill timer, so create a new one and |
1970 | + * adjust its due time. By the time the main loop gets |
1971 | + * called, the due time will probably be in the past |
1972 | + * such that the job will be stopped. |
1973 | + * |
1974 | + * To be completely fair we should: |
1975 | + * |
1976 | + * - encode the time at the point of serialisation in a |
1977 | + * JSON 'meta' header. |
1978 | + * - query the time post-deserialisation and calculate |
1979 | + * the delta (being the time to perform the stateful |
1980 | + * re-exec). |
1981 | + * - add that time to all jobs with active kill timers |
1982 | + * to give their processes the full amount of time to |
1983 | + * end. |
1984 | + */ |
1985 | + nih_local NihTimer *kill_timer = job_deserialise_kill_timer (json_kill_timer); |
1986 | + if (! kill_timer) |
1987 | + goto error; |
1988 | + |
1989 | + nih_assert (job->kill_process); |
1990 | + job_process_set_kill_timer (job, job->kill_process, |
1991 | + kill_timer->timeout); |
1992 | + job_process_adj_kill_timer (job, kill_timer->due); |
1993 | + } |
1994 | + |
1995 | + if (! state_get_json_int_var_to_obj (json, job, failed)) |
1996 | + goto error; |
1997 | + |
1998 | + if (! state_get_json_enum_var (json, |
1999 | + process_type_str_to_enum, |
2000 | + "failed_process", job->failed_process)) |
2001 | + goto error; |
2002 | + |
2003 | + if (! state_get_json_int_var_to_obj (json, job, exit_status)) |
2004 | + goto error; |
2005 | + |
2006 | + if (! state_get_json_int_var_to_obj (json, job, respawn_time)) |
2007 | + goto error; |
2008 | + |
2009 | + if (! state_get_json_int_var_to_obj (json, job, respawn_count)) |
2010 | + goto error; |
2011 | + |
2012 | + json_fds = json_object_object_get (json, "fds"); |
2013 | + if (! json_fds) |
2014 | + goto error; |
2015 | + |
2016 | + ret = state_deserialise_int_array (job, json_fds, |
2017 | + int, &job->fds, &job->num_fds); |
2018 | + if (ret < 0) |
2019 | + goto error; |
2020 | + |
2021 | + json_pid = json_object_object_get (json, "pid"); |
2022 | + if (! json_pid) |
2023 | + goto error; |
2024 | + |
2025 | + ret = state_deserialise_int_array (job, json_pid, |
2026 | + pid_t, &job->pid, &len); |
2027 | + if (ret < 0) |
2028 | + goto error; |
2029 | + |
2030 | + if (len != PROCESS_LAST) |
2031 | + goto error; |
2032 | + |
2033 | + if (! state_get_json_int_var_to_obj (json, job, trace_forks)) |
2034 | + goto error; |
2035 | + |
2036 | + if (! state_get_json_enum_var (json, |
2037 | + job_trace_state_str_to_enum, |
2038 | + "trace_state", job->trace_state)) |
2039 | + goto error; |
2040 | + |
2041 | + json_logs = json_object_object_get (json, "log"); |
2042 | + |
2043 | + if (! json_logs) |
2044 | + goto error; |
2045 | + |
2046 | + if (! state_check_json_type (json_logs, array)) |
2047 | + goto error; |
2048 | + |
2049 | + for (int process = 0; process < PROCESS_LAST; process++) { |
2050 | + json_object *json_log; |
2051 | + |
2052 | + json_log = json_object_array_get_idx (json_logs, process); |
2053 | + if (! json_log) |
2054 | + goto error; |
2055 | + |
2056 | + /* NULL if there was no log configured, or we failed to |
2057 | + * deserialise it; either way, this should be non-fatal. |
2058 | + */ |
2059 | + job->log[process] = log_deserialise (job->log, json_log); |
2060 | + } |
2061 | + |
2062 | + return job; |
2063 | + |
2064 | +error: |
2065 | + nih_free (job); |
2066 | + return NULL; |
2067 | +} |
2068 | + |
2069 | +/** |
2070 | + * job_deserialise_all: |
2071 | + * |
2072 | + * @parent: job class for JSON-encoded jobs, |
2073 | + * @json: root of JSON-serialised state. |
2074 | + * |
2075 | + * Convert JSON representation of jobs back into Job objects associated |
2076 | + * with @parent. |
2077 | + * |
2078 | + * Returns: 0 on success, -1 on error. |
2079 | + **/ |
2080 | +int |
2081 | +job_deserialise_all (JobClass *parent, json_object *json) |
2082 | +{ |
2083 | + json_object *json_jobs; |
2084 | + Job *job; |
2085 | + |
2086 | + nih_assert (parent); |
2087 | + nih_assert (json); |
2088 | + |
2089 | + json_jobs = json_object_object_get (json, "jobs"); |
2090 | + |
2091 | + if (! json_jobs) |
2092 | + goto error; |
2093 | + |
2094 | + if (! state_check_json_type (json_jobs, array)) |
2095 | + goto error; |
2096 | + |
2097 | + for (int i = 0; i < json_object_array_length (json_jobs); i++) { |
2098 | + json_object *json_job; |
2099 | + |
2100 | + json_job = json_object_array_get_idx (json_jobs, i); |
2101 | + if (! json_job) |
2102 | + goto error; |
2103 | + |
2104 | + if (! state_check_json_type (json_job, object)) |
2105 | + goto error; |
2106 | + |
2107 | + job = job_deserialise (parent, json_job); |
2108 | + if (! job) |
2109 | + goto error; |
2110 | + } |
2111 | + |
2112 | + return 0; |
2113 | + |
2114 | +error: |
2115 | + return -1; |
2116 | +} |
2117 | + |
2118 | +/** |
2119 | + * job_goal_enum_to_str: |
2120 | + * |
2121 | + * @goal: JobGoal. |
2122 | + * |
2123 | + * Convert JobGoal to a string representation. |
2124 | + * |
2125 | + * Returns: string representation of @goal, or NULL if not known. |
2126 | + **/ |
2127 | +static const char * |
2128 | +job_goal_enum_to_str (JobGoal goal) |
2129 | +{ |
2130 | + state_enum_to_str (JOB_STOP, goal); |
2131 | + state_enum_to_str (JOB_START, goal); |
2132 | + state_enum_to_str (JOB_RESPAWN, goal); |
2133 | + |
2134 | + return NULL; |
2135 | +} |
2136 | + |
2137 | +/** |
2138 | + * job_goal_str_to_enum: |
2139 | + * |
2140 | + * @goal: string JobGoal value. |
2141 | + * |
2142 | + * Convert @goal back into enum value. |
2143 | + * |
2144 | + * Returns: JobGoal representation of @goal, or -1 if not known. |
2145 | + **/ |
2146 | +static JobGoal |
2147 | +job_goal_str_to_enum (const char *goal) |
2148 | +{ |
2149 | + state_str_to_enum (JOB_STOP, goal); |
2150 | + state_str_to_enum (JOB_START, goal); |
2151 | + state_str_to_enum (JOB_RESPAWN, goal); |
2152 | + |
2153 | + return -1; |
2154 | +} |
2155 | + |
2156 | +/** |
2157 | + * job_state_enum_to_str: |
2158 | + * |
2159 | + * @state: JobState. |
2160 | + * |
2161 | + * Convert JobState to a string representation. |
2162 | + * |
2163 | + * Returns: string representation of @state, or NULL if not known. |
2164 | + **/ |
2165 | +static const char * |
2166 | +job_state_enum_to_str (JobState state) |
2167 | +{ |
2168 | + state_enum_to_str (JOB_WAITING, state); |
2169 | + state_enum_to_str (JOB_STARTING, state); |
2170 | + state_enum_to_str (JOB_PRE_START, state); |
2171 | + state_enum_to_str (JOB_SPAWNED, state); |
2172 | + state_enum_to_str (JOB_POST_START, state); |
2173 | + state_enum_to_str (JOB_RUNNING, state); |
2174 | + state_enum_to_str (JOB_PRE_STOP, state); |
2175 | + state_enum_to_str (JOB_STOPPING, state); |
2176 | + state_enum_to_str (JOB_KILLED, state); |
2177 | + state_enum_to_str (JOB_POST_STOP, state); |
2178 | + |
2179 | + return NULL; |
2180 | +} |
2181 | + |
2182 | +/** |
2183 | + * job_state_str_to_enum: |
2184 | + * |
2185 | + * @state: string JobState value. |
2186 | + * |
2187 | + * Convert @state back into enum value. |
2188 | + * |
2189 | + * Returns: JobState representation of @state, or -1 if not known. |
2190 | + **/ |
2191 | +static JobState |
2192 | +job_state_str_to_enum (const char *state) |
2193 | +{ |
2194 | + state_str_to_enum (JOB_WAITING, state); |
2195 | + state_str_to_enum (JOB_STARTING, state); |
2196 | + state_str_to_enum (JOB_PRE_START, state); |
2197 | + state_str_to_enum (JOB_SPAWNED, state); |
2198 | + state_str_to_enum (JOB_POST_START, state); |
2199 | + state_str_to_enum (JOB_RUNNING, state); |
2200 | + state_str_to_enum (JOB_PRE_STOP, state); |
2201 | + state_str_to_enum (JOB_STOPPING, state); |
2202 | + state_str_to_enum (JOB_KILLED, state); |
2203 | + state_str_to_enum (JOB_POST_STOP, state); |
2204 | + |
2205 | + return -1; |
2206 | +} |
2207 | + |
2208 | +/** |
2209 | + * job_state_enum_to_str: |
2210 | + * |
2211 | + * @state: TraceState. |
2212 | + * |
2213 | + * Convert TraceState to a string representation. |
2214 | + * |
2215 | + * Returns: string representation of @state, or NULL if not known. |
2216 | + **/ |
2217 | +static const char * |
2218 | +job_trace_state_enum_to_str (TraceState state) |
2219 | +{ |
2220 | + state_enum_to_str (TRACE_NONE, state); |
2221 | + state_enum_to_str (TRACE_NEW, state); |
2222 | + state_enum_to_str (TRACE_NEW_CHILD, state); |
2223 | + state_enum_to_str (TRACE_NORMAL, state); |
2224 | + |
2225 | + return NULL; |
2226 | +} |
2227 | + |
2228 | +/** |
2229 | + * job_trace_state_str_to_enum: |
2230 | + * |
2231 | + * @state: string TraceState value. |
2232 | + * |
2233 | + * Convert @state back into enum value. |
2234 | + * |
2235 | + * Returns: TraceState representation of @state, or -1 if not known. |
2236 | + **/ |
2237 | +static TraceState |
2238 | +job_trace_state_str_to_enum (const char *state) |
2239 | +{ |
2240 | + state_str_to_enum (TRACE_NONE, state); |
2241 | + state_str_to_enum (TRACE_NEW, state); |
2242 | + state_str_to_enum (TRACE_NEW_CHILD, state); |
2243 | + state_str_to_enum (TRACE_NORMAL, state); |
2244 | + |
2245 | + return -1; |
2246 | +} |
2247 | + |
2248 | +/** |
2249 | + * job_serialise_kill_timer: |
2250 | + * |
2251 | + * @timer: NihTimer to serialise. |
2252 | + * |
2253 | + * Serialise @timer into JSON. |
2254 | + * |
2255 | + * Returns: JSON-serialised NihTimer object, or NULL on error. |
2256 | + **/ |
2257 | +static json_object * |
2258 | +job_serialise_kill_timer (NihTimer *timer) |
2259 | +{ |
2260 | + json_object *json; |
2261 | + |
2262 | + nih_assert (timer); |
2263 | + |
2264 | + json = json_object_new_object (); |
2265 | + if (! json) |
2266 | + return NULL; |
2267 | + |
2268 | + if (! state_set_json_int_var_from_obj (json, timer, timeout)) |
2269 | + goto error; |
2270 | + |
2271 | + if (! state_set_json_int_var_from_obj (json, timer, due)) |
2272 | + goto error; |
2273 | + |
2274 | + return json; |
2275 | + |
2276 | +error: |
2277 | + json_object_put (json); |
2278 | + return NULL; |
2279 | +} |
2280 | + |
2281 | +/** |
2282 | + * job_deserialise_kill_timer: |
2283 | + * |
2284 | + * @json: JSON representation of NihTimer. |
2285 | + * |
2286 | + * Deserialise @json back into an NihTimer. |
2287 | + * |
2288 | + * Returns: NihTimer on NULL on error. |
2289 | + **/ |
2290 | +static NihTimer * |
2291 | +job_deserialise_kill_timer (json_object *json) |
2292 | +{ |
2293 | + NihTimer *timer; |
2294 | + |
2295 | + nih_assert (json); |
2296 | + |
2297 | + timer = nih_new (NULL, NihTimer); |
2298 | + if (! timer) |
2299 | + return NULL; |
2300 | + |
2301 | + memset (timer, '\0', sizeof (NihTimer)); |
2302 | + |
2303 | + if (! state_get_json_int_var_to_obj (json, timer, due)) |
2304 | + goto error; |
2305 | + |
2306 | + if (! state_get_json_int_var_to_obj (json, timer, timeout)) |
2307 | + goto error; |
2308 | + |
2309 | + return timer; |
2310 | + |
2311 | +error: |
2312 | + nih_free (timer); |
2313 | + return NULL; |
2314 | +} |
2315 | |
2316 | === modified file 'init/job.h' |
2317 | --- init/job.h 2012-03-07 12:13:28 +0000 |
2318 | +++ init/job.h 2012-11-14 14:49:20 +0000 |
2319 | @@ -103,6 +103,9 @@ |
2320 | * @start_env: environment to use next time the job is started, |
2321 | * @stop_env: environment to add for the next pre-stop script, |
2322 | * @stop_on: event operator expression that can stop this job. |
2323 | + * @fds: array of file descriptors associated with events in parent |
2324 | + * JobClasses @start_on condition, |
2325 | + * @num_fds: number of elements in @fds, |
2326 | * @pid: current process ids, |
2327 | * @blocker: emitted event we're waiting to finish, |
2328 | * @blocking: list of events we're blocking from finishing, |
2329 | @@ -136,8 +139,8 @@ |
2330 | char **stop_env; |
2331 | EventOperator *stop_on; |
2332 | |
2333 | - int *fds; |
2334 | - size_t num_fds; |
2335 | + int *fds; |
2336 | + size_t num_fds; |
2337 | |
2338 | pid_t *pid; |
2339 | Event *blocker; |
2340 | @@ -208,6 +211,15 @@ |
2341 | JobProcessesElement ***processes) |
2342 | __attribute__ ((warn_unused_result)); |
2343 | |
2344 | +json_object *job_serialise (const Job *job); |
2345 | +Job *job_deserialise (JobClass *parent, json_object *json); |
2346 | + |
2347 | +json_object *job_serialise_all (const NihHash *jobs) |
2348 | + __attribute__ ((malloc, warn_unused_result)); |
2349 | + |
2350 | +int job_deserialise_all (JobClass *parent, json_object *json) |
2351 | + __attribute__ ((warn_unused_result)); |
2352 | + |
2353 | NIH_END_EXTERN |
2354 | |
2355 | #endif /* INIT_JOB_H */ |
2356 | |
2357 | === modified file 'init/job_class.c' |
2358 | --- init/job_class.c 2012-03-01 11:55:19 +0000 |
2359 | +++ init/job_class.c 2012-11-14 14:49:20 +0000 |
2360 | @@ -52,14 +52,18 @@ |
2361 | #include "blocked.h" |
2362 | #include "conf.h" |
2363 | #include "control.h" |
2364 | +#include "parse_job.h" |
2365 | |
2366 | #include "com.ubuntu.Upstart.h" |
2367 | #include "com.ubuntu.Upstart.Job.h" |
2368 | |
2369 | +#include <json.h> |
2370 | + |
2371 | +extern json_object *json_classes; |
2372 | |
2373 | /* Prototypes for static functions */ |
2374 | -static void job_class_add (JobClass *class); |
2375 | -static int job_class_remove (JobClass *class, const Session *session); |
2376 | +static void job_class_add (JobClass *class); |
2377 | +static int job_class_remove (JobClass *class, const Session *session); |
2378 | |
2379 | /** |
2380 | * default_console: |
2381 | @@ -356,6 +360,30 @@ |
2382 | } |
2383 | |
2384 | /** |
2385 | + * job_class_add_safe: |
2386 | + * @class: new class to select. |
2387 | + * |
2388 | + * Adds @class to the hash table iff no existing entry of the |
2389 | + * same name exists. |
2390 | + **/ |
2391 | +void |
2392 | +job_class_add_safe (JobClass *class) |
2393 | +{ |
2394 | + JobClass *existing = NULL; |
2395 | + |
2396 | + nih_assert (class); |
2397 | + nih_assert (class->name); |
2398 | + |
2399 | + control_init (); |
2400 | + |
2401 | + existing = (JobClass *)nih_hash_search (job_classes, class->name, NULL); |
2402 | + |
2403 | + nih_assert (! existing); |
2404 | + |
2405 | + job_class_add (class); |
2406 | +} |
2407 | + |
2408 | +/** |
2409 | * job_class_remove: |
2410 | * @class: class to remove, |
2411 | * @session: Session of @class. |
2412 | @@ -720,7 +748,7 @@ |
2413 | * If the instance goal is already start, |
2414 | * the com.ubuntu.Upstart.Error.AlreadyStarted D-Bus error will be returned |
2415 | * immediately. If the instance fails to start, the |
2416 | - * com.ubuntu.Upstart.Error.JobFailed D-BUs error will be returned when the |
2417 | + * com.ubuntu.Upstart.Error.JobFailed D-Bus error will be returned when the |
2418 | * problem occurs. |
2419 | * |
2420 | * When @wait is TRUE the method call will not return until the job has |
2421 | @@ -1112,6 +1140,39 @@ |
2422 | return 0; |
2423 | } |
2424 | |
2425 | +/** |
2426 | + * job_class_get: |
2427 | + * |
2428 | + * @name: name of job class, |
2429 | + * @session: session of job class. |
2430 | + * |
2431 | + * Obtain JobClass with name @name and session @session. |
2432 | + * |
2433 | + * Returns: JobClass, or NULL if no matching job class found. |
2434 | + **/ |
2435 | +JobClass * |
2436 | +job_class_get (const char *name, Session *session) |
2437 | +{ |
2438 | + JobClass *class = NULL; |
2439 | + NihList *prev = NULL; |
2440 | + |
2441 | + nih_assert (name); |
2442 | + |
2443 | + do { |
2444 | + class = (JobClass *)nih_hash_search (job_classes, name, prev); |
2445 | + if (! class) |
2446 | + return NULL; |
2447 | + if (class && class->session == session) |
2448 | + return class; |
2449 | + |
2450 | + nih_assert (class); |
2451 | + |
2452 | + prev = (NihList *)class; |
2453 | + } while (TRUE); |
2454 | + |
2455 | + nih_assert_not_reached (); |
2456 | +} |
2457 | + |
2458 | |
2459 | /** |
2460 | * job_class_get_name: |
2461 | @@ -1500,3 +1561,651 @@ |
2462 | |
2463 | return 0; |
2464 | } |
2465 | + |
2466 | + |
2467 | +/** |
2468 | + * job_class_serialise: |
2469 | + * @class: job class to serialise. |
2470 | + * |
2471 | + * Convert @class into a JSON representation for serialisation. |
2472 | + * Caller must free returned value using json_object_put(). |
2473 | + * |
2474 | + * Returns: JSON-serialised JobClass object, or NULL on error. |
2475 | + **/ |
2476 | +json_object * |
2477 | +job_class_serialise (const JobClass *class) |
2478 | +{ |
2479 | + json_object *json; |
2480 | + json_object *json_export; |
2481 | + json_object *json_emits; |
2482 | + json_object *json_processes; |
2483 | + json_object *json_normalexit; |
2484 | + json_object *json_limits; |
2485 | + json_object *json_jobs; |
2486 | + nih_local char *start_on = NULL; |
2487 | + nih_local char *stop_on = NULL; |
2488 | + int session_index; |
2489 | + |
2490 | + nih_assert (class); |
2491 | + nih_assert (job_classes); |
2492 | + |
2493 | + json = json_object_new_object (); |
2494 | + if (! json) |
2495 | + return NULL; |
2496 | + |
2497 | + session_index = session_get_index (class->session); |
2498 | + if (session_index < 0) |
2499 | + goto error; |
2500 | + |
2501 | + if (! state_set_json_int_var (json, "session", session_index)) |
2502 | + goto error; |
2503 | + |
2504 | + if (! state_set_json_string_var_from_obj (json, class, name)) |
2505 | + goto error; |
2506 | + |
2507 | + if (! state_set_json_string_var_from_obj (json, class, path)) |
2508 | + goto error; |
2509 | + |
2510 | + if (! state_set_json_string_var_from_obj (json, class, instance)) |
2511 | + goto error; |
2512 | + |
2513 | + json_jobs = job_serialise_all (class->instances); |
2514 | + |
2515 | + if (! json_jobs) |
2516 | + goto error; |
2517 | + |
2518 | + json_object_object_add (json, "jobs", json_jobs); |
2519 | + |
2520 | + if (! state_set_json_string_var_from_obj (json, class, description)) |
2521 | + goto error; |
2522 | + |
2523 | + if (! state_set_json_string_var_from_obj (json, class, author)) |
2524 | + goto error; |
2525 | + |
2526 | + if (! state_set_json_string_var_from_obj (json, class, version)) |
2527 | + goto error; |
2528 | + |
2529 | + if (! state_set_json_str_array_from_obj (json, class, env)) |
2530 | + goto error; |
2531 | + |
2532 | + json_export = class->export |
2533 | + ? state_serialise_str_array (class->export) |
2534 | + : json_object_new_array (); |
2535 | + |
2536 | + if (! json_export) |
2537 | + goto error; |
2538 | + json_object_object_add (json, "export", json_export); |
2539 | + |
2540 | + if (class->start_on) { |
2541 | + start_on = event_operator_collapse (class->start_on); |
2542 | + if (! start_on) |
2543 | + goto error; |
2544 | + |
2545 | + if (! state_set_json_string_var (json, "start_on", start_on)) |
2546 | + goto error; |
2547 | + } |
2548 | + |
2549 | + if (class->stop_on) { |
2550 | + stop_on = event_operator_collapse (class->stop_on); |
2551 | + if (! stop_on) |
2552 | + goto error; |
2553 | + |
2554 | + if (! state_set_json_string_var (json, "stop_on", stop_on)) |
2555 | + goto error; |
2556 | + } |
2557 | + |
2558 | + json_emits = class->emits |
2559 | + ? state_serialise_str_array (class->emits) |
2560 | + : json_object_new_array (); |
2561 | + |
2562 | + if (! json_emits) |
2563 | + goto error; |
2564 | + json_object_object_add (json, "emits", json_emits); |
2565 | + |
2566 | + json_processes = process_serialise_all ( |
2567 | + (const Process const * const * const)class->process); |
2568 | + if (! json_processes) |
2569 | + goto error; |
2570 | + json_object_object_add (json, "process", json_processes); |
2571 | + |
2572 | + if (! state_set_json_enum_var (json, |
2573 | + job_class_expect_type_enum_to_str, |
2574 | + "expect", class->expect)) |
2575 | + goto error; |
2576 | + |
2577 | + if (! state_set_json_int_var_from_obj (json, class, task)) |
2578 | + goto error; |
2579 | + |
2580 | + if (! state_set_json_int_var_from_obj (json, class, kill_timeout)) |
2581 | + goto error; |
2582 | + |
2583 | + if (! state_set_json_int_var_from_obj (json, class, kill_signal)) |
2584 | + goto error; |
2585 | + |
2586 | + if (! state_set_json_int_var_from_obj (json, class, respawn)) |
2587 | + goto error; |
2588 | + |
2589 | + if (! state_set_json_int_var_from_obj (json, class, respawn_limit)) |
2590 | + goto error; |
2591 | + |
2592 | + if (! state_set_json_int_var_from_obj (json, class, respawn_interval)) |
2593 | + goto error; |
2594 | + |
2595 | + json_normalexit = state_serialise_int_array (int, class->normalexit, |
2596 | + class->normalexit_len); |
2597 | + if (! json_normalexit) |
2598 | + goto error; |
2599 | + |
2600 | + json_object_object_add (json, "normalexit", json_normalexit); |
2601 | + |
2602 | + if (! state_set_json_enum_var (json, |
2603 | + job_class_console_type_enum_to_str, |
2604 | + "console", class->console)) |
2605 | + goto error; |
2606 | + |
2607 | + if (! state_set_json_int_var_from_obj (json, class, umask)) |
2608 | + goto error; |
2609 | + |
2610 | + if (! state_set_json_int_var_from_obj (json, class, nice)) |
2611 | + goto error; |
2612 | + |
2613 | + if (! state_set_json_int_var_from_obj (json, class, oom_score_adj)) |
2614 | + goto error; |
2615 | + |
2616 | + json_limits = state_rlimit_serialise_all (class->limits); |
2617 | + if (! json_limits) |
2618 | + goto error; |
2619 | + json_object_object_add (json, "limits", json_limits); |
2620 | + |
2621 | + if (! state_set_json_string_var_from_obj (json, class, chroot)) |
2622 | + goto error; |
2623 | + |
2624 | + if (! state_set_json_string_var_from_obj (json, class, chdir)) |
2625 | + goto error; |
2626 | + |
2627 | + if (! state_set_json_string_var_from_obj (json, class, setuid)) |
2628 | + goto error; |
2629 | + |
2630 | + if (! state_set_json_string_var_from_obj (json, class, setgid)) |
2631 | + goto error; |
2632 | + |
2633 | + if (! state_set_json_int_var_from_obj (json, class, deleted)) |
2634 | + goto error; |
2635 | + |
2636 | + if (! state_set_json_int_var_from_obj (json, class, debug)) |
2637 | + goto error; |
2638 | + |
2639 | + if (! state_set_json_string_var_from_obj (json, class, usage)) |
2640 | + goto error; |
2641 | + |
2642 | + return json; |
2643 | + |
2644 | +error: |
2645 | + json_object_put (json); |
2646 | + return NULL; |
2647 | +} |
2648 | + |
2649 | +/** |
2650 | + * job_class_serialise_all: |
2651 | + * |
2652 | + * Convert existing JobClass objects to JSON representation. |
2653 | + * |
2654 | + * Returns: JSON object containing array of JobClass objects, |
2655 | + * or NULL on error. |
2656 | + **/ |
2657 | +json_object * |
2658 | +job_class_serialise_all (void) |
2659 | +{ |
2660 | + json_object *json; |
2661 | + |
2662 | + job_class_init (); |
2663 | + |
2664 | + json = json_object_new_array (); |
2665 | + if (! json) |
2666 | + return NULL; |
2667 | + |
2668 | + NIH_HASH_FOREACH (job_classes, iter) { |
2669 | + json_object *json_class; |
2670 | + JobClass *class = (JobClass *)iter; |
2671 | + |
2672 | + json_class = job_class_serialise (class); |
2673 | + |
2674 | + /* No object returned means the class doesn't need to be |
2675 | + * serialised. Even if this is a real failure, it's always |
2676 | + * better to serialise as much of the state as possible. |
2677 | + */ |
2678 | + if (! json_class) |
2679 | + continue; |
2680 | + |
2681 | + json_object_array_add (json, json_class); |
2682 | + } |
2683 | + |
2684 | + return json; |
2685 | +} |
2686 | + |
2687 | +/** |
2688 | + * job_class_deserialise: |
2689 | + * @json: JSON-serialised JobClass object to deserialise. |
2690 | + * |
2691 | + * Create JobClass from provided JSON and add to the |
2692 | + * job classes table. |
2693 | + * |
2694 | + * Returns: JobClass object, or NULL on error. |
2695 | + **/ |
2696 | +JobClass * |
2697 | +job_class_deserialise (json_object *json) |
2698 | +{ |
2699 | + json_object *json_normalexit; |
2700 | + JobClass *class = NULL; |
2701 | + int session_index = -1; |
2702 | + int ret; |
2703 | + nih_local char *name = NULL; |
2704 | + nih_local char *path = NULL; |
2705 | + |
2706 | + nih_assert (json); |
2707 | + nih_assert (job_classes); |
2708 | + |
2709 | + if (! state_check_json_type (json, object)) |
2710 | + goto error; |
2711 | + |
2712 | + if (! state_get_json_int_var (json, "session", session_index)) |
2713 | + goto error; |
2714 | + |
2715 | + if (session_index < 0) |
2716 | + goto error; |
2717 | + |
2718 | + if (! state_get_json_string_var_strict (json, "name", NULL, name)) |
2719 | + goto error; |
2720 | + |
2721 | + class = NIH_MUST (job_class_new (NULL, name, |
2722 | + session_from_index (session_index))); |
2723 | + if (! class) |
2724 | + goto error; |
2725 | + |
2726 | + if (class->session != NULL) { |
2727 | + nih_warn ("XXX: WARNING (%s:%d): deserialisation of " |
2728 | + "user jobs and chroot sessions not currently supported", |
2729 | + __func__, __LINE__); |
2730 | + goto error; |
2731 | + } |
2732 | + |
2733 | + /* job_class_new() sets path */ |
2734 | + if (! state_get_json_string_var_strict (json, "path", NULL, path)) |
2735 | + goto error; |
2736 | + |
2737 | + nih_assert (! strcmp (class->path, path)); |
2738 | + |
2739 | + /* Discard default instance as we're about to be handed a fresh |
2740 | + * string from the JSON. |
2741 | + */ |
2742 | + nih_free (class->instance); |
2743 | + |
2744 | + if (! state_get_json_string_var_to_obj (json, class, instance)) |
2745 | + goto error; |
2746 | + |
2747 | + if (! state_get_json_string_var_to_obj (json, class, description)) |
2748 | + goto error; |
2749 | + |
2750 | + if (! state_get_json_string_var_to_obj (json, class, author)) |
2751 | + goto error; |
2752 | + |
2753 | + if (! state_get_json_string_var_to_obj (json, class, version)) |
2754 | + goto error; |
2755 | + |
2756 | + if (! state_get_json_env_array_to_obj (json, class, env)) |
2757 | + goto error; |
2758 | + |
2759 | + if (! state_get_json_env_array_to_obj (json, class, export)) |
2760 | + goto error; |
2761 | + |
2762 | + /* start and stop conditions are optional */ |
2763 | + if (json_object_object_get (json, "start_on")) { |
2764 | + nih_local char *start_on = NULL; |
2765 | + |
2766 | + if (! state_get_json_string_var_strict (json, "start_on", NULL, start_on)) |
2767 | + goto error; |
2768 | + |
2769 | + if (*start_on) { |
2770 | + class->start_on = parse_on_simple (class, "start", start_on); |
2771 | + if (! class->start_on) { |
2772 | + NihError *err; |
2773 | + |
2774 | + err = nih_error_get (); |
2775 | + |
2776 | + nih_error ("%s %s: %s", |
2777 | + _("BUG"), |
2778 | + _("'start on' parse error"), |
2779 | + err->message); |
2780 | + |
2781 | + nih_free (err); |
2782 | + |
2783 | + goto error; |
2784 | + } |
2785 | + } |
2786 | + } |
2787 | + |
2788 | + if (json_object_object_get (json, "stop_on")) { |
2789 | + nih_local char *stop_on = NULL; |
2790 | + |
2791 | + if (! state_get_json_string_var_strict (json, "stop_on", NULL, stop_on)) |
2792 | + goto error; |
2793 | + |
2794 | + if (*stop_on) { |
2795 | + class->stop_on = parse_on_simple (class, "stop", stop_on); |
2796 | + if (! class->stop_on) { |
2797 | + NihError *err; |
2798 | + |
2799 | + err = nih_error_get (); |
2800 | + |
2801 | + nih_error ("%s %s: %s", |
2802 | + _("BUG"), |
2803 | + _("'stop on' parse error"), |
2804 | + err->message); |
2805 | + |
2806 | + nih_free (err); |
2807 | + |
2808 | + goto error; |
2809 | + } |
2810 | + } |
2811 | + } |
2812 | + |
2813 | + if (! state_get_json_str_array_to_obj (json, class, emits)) |
2814 | + goto error; |
2815 | + |
2816 | + if (! state_get_json_enum_var (json, |
2817 | + job_class_expect_type_str_to_enum, |
2818 | + "expect", class->expect)) |
2819 | + goto error; |
2820 | + |
2821 | + if (! state_get_json_int_var_to_obj (json, class, task)) |
2822 | + goto error; |
2823 | + |
2824 | + if (! state_get_json_int_var_to_obj (json, class, kill_timeout)) |
2825 | + goto error; |
2826 | + |
2827 | + if (! state_get_json_int_var_to_obj (json, class, kill_signal)) |
2828 | + goto error; |
2829 | + |
2830 | + if (! state_get_json_int_var_to_obj (json, class, respawn)) |
2831 | + goto error; |
2832 | + |
2833 | + if (! state_get_json_int_var_to_obj (json, class, respawn_limit)) |
2834 | + goto error; |
2835 | + |
2836 | + if (! state_get_json_int_var_to_obj (json, class, respawn_interval)) |
2837 | + goto error; |
2838 | + |
2839 | + if (! state_get_json_enum_var (json, |
2840 | + job_class_console_type_str_to_enum, |
2841 | + "console", class->console)) |
2842 | + goto error; |
2843 | + |
2844 | + if (! state_get_json_int_var_to_obj (json, class, umask)) |
2845 | + goto error; |
2846 | + |
2847 | + if (! state_get_json_int_var_to_obj (json, class, nice)) |
2848 | + goto error; |
2849 | + |
2850 | + if (! state_get_json_int_var_to_obj (json, class, oom_score_adj)) |
2851 | + goto error; |
2852 | + |
2853 | + if (! state_get_json_string_var_to_obj (json, class, chroot)) |
2854 | + goto error; |
2855 | + |
2856 | + if (! state_get_json_string_var_to_obj (json, class, chdir)) |
2857 | + goto error; |
2858 | + |
2859 | + if (! state_get_json_string_var_to_obj (json, class, setuid)) |
2860 | + goto error; |
2861 | + |
2862 | + if (! state_get_json_string_var_to_obj (json, class, setgid)) |
2863 | + goto error; |
2864 | + |
2865 | + if (! state_get_json_int_var_to_obj (json, class, deleted)) |
2866 | + goto error; |
2867 | + |
2868 | + if (! state_get_json_int_var_to_obj (json, class, debug)) |
2869 | + goto error; |
2870 | + |
2871 | + if (! state_get_json_string_var_to_obj (json, class, usage)) |
2872 | + goto error; |
2873 | + |
2874 | + json_normalexit = json_object_object_get (json, "normalexit"); |
2875 | + if (! json_normalexit) |
2876 | + goto error; |
2877 | + |
2878 | + ret = state_deserialise_int_array (class, json_normalexit, |
2879 | + int, &class->normalexit, &class->normalexit_len); |
2880 | + if (ret < 0) |
2881 | + goto error; |
2882 | + |
2883 | + if (state_rlimit_deserialise_all (json, class, &class->limits) < 0) |
2884 | + goto error; |
2885 | + |
2886 | + if (process_deserialise_all (json, class->process, class->process) < 0) |
2887 | + goto error; |
2888 | + |
2889 | + /* Force class to be known. |
2890 | + * |
2891 | + * We cannot use job_class_*consider() since the |
2892 | + * JobClasses have no associated ConfFile. |
2893 | + */ |
2894 | + job_class_add_safe (class); |
2895 | + |
2896 | + /* Any jobs must be added after the class is registered |
2897 | + * (since you cannot add a job to a partially-created |
2898 | + * class). |
2899 | + */ |
2900 | + if (job_deserialise_all (class, json) < 0) |
2901 | + goto error; |
2902 | + |
2903 | + return class; |
2904 | + |
2905 | +error: |
2906 | + nih_free (class); |
2907 | + return NULL; |
2908 | +} |
2909 | + |
2910 | +/** |
2911 | + * job_class_deserialise_all: |
2912 | + * |
2913 | + * @json: root of JSON-serialised state. |
2914 | + * |
2915 | + * Convert JSON representation of JobClasses back into JobClass objects. |
2916 | + * |
2917 | + * Returns: 0 on success, -1 on error. |
2918 | + **/ |
2919 | +int |
2920 | +job_class_deserialise_all (json_object *json) |
2921 | +{ |
2922 | + JobClass *class = NULL; |
2923 | + |
2924 | + nih_assert (json); |
2925 | + |
2926 | + job_class_init (); |
2927 | + |
2928 | + json_classes = json_object_object_get (json, "job_classes"); |
2929 | + |
2930 | + if (! json_classes) |
2931 | + goto error; |
2932 | + |
2933 | + if (! state_check_json_type (json_classes, array)) |
2934 | + goto error; |
2935 | + |
2936 | + for (int i = 0; i < json_object_array_length (json_classes); i++) { |
2937 | + json_object *json_class; |
2938 | + |
2939 | + json_class = json_object_array_get_idx (json_classes, i); |
2940 | + if (! json_class) |
2941 | + goto error; |
2942 | + |
2943 | + if (! state_check_json_type (json_class, object)) |
2944 | + goto error; |
2945 | + |
2946 | + class = job_class_deserialise (json_class); |
2947 | + if (! class) |
2948 | + goto error; |
2949 | + |
2950 | + /* FIXME: |
2951 | + * |
2952 | + * If user sessions exist (ie 'initctl --session list' |
2953 | + * has been run), we get this failure: |
2954 | + * |
2955 | + * serialised path='/com/ubuntu/Upstart/jobs/1000/bang' |
2956 | + * path set by job_class_new()='/com/ubuntu/Upstart/jobs/_/1000/bang' |
2957 | + * |
2958 | + */ |
2959 | + |
2960 | + } |
2961 | + |
2962 | + return 0; |
2963 | + |
2964 | +error: |
2965 | + if (class) |
2966 | + nih_free (class); |
2967 | + |
2968 | + return -1; |
2969 | +} |
2970 | + |
2971 | + |
2972 | +/** |
2973 | + * job_class_expect_type_enum_to_str: |
2974 | + * |
2975 | + * @expect: ExpectType. |
2976 | + * |
2977 | + * Convert ExpectType to a string representation. |
2978 | + * |
2979 | + * Returns: string representation of @expect, or NULL if not known. |
2980 | + **/ |
2981 | +const char * |
2982 | +job_class_expect_type_enum_to_str (ExpectType expect) |
2983 | +{ |
2984 | + state_enum_to_str (EXPECT_NONE, expect); |
2985 | + state_enum_to_str (EXPECT_STOP, expect); |
2986 | + state_enum_to_str (EXPECT_DAEMON, expect); |
2987 | + state_enum_to_str (EXPECT_FORK, expect); |
2988 | + |
2989 | + return NULL; |
2990 | +} |
2991 | + |
2992 | +/** |
2993 | + * job_class_expect_type_str_to_enum: |
2994 | + * |
2995 | + * @expect: string ExpectType value. |
2996 | + * |
2997 | + * Convert @expect back into an enum value. |
2998 | + * |
2999 | + * Returns: ExpectType representing @expect, or -1 if not known. |
3000 | + **/ |
3001 | +ExpectType |
3002 | +job_class_expect_type_str_to_enum (const char *expect) |
3003 | +{ |
3004 | + nih_assert (expect); |
3005 | + |
3006 | + state_str_to_enum (EXPECT_NONE, expect); |
3007 | + state_str_to_enum (EXPECT_STOP, expect); |
3008 | + state_str_to_enum (EXPECT_DAEMON, expect); |
3009 | + state_str_to_enum (EXPECT_FORK, expect); |
3010 | + |
3011 | + return -1; |
3012 | +} |
3013 | + |
3014 | +/** |
3015 | + * job_class_console_type_enum_to_str: |
3016 | + * |
3017 | + * @console: ConsoleType. |
3018 | + * |
3019 | + * Convert ConsoleType to a string representation. |
3020 | + * |
3021 | + * Returns: string representation of @console, or NULL if not known. |
3022 | + **/ |
3023 | +const char * |
3024 | +job_class_console_type_enum_to_str (ConsoleType console) |
3025 | +{ |
3026 | + state_enum_to_str (CONSOLE_NONE, console); |
3027 | + state_enum_to_str (CONSOLE_OUTPUT, console); |
3028 | + state_enum_to_str (CONSOLE_OWNER, console); |
3029 | + state_enum_to_str (CONSOLE_LOG, console); |
3030 | + |
3031 | + return NULL; |
3032 | +} |
3033 | + |
3034 | +/** |
3035 | + * job_class_console_type_str_to_enum: |
3036 | + * |
3037 | + * @console: string ConsoleType value. |
3038 | + * |
3039 | + * Convert @console back into enum value. |
3040 | + * |
3041 | + * Returns: ExpectType representing @console, or -1 if not known. |
3042 | + **/ |
3043 | +ConsoleType |
3044 | +job_class_console_type_str_to_enum (const char *console) |
3045 | +{ |
3046 | + if (! console) |
3047 | + goto error; |
3048 | + |
3049 | + state_str_to_enum (CONSOLE_NONE, console); |
3050 | + state_str_to_enum (CONSOLE_OUTPUT, console); |
3051 | + state_str_to_enum (CONSOLE_OWNER, console); |
3052 | + state_str_to_enum (CONSOLE_LOG, console); |
3053 | + |
3054 | +error: |
3055 | + return -1; |
3056 | +} |
3057 | + |
3058 | +/** |
3059 | + * job_class_prepare_reexec: |
3060 | + * |
3061 | + * Prepare for a re-exec by clearing the CLOEXEC bit on all log object |
3062 | + * file descriptors associated with their parent jobs. |
3063 | + **/ |
3064 | +void |
3065 | +job_class_prepare_reexec (void) |
3066 | +{ |
3067 | + job_class_init (); |
3068 | + |
3069 | + NIH_HASH_FOREACH (job_classes, iter) { |
3070 | + JobClass *class = (JobClass *)iter; |
3071 | + |
3072 | + NIH_HASH_FOREACH (class->instances, job_iter) { |
3073 | + Job *job = (Job *)job_iter; |
3074 | + |
3075 | + nih_assert (job->log); |
3076 | + |
3077 | + for (int process = 0; process < PROCESS_LAST; process++) { |
3078 | + int fd; |
3079 | + Log *log; |
3080 | + |
3081 | + log = job->log[process]; |
3082 | + |
3083 | + /* No associated job process or logger has detected |
3084 | + * remote end of pty has closed. |
3085 | + */ |
3086 | + if (! log || ! log->io) |
3087 | + continue; |
3088 | + |
3089 | + nih_assert (log->io->watch); |
3090 | + |
3091 | + fd = log->io->watch->fd; |
3092 | + if (fd < 0) |
3093 | + continue; |
3094 | + |
3095 | + if (state_toggle_cloexec (fd, FALSE) < 0) |
3096 | + goto error; |
3097 | + |
3098 | + fd = log->fd; |
3099 | + if (fd < 0) |
3100 | + continue; |
3101 | + |
3102 | + if (state_toggle_cloexec (fd, FALSE) < 0) |
3103 | + goto error; |
3104 | + } |
3105 | + } |
3106 | + } |
3107 | + |
3108 | + return; |
3109 | + |
3110 | +error: |
3111 | + nih_warn (_("unable to clear CLOEXEC bit on log fd")); |
3112 | +} |
3113 | |
3114 | === modified file 'init/job_class.h' |
3115 | --- init/job_class.h 2012-02-16 15:45:41 +0000 |
3116 | +++ init/job_class.h 2012-11-14 14:49:20 +0000 |
3117 | @@ -196,7 +196,7 @@ |
3118 | int task; |
3119 | |
3120 | time_t kill_timeout; |
3121 | - int kill_signal; |
3122 | + int kill_signal; |
3123 | |
3124 | int respawn; |
3125 | int respawn_limit; |
3126 | @@ -238,6 +238,8 @@ |
3127 | int job_class_consider (JobClass *class); |
3128 | int job_class_reconsider (JobClass *class); |
3129 | |
3130 | +void job_class_add_safe (JobClass *class); |
3131 | + |
3132 | void job_class_register (JobClass *class, |
3133 | DBusConnection *conn, int signal); |
3134 | void job_class_unregister (JobClass *class, |
3135 | @@ -306,9 +308,42 @@ |
3136 | NihDBusMessage *message, |
3137 | char **usage); |
3138 | |
3139 | +const char * |
3140 | +job_class_console_type_enum_to_str (ConsoleType console) |
3141 | + __attribute__ ((warn_unused_result)); |
3142 | + |
3143 | +ConsoleType |
3144 | +job_class_console_type_str_to_enum (const char *name) |
3145 | + __attribute__ ((warn_unused_result)); |
3146 | + |
3147 | +const char * |
3148 | +job_class_expect_type_enum_to_str (ExpectType expect) |
3149 | + __attribute__ ((warn_unused_result)); |
3150 | + |
3151 | +ExpectType |
3152 | +job_class_expect_type_str_to_enum (const char *name) |
3153 | + __attribute__ ((warn_unused_result)); |
3154 | + |
3155 | ConsoleType job_class_console_type (const char *console) |
3156 | __attribute__ ((warn_unused_result)); |
3157 | |
3158 | +json_object *job_class_serialise (const JobClass *class) |
3159 | + __attribute__ ((warn_unused_result, malloc)); |
3160 | + |
3161 | +JobClass *job_class_deserialise (json_object *json) |
3162 | + __attribute__ ((malloc, warn_unused_result)); |
3163 | + |
3164 | +json_object * job_class_serialise_all (void) |
3165 | + __attribute__ ((warn_unused_result, malloc)); |
3166 | + |
3167 | +int job_class_deserialise_all (json_object *json) |
3168 | + __attribute__ ((warn_unused_result)); |
3169 | + |
3170 | +JobClass * job_class_get (const char *name, Session *session) |
3171 | + __attribute__ ((warn_unused_result)); |
3172 | + |
3173 | +void job_class_prepare_reexec (void); |
3174 | + |
3175 | NIH_END_EXTERN |
3176 | |
3177 | #endif /* INIT_JOB_CLASS_H */ |
3178 | |
3179 | === modified file 'init/job_process.c' |
3180 | --- init/job_process.c 2012-08-02 08:57:20 +0000 |
3181 | +++ init/job_process.c 2012-11-14 14:49:20 +0000 |
3182 | @@ -637,7 +637,7 @@ |
3183 | */ |
3184 | setsid (); |
3185 | |
3186 | - /* Set the process environment from the function paramters. */ |
3187 | + /* Set the process environment from the function parameters. */ |
3188 | environ = (char **)env; |
3189 | |
3190 | /* Handle unprivileged user job by dropping privileges to |
3191 | @@ -1226,7 +1226,7 @@ |
3192 | nih_assert (job != NULL); |
3193 | nih_assert (job->pid[process] > 0); |
3194 | nih_assert (job->kill_timer == NULL); |
3195 | - nih_assert (job->kill_process = -1); |
3196 | + nih_assert (job->kill_process == PROCESS_INVALID); |
3197 | |
3198 | nih_info (_("Sending %s signal to %s %s process (%d)"), |
3199 | nih_signal_to_name (job->class->kill_signal), |
3200 | @@ -1246,13 +1246,50 @@ |
3201 | return; |
3202 | } |
3203 | |
3204 | + job_process_set_kill_timer (job, process, job->class->kill_timeout); |
3205 | +} |
3206 | + |
3207 | +/** |
3208 | + * job_process_set_kill_timer: |
3209 | + * @job: job to set kill timer for, |
3210 | + * @process: process to be killed, |
3211 | + * @timeout: timeout to apply for timer. |
3212 | + * |
3213 | + * Set kill timer for specified @job @process with timeout @timeout. |
3214 | + **/ |
3215 | +void |
3216 | +job_process_set_kill_timer (Job *job, |
3217 | + ProcessType process, |
3218 | + time_t timeout) |
3219 | +{ |
3220 | + nih_assert (job); |
3221 | + nih_assert (timeout); |
3222 | + |
3223 | job->kill_process = process; |
3224 | job->kill_timer = NIH_MUST (nih_timer_add_timeout ( |
3225 | - job, job->class->kill_timeout, |
3226 | + job, timeout, |
3227 | (NihTimerCb)job_process_kill_timer, job)); |
3228 | } |
3229 | |
3230 | /** |
3231 | + * job_process_adj_kill_timer: |
3232 | + * |
3233 | + * @job: job whose kill timer is to be modified, |
3234 | + * @due: new due time to set for job kill timer. |
3235 | + * |
3236 | + * Adjust due time for @job's kill timer to @due. |
3237 | + **/ |
3238 | +void |
3239 | +job_process_adj_kill_timer (Job *job, time_t due) |
3240 | +{ |
3241 | + nih_assert (job); |
3242 | + nih_assert (job->kill_timer); |
3243 | + nih_assert (due); |
3244 | + |
3245 | + job->kill_timer->due = due; |
3246 | +} |
3247 | + |
3248 | +/** |
3249 | * job_process_kill_timer: |
3250 | * @job: job to kill process of, |
3251 | * @timer: timer that caused us to be called. |
3252 | @@ -1270,13 +1307,13 @@ |
3253 | nih_assert (job != NULL); |
3254 | nih_assert (timer != NULL); |
3255 | nih_assert (job->kill_timer == timer); |
3256 | - nih_assert (job->kill_process != (ProcessType)-1); |
3257 | + nih_assert (job->kill_process != PROCESS_INVALID); |
3258 | |
3259 | process = job->kill_process; |
3260 | nih_assert (job->pid[process] > 0); |
3261 | |
3262 | job->kill_timer = NULL; |
3263 | - job->kill_process = -1; |
3264 | + job->kill_process = PROCESS_INVALID; |
3265 | |
3266 | nih_info (_("Sending %s signal to %s %s process (%d)"), |
3267 | "KILL", |
3268 | @@ -1547,7 +1584,7 @@ |
3269 | job_name (job)); |
3270 | |
3271 | failed = FALSE; |
3272 | - job_failed (job, -1, 0); |
3273 | + job_failed (job, PROCESS_INVALID, 0); |
3274 | } else { |
3275 | nih_warn (_("%s %s process ended, respawning"), |
3276 | job_name (job), |
3277 | @@ -1631,7 +1668,7 @@ |
3278 | if (job->kill_timer) { |
3279 | nih_unref (job->kill_timer, job); |
3280 | job->kill_timer = NULL; |
3281 | - job->kill_process = -1; |
3282 | + job->kill_process = PROCESS_INVALID; |
3283 | } |
3284 | |
3285 | if (job->class->console == CONSOLE_LOG && job->log[process]) { |
3286 | |
3287 | === modified file 'init/job_process.h' |
3288 | --- init/job_process.h 2012-08-02 08:57:20 +0000 |
3289 | +++ init/job_process.h 2012-11-14 14:49:20 +0000 |
3290 | @@ -143,6 +143,12 @@ |
3291 | char *job_process_log_path (Job *job, int user_job) |
3292 | __attribute__ ((malloc, warn_unused_result)); |
3293 | |
3294 | +void job_process_set_kill_timer (Job *job, |
3295 | + ProcessType process, |
3296 | + time_t timeout); |
3297 | + |
3298 | +void job_process_adj_kill_timer (Job *job, time_t due); |
3299 | + |
3300 | NIH_END_EXTERN |
3301 | |
3302 | #endif /* INIT_JOB_PROCESS_H */ |
3303 | |
3304 | === modified file 'init/log.c' |
3305 | --- init/log.c 2012-03-16 21:06:11 +0000 |
3306 | +++ init/log.c 2012-11-14 14:49:20 +0000 |
3307 | @@ -48,8 +48,13 @@ |
3308 | /** |
3309 | * log_unflushed_files: |
3310 | * |
3311 | - * List of known sources of configuration; each item is an |
3312 | - * NihListEntry. |
3313 | + * List of NihListEntry objects containing Log objects which are no |
3314 | + * longer associated with Job processes. |
3315 | + * |
3316 | + * All the entries in the list contain unflushed Log data. |
3317 | + * |
3318 | + * Used to capture job process output early in the boot process for |
3319 | + * jobs that end before the log partition is mounted and writeable. |
3320 | **/ |
3321 | NihList *log_unflushed_files = NULL; |
3322 | |
3323 | @@ -393,7 +398,7 @@ |
3324 | * |
3325 | * Opens log file associated with @log if not already open. |
3326 | * |
3327 | - * Returns 0 on success, -1 on failure. |
3328 | + * Returns: 0 on success, -1 on failure. |
3329 | **/ |
3330 | static int |
3331 | log_file_open (Log *log) |
3332 | @@ -505,7 +510,7 @@ |
3333 | * a corrupted log file should space later become |
3334 | * available. |
3335 | * |
3336 | - * Returns 0 on success, -1 on failure. |
3337 | + * Returns: 0 on success, -1 on failure. |
3338 | **/ |
3339 | static int |
3340 | log_file_write (Log *log, const char *buf, size_t len) |
3341 | @@ -638,6 +643,8 @@ |
3342 | if (nih_io_buffer_resize (io->recv_buf, LOG_READ_SIZE) < 0) |
3343 | break; |
3344 | |
3345 | + errno = 0; |
3346 | + |
3347 | /* Append to buffer */ |
3348 | len = read (io->watch->fd, |
3349 | io->recv_buf->buf + io->recv_buf->len, |
3350 | @@ -680,22 +687,23 @@ |
3351 | * causes the loop to be exited. |
3352 | */ |
3353 | if (len <= 0) { |
3354 | + /* Job process has ended and we've drained all the data the job |
3355 | + * produced, so remote end must have closed. |
3356 | + * |
3357 | + * This cannot be handled entirely by log_io_error_handler() |
3358 | + * since the job may produce some output prior to disks being |
3359 | + * writeable, then end without producing further output. |
3360 | + * In this scenario the error handler is never called. |
3361 | + * |
3362 | + */ |
3363 | + if (saved && saved != EAGAIN && saved != EWOULDBLOCK) |
3364 | + log->remote_closed = 1; |
3365 | + |
3366 | close (log->fd); |
3367 | log->fd = -1; |
3368 | break; |
3369 | } |
3370 | } |
3371 | - |
3372 | - /* Job process has ended and we've drained all the data the job |
3373 | - * produced, so remote end must have closed. |
3374 | - * |
3375 | - * This cannot be handled entirely by log_io_error_handler() |
3376 | - * since the job may produce some output prior to disks being |
3377 | - * writeable, then end without producing further output. |
3378 | - * In this scenario the error handler is never called. |
3379 | - * |
3380 | - */ |
3381 | - log->remote_closed = 1; |
3382 | } |
3383 | |
3384 | /** |
3385 | @@ -810,3 +818,193 @@ |
3386 | |
3387 | return 0; |
3388 | } |
3389 | + |
3390 | +/** |
3391 | + * log_serialise: |
3392 | + * @log: log to serialise. |
3393 | + * |
3394 | + * Convert @log into a JSON representation for serialisation. |
3395 | + * Caller must free returned value using json_object_put(). |
3396 | + * |
3397 | + * Returns: JSON-serialised Log object, or NULL on error. |
3398 | + **/ |
3399 | +json_object * |
3400 | +log_serialise (Log *log) |
3401 | +{ |
3402 | + json_object *json; |
3403 | + nih_local char *unflushed_hex = NULL; |
3404 | + |
3405 | + json = json_object_new_object (); |
3406 | + if (! json) |
3407 | + return NULL; |
3408 | + |
3409 | + if (! log || (! log->io && log->unflushed && ! log->unflushed->len)) { |
3410 | + /* Create a "placeholder" log object for non-existent |
3411 | + * log objects and for those that are no longer usable. |
3412 | + */ |
3413 | + if (! state_set_json_string_var (json, "path", NULL)) |
3414 | + goto error; |
3415 | + return json; |
3416 | + } |
3417 | + |
3418 | + /* Attempt to flush any cached data */ |
3419 | + if (log->unflushed->len) { |
3420 | + /* Don't check return values since if this fails and |
3421 | + * unflushed data remains, we encode it below. |
3422 | + */ |
3423 | + if (log->fd < 0) |
3424 | + (void)log_file_open (log); |
3425 | + if (log->fd != -1) |
3426 | + (void)log_file_write (log, NULL, 0); |
3427 | + } |
3428 | + |
3429 | + nih_assert (log->io); |
3430 | + |
3431 | + if (! state_set_json_int_var_from_obj (json, log, fd)) |
3432 | + goto error; |
3433 | + |
3434 | + nih_assert (log->io->watch); |
3435 | + |
3436 | + if (! state_set_json_int_var (json, "io_watch_fd", log->io->watch->fd)) |
3437 | + goto error; |
3438 | + |
3439 | + if (! state_set_json_string_var_from_obj (json, log, path)) |
3440 | + goto error; |
3441 | + |
3442 | + /* log->io itself is not encoded */ |
3443 | + |
3444 | + if (! state_set_json_int_var_from_obj (json, log, uid)) |
3445 | + goto error; |
3446 | + |
3447 | + /* Encode unflushed data as hex to ensure any embedded |
3448 | + * nulls are handled. |
3449 | + */ |
3450 | + if (log->unflushed->len) { |
3451 | + unflushed_hex = state_data_to_hex (NULL, |
3452 | + log->unflushed->buf, |
3453 | + log->unflushed->len); |
3454 | + |
3455 | + if (! unflushed_hex) |
3456 | + goto error; |
3457 | + |
3458 | + if (! state_set_json_string_var (json, "unflushed", unflushed_hex)) |
3459 | + goto error; |
3460 | + } |
3461 | + |
3462 | + if (! state_set_json_int_var_from_obj (json, log, detached)) |
3463 | + goto error; |
3464 | + |
3465 | + if (! state_set_json_int_var_from_obj (json, log, remote_closed)) |
3466 | + goto error; |
3467 | + |
3468 | + if (! state_set_json_int_var_from_obj (json, log, open_errno)) |
3469 | + goto error; |
3470 | + |
3471 | + return json; |
3472 | + |
3473 | +error: |
3474 | + json_object_put (json); |
3475 | + return NULL; |
3476 | +} |
3477 | + |
3478 | +/** |
3479 | + * log_deserialise: |
3480 | + * @json: JSON-serialised Log object to deserialise. |
3481 | + * |
3482 | + * Convert @json into a Log object. |
3483 | + * |
3484 | + * Returns: Log object, or NULL on error. |
3485 | + **/ |
3486 | +Log * |
3487 | +log_deserialise (const void *parent, |
3488 | + json_object *json) |
3489 | +{ |
3490 | + Log *log; |
3491 | + nih_local char *unflushed_hex = NULL; |
3492 | + nih_local char *unflushed = NULL; |
3493 | + int ret; |
3494 | + size_t len; |
3495 | + json_object *json_unflushed; |
3496 | + nih_local char *path = NULL; |
3497 | + int io_watch_fd = -1; |
3498 | + uid_t uid = (uid_t)-1; |
3499 | + |
3500 | + nih_assert (json); |
3501 | + |
3502 | + log_unflushed_init (); |
3503 | + |
3504 | + if (! state_check_json_type (json, object)) |
3505 | + return NULL; |
3506 | + |
3507 | + if (! state_get_json_string_var (json, "path", NULL, path)) |
3508 | + return NULL; |
3509 | + |
3510 | + if (! path) { |
3511 | + /* placeholder log object */ |
3512 | + return NULL; |
3513 | + } |
3514 | + |
3515 | + if (! state_get_json_int_var (json, "io_watch_fd", io_watch_fd)) |
3516 | + return NULL; |
3517 | + |
3518 | + nih_assert (io_watch_fd != -1); |
3519 | + |
3520 | + /* re-apply CLOEXEC flag to stop job fd being leaked to children */ |
3521 | + if (state_toggle_cloexec (io_watch_fd, TRUE) < 0) |
3522 | + return NULL; |
3523 | + |
3524 | + if (! state_get_json_int_var (json, "uid", uid)) |
3525 | + return NULL; |
3526 | + |
3527 | + log = log_new (parent, path, io_watch_fd, uid); |
3528 | + if (! log) |
3529 | + return NULL; |
3530 | + |
3531 | + if (! state_get_json_int_var_to_obj (json, log, fd)) |
3532 | + goto error; |
3533 | + |
3534 | + /* Re-apply CLOEXEC flag to stop log file fd being leaked to children. |
3535 | + * |
3536 | + * Note we discard return value since if this fails, |
3537 | + * we would never close the fd. |
3538 | + */ |
3539 | + if (log->fd != -1) |
3540 | + (void)state_toggle_cloexec (log->fd, TRUE); |
3541 | + |
3542 | + log->unflushed = nih_io_buffer_new (log); |
3543 | + if (! log->unflushed) |
3544 | + goto error; |
3545 | + |
3546 | + json_unflushed = json_object_object_get (json, "unflushed"); |
3547 | + if (json_unflushed) { |
3548 | + if (! state_get_json_string_var_strict (json, "unflushed", NULL, unflushed_hex)) |
3549 | + goto error; |
3550 | + |
3551 | + ret = state_hex_to_data (NULL, |
3552 | + unflushed_hex, |
3553 | + strlen (unflushed_hex), |
3554 | + &unflushed, |
3555 | + &len); |
3556 | + |
3557 | + if (ret < 0) |
3558 | + goto error; |
3559 | + |
3560 | + if (nih_io_buffer_push (log->unflushed, unflushed, len) < 0) |
3561 | + goto error; |
3562 | + } |
3563 | + |
3564 | + if (! state_get_json_int_var_to_obj (json, log, detached)) |
3565 | + goto error; |
3566 | + |
3567 | + if (! state_get_json_int_var_to_obj (json, log, remote_closed)) |
3568 | + goto error; |
3569 | + |
3570 | + if (! state_get_json_int_var_to_obj (json, log, open_errno)) |
3571 | + goto error; |
3572 | + |
3573 | + return log; |
3574 | + |
3575 | +error: |
3576 | + nih_free (log); |
3577 | + return NULL; |
3578 | +} |
3579 | |
3580 | === modified file 'init/log.h' |
3581 | --- init/log.h 2012-02-14 16:47:34 +0000 |
3582 | +++ init/log.h 2012-11-14 14:49:20 +0000 |
3583 | @@ -29,6 +29,8 @@ |
3584 | #include <nih/logging.h> |
3585 | #include <nih/error.h> |
3586 | |
3587 | +#include "state.h" |
3588 | + |
3589 | /** LOG_DEFAULT_UMASK: |
3590 | * |
3591 | * The default file creation mask for log files. |
3592 | @@ -55,7 +57,7 @@ |
3593 | * @io: NihIo associated with jobs stdout and stderr, |
3594 | * @uid: User ID of caller, |
3595 | * @unflushed: Unflushed data, |
3596 | - * @detached: TRUE if log is no longer associated with a parent,(job), |
3597 | + * @detached: TRUE if log is no longer associated with a parent (job), |
3598 | * @remote_closed: TRUE if remote end of pty has been closed, |
3599 | * @open_errno: value of errno immediately after last attempt to open @path. |
3600 | **/ |
3601 | @@ -79,11 +81,17 @@ |
3602 | __attribute__ ((warn_unused_result, malloc)); |
3603 | void log_io_reader (Log *log, NihIo *io, const char *buf, size_t len); |
3604 | void log_io_error_handler (Log *log, NihIo *io); |
3605 | -int log_destroy (Log *log); |
3606 | -int log_handle_unflushed (void *parent, Log *log); |
3607 | -int log_clear_unflushed (void); |
3608 | +int log_destroy (Log *log) |
3609 | + __attribute__ ((warn_unused_result)); |
3610 | +int log_handle_unflushed (void *parent, Log *log) |
3611 | + __attribute__ ((warn_unused_result)); |
3612 | +int log_clear_unflushed (void) |
3613 | + __attribute__ ((warn_unused_result)); |
3614 | void log_unflushed_init (void); |
3615 | - |
3616 | +json_object * log_serialise (Log *log) |
3617 | + __attribute__ ((warn_unused_result, malloc)); |
3618 | +Log * log_deserialise (const void *parent, json_object *json) |
3619 | + __attribute__ ((warn_unused_result, malloc)); |
3620 | |
3621 | NIH_END_EXTERN |
3622 | |
3623 | |
3624 | === modified file 'init/main.c' |
3625 | --- init/main.c 2012-08-07 16:39:00 +0000 |
3626 | +++ init/main.c 2012-11-14 14:49:20 +0000 |
3627 | @@ -60,12 +60,16 @@ |
3628 | #include "event.h" |
3629 | #include "conf.h" |
3630 | #include "control.h" |
3631 | +#include "state.h" |
3632 | |
3633 | |
3634 | /* Prototypes for static functions */ |
3635 | #ifndef DEBUG |
3636 | static int logger_kmsg (NihLogLevel priority, const char *message); |
3637 | static void crash_handler (int signum); |
3638 | +#endif /* DEBUG */ |
3639 | +static void term_handler (void *data, NihSignal *signal); |
3640 | +#ifndef DEBUG |
3641 | static void cad_handler (void *data, NihSignal *signal); |
3642 | static void kbd_handler (void *data, NihSignal *signal); |
3643 | static void pwr_handler (void *data, NihSignal *signal); |
3644 | @@ -79,20 +83,12 @@ |
3645 | |
3646 | |
3647 | /** |
3648 | - * argv0: |
3649 | - * |
3650 | - * Path to program executed, used for re-executing the init binary from the |
3651 | - * same location we were executed from. |
3652 | - **/ |
3653 | -static const char *argv0 = NULL; |
3654 | - |
3655 | -/** |
3656 | - * restart: |
3657 | - * |
3658 | - * This is set to TRUE if we're being re-exec'd by an existing init |
3659 | - * process. |
3660 | - **/ |
3661 | -static int restart = FALSE; |
3662 | + * state_fd: |
3663 | + * |
3664 | + * File descriptor to read serialised state from when performing |
3665 | + * stateful re-exec. If value is not -1, attempt stateful re-exec. |
3666 | + **/ |
3667 | +static int state_fd = -1; |
3668 | |
3669 | /** |
3670 | * conf_dir: |
3671 | @@ -147,7 +143,13 @@ |
3672 | { 0, "no-startup-event", N_("do not emit any startup event (for testing)"), |
3673 | NULL, NULL, &disable_startup_event, NULL }, |
3674 | |
3675 | - { 0, "restart", NULL, NULL, NULL, &restart, NULL }, |
3676 | + /* Must be specified for both stateful and stateless re-exec */ |
3677 | + { 0, "restart", N_("flag a re-exec has occurred"), |
3678 | + NULL, NULL, &restart, NULL }, |
3679 | + |
3680 | + /* Required for stateful re-exec */ |
3681 | + { 0, "state-fd", N_("specify file descriptor to read serialisation data from"), |
3682 | + NULL, "FD", &state_fd, nih_option_int }, |
3683 | |
3684 | { 0, "session", N_("use D-Bus session bus rather than system bus (for testing)"), |
3685 | NULL, NULL, &use_session_bus, NULL }, |
3686 | @@ -166,11 +168,12 @@ |
3687 | main (int argc, |
3688 | char *argv[]) |
3689 | { |
3690 | - char **args; |
3691 | + char **args = NULL; |
3692 | int ret; |
3693 | |
3694 | - argv0 = argv[0]; |
3695 | - nih_main_init (argv0); |
3696 | + args_copy = NIH_MUST (nih_str_array_copy (NULL, NULL, argv)); |
3697 | + |
3698 | + nih_main_init (args_copy[0]); |
3699 | |
3700 | nih_option_set_synopsis (_("Process management daemon.")); |
3701 | nih_option_set_help ( |
3702 | @@ -295,8 +298,9 @@ |
3703 | */ |
3704 | if (system_setup_console (CONSOLE_OUTPUT, (! restart)) < 0) { |
3705 | NihError *err; |
3706 | - |
3707 | + |
3708 | err = nih_error_get (); |
3709 | + |
3710 | nih_warn ("%s: %s", _("Unable to initialize console, will try /dev/null"), |
3711 | err->message); |
3712 | nih_free (err); |
3713 | @@ -411,7 +415,16 @@ |
3714 | /* SIGUSR1 instructs us to reconnect to D-Bus */ |
3715 | nih_signal_set_handler (SIGUSR1, nih_signal_handler); |
3716 | NIH_MUST (nih_signal_add_handler (NULL, SIGUSR1, usr1_handler, NULL)); |
3717 | + |
3718 | } |
3719 | + |
3720 | + /* SIGTERM instructs us to re-exec ourselves; this should be the |
3721 | + * last in the list to ensure that all other signals are handled |
3722 | + * before a SIGTERM. |
3723 | + */ |
3724 | + nih_signal_set_handler (SIGTERM, nih_signal_handler); |
3725 | + NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, term_handler, NULL)); |
3726 | + |
3727 | #endif /* DEBUG */ |
3728 | |
3729 | |
3730 | @@ -456,6 +469,48 @@ |
3731 | } |
3732 | |
3733 | |
3734 | + if (restart) { |
3735 | + if (state_fd == -1) { |
3736 | + nih_warn ("%s", |
3737 | + _("Stateful re-exec supported but stateless re-exec requested")); |
3738 | + } else if (state_read (state_fd) < 0) { |
3739 | + nih_local char *arg = NULL; |
3740 | + |
3741 | + /* Stateful re-exec has failed so try once more by |
3742 | + * degrading to stateless re-exec, which even in |
3743 | + * the case of low-memory scenarios will work. |
3744 | + */ |
3745 | + |
3746 | + /* Inform the child we've given up on stateful |
3747 | + * re-exec. |
3748 | + */ |
3749 | + close (state_fd); |
3750 | + |
3751 | + nih_error ("%s - %s", |
3752 | + _("Failed to read serialisation data"), |
3753 | + _("reverting to stateless re-exec")); |
3754 | + |
3755 | + /* Remove any existing (but now stale) state fd |
3756 | + * args which will effectively disable stateful |
3757 | + * re-exec. |
3758 | + */ |
3759 | + clean_args (&args_copy); |
3760 | + |
3761 | + /* Attempt stateless re-exec */ |
3762 | + perform_reexec (); |
3763 | + |
3764 | + nih_error ("%s", |
3765 | + _("Both stateful and stateless re-execs failed")); |
3766 | + |
3767 | + /* Out of options */ |
3768 | + nih_assert_not_reached (); |
3769 | + } else { |
3770 | + close (state_fd); |
3771 | + |
3772 | + nih_info ("Stateful re-exec completed"); |
3773 | + } |
3774 | + } |
3775 | + |
3776 | /* Read configuration */ |
3777 | NIH_MUST (conf_source_new (NULL, CONFFILE, CONF_FILE)); |
3778 | NIH_MUST (conf_source_new (NULL, conf_dir, CONF_JOB_DIR)); |
3779 | @@ -479,7 +534,8 @@ |
3780 | } |
3781 | |
3782 | /* Open connection to the appropriate D-Bus bus; we normally expect this to |
3783 | - * fail and will try again later - don't let ENOMEM stop us though. |
3784 | + * fail (since dbus-daemon probably isn't running yet) and will try again |
3785 | + * later - don't let ENOMEM stop us though. |
3786 | */ |
3787 | while (control_bus_open () < 0) { |
3788 | NihError *err; |
3789 | @@ -529,9 +585,17 @@ |
3790 | } |
3791 | |
3792 | } else { |
3793 | - sigset_t mask; |
3794 | - |
3795 | - /* We're ok to receive signals again */ |
3796 | + sigset_t mask; |
3797 | + |
3798 | + /* We have been re-exec'd. Don't emit an initial event |
3799 | + * as only Upstart is restarting - we don't want to restart |
3800 | + * the system (another reason being that we don't yet support |
3801 | + * upstart-in-initramfs to upstart-in-root-filesystem |
3802 | + * state-passing transitions). |
3803 | + */ |
3804 | + |
3805 | + /* We're ok to receive signals again so restore signals |
3806 | + * disabled by the term_handler */ |
3807 | sigemptyset (&mask); |
3808 | sigprocmask (SIG_SETMASK, &mask, NULL); |
3809 | } |
3810 | @@ -631,7 +695,7 @@ |
3811 | { |
3812 | pid_t pid; |
3813 | |
3814 | - nih_assert (argv0 != NULL); |
3815 | + nih_assert (args_copy[0] != NULL); |
3816 | |
3817 | pid = fork (); |
3818 | if (pid == 0) { |
3819 | @@ -685,7 +749,30 @@ |
3820 | /* Goodbye, cruel world. */ |
3821 | exit (signum); |
3822 | } |
3823 | - |
3824 | +#endif |
3825 | + |
3826 | +/** |
3827 | + * term_handler: |
3828 | + * @data: unused, |
3829 | + * @signal: signal caught. |
3830 | + * |
3831 | + * This is called when we receive the TERM signal, which instructs us |
3832 | + * to reexec ourselves. |
3833 | + **/ |
3834 | +static void |
3835 | +term_handler (void *data, |
3836 | + NihSignal *signal) |
3837 | +{ |
3838 | + nih_assert (args_copy[0] != NULL); |
3839 | + nih_assert (signal != NULL); |
3840 | + |
3841 | + nih_warn (_("Re-executing %s"), args_copy[0]); |
3842 | + |
3843 | + stateful_reexec (); |
3844 | +} |
3845 | + |
3846 | + |
3847 | +#ifndef DEBUG |
3848 | /** |
3849 | * cad_handler: |
3850 | * @data: unused, |
3851 | @@ -835,7 +922,7 @@ |
3852 | * NihOption setter function to handle selection of default console |
3853 | * type. |
3854 | * |
3855 | - * Returns 0 on success, -1 on invalid console type. |
3856 | + * Returns: 0 on success, -1 on invalid console type. |
3857 | **/ |
3858 | static int |
3859 | console_type_setter (NihOption *option, const char *arg) |
3860 | @@ -851,3 +938,4 @@ |
3861 | |
3862 | return 0; |
3863 | } |
3864 | + |
3865 | |
3866 | === modified file 'init/parse_job.c' |
3867 | --- init/parse_job.c 2012-10-22 13:29:45 +0000 |
3868 | +++ init/parse_job.c 2012-11-14 14:49:20 +0000 |
3869 | @@ -37,7 +37,6 @@ |
3870 | #include <nih/string.h> |
3871 | #include <nih/list.h> |
3872 | #include <nih/signal.h> |
3873 | -#include <nih/config.h> |
3874 | #include <nih/logging.h> |
3875 | #include <nih/error.h> |
3876 | |
3877 | @@ -625,6 +624,48 @@ |
3878 | } |
3879 | |
3880 | /** |
3881 | + * parse_on_simple: |
3882 | + * @class: job class being parsed, |
3883 | + * @stanza_name: name of stanza type to parse ("start" or "stop"), |
3884 | + * @string: string to parse. |
3885 | + * |
3886 | + * Parse either a "start" or "stop" condition from @string (which must |
3887 | + * start with the first byte beyond either "start on" or "stop on". |
3888 | + * |
3889 | + * Returns: EventOperator at root of expression tree on success, NULL |
3890 | + * on raised error. |
3891 | + **/ |
3892 | +EventOperator * |
3893 | +parse_on_simple (JobClass *class, const char *stanza_name, const char *string) |
3894 | +{ |
3895 | + EventOperator *root = NULL; |
3896 | + NihConfigStanza *stanza = NULL; |
3897 | + size_t pos = 0; |
3898 | + size_t lineno = 0; |
3899 | + size_t len; |
3900 | + |
3901 | + nih_assert (class); |
3902 | + nih_assert (stanza_name); |
3903 | + nih_assert (string); |
3904 | + |
3905 | + /* Find the appropriate config stanza */ |
3906 | + for (NihConfigStanza *s = stanzas; s->name; s++) { |
3907 | + if (! strcmp (stanza_name, s->name)) { |
3908 | + stanza = s; |
3909 | + break; |
3910 | + } |
3911 | + } |
3912 | + |
3913 | + nih_assert (stanza); |
3914 | + |
3915 | + len = strlen (string); |
3916 | + |
3917 | + root = parse_on (class, stanza, string, len, &pos, &lineno); |
3918 | + |
3919 | + return root; |
3920 | +} |
3921 | + |
3922 | +/** |
3923 | * parse_on_operator: |
3924 | * @class: job class being parsed, |
3925 | * @stanza: stanza found, |
3926 | |
3927 | === modified file 'init/parse_job.h' |
3928 | --- init/parse_job.h 2011-06-06 12:52:08 +0000 |
3929 | +++ init/parse_job.h 2012-11-14 14:49:20 +0000 |
3930 | @@ -21,6 +21,7 @@ |
3931 | #define INIT_PARSE_JOB_H |
3932 | |
3933 | #include <nih/macros.h> |
3934 | +#include <nih/config.h> |
3935 | |
3936 | #include "session.h" |
3937 | #include "job_class.h" |
3938 | @@ -34,6 +35,10 @@ |
3939 | size_t *pos, size_t *lineno) |
3940 | __attribute__ ((warn_unused_result, malloc)); |
3941 | |
3942 | +EventOperator * |
3943 | +parse_on_simple (JobClass *class, const char *stanza_name, |
3944 | + const char *string) |
3945 | + __attribute__ ((warn_unused_result, malloc)); |
3946 | NIH_END_EXTERN |
3947 | |
3948 | #endif /* INIT_PARSE_JOB_H */ |
3949 | |
3950 | === modified file 'init/process.c' |
3951 | --- init/process.c 2009-06-23 09:29:35 +0000 |
3952 | +++ init/process.c 2012-11-14 14:49:20 +0000 |
3953 | @@ -29,8 +29,10 @@ |
3954 | #include <nih/macros.h> |
3955 | #include <nih/alloc.h> |
3956 | #include <nih/logging.h> |
3957 | +#include <nih/string.h> |
3958 | |
3959 | #include "process.h" |
3960 | +#include "state.h" |
3961 | |
3962 | |
3963 | /** |
3964 | @@ -116,3 +118,224 @@ |
3965 | return -1; |
3966 | } |
3967 | } |
3968 | + |
3969 | +/** |
3970 | + * process_serialise: |
3971 | + * @process: process to serialise. |
3972 | + * |
3973 | + * Convert @process into a JSON representation for serialisation. |
3974 | + * Caller must free returned value using json_object_put(). |
3975 | + * |
3976 | + * Returns: JSON-serialised Process object, or NULL on error. |
3977 | + **/ |
3978 | +json_object * |
3979 | +process_serialise (const Process *process) |
3980 | +{ |
3981 | + json_object *json; |
3982 | + |
3983 | + nih_assert (process); |
3984 | + |
3985 | + json = json_object_new_object (); |
3986 | + if (! json) |
3987 | + return NULL; |
3988 | + |
3989 | + if (! state_set_json_int_var_from_obj (json, process, script)) |
3990 | + goto error; |
3991 | + |
3992 | + if (! state_set_json_string_var_from_obj (json, process, command)) |
3993 | + goto error; |
3994 | + |
3995 | + return json; |
3996 | + |
3997 | +error: |
3998 | + json_object_put (json); |
3999 | + return NULL; |
4000 | + |
4001 | +} |
4002 | + |
4003 | +/** |
4004 | + * process_serialise_all: |
4005 | + * |
4006 | + * @processes: array of Processes. |
4007 | + * |
4008 | + * Convert array of Process objects to JSON representation. |
4009 | + * |
4010 | + * Returns: JSON object containing array of Processes, or NULL on error. |
4011 | + */ |
4012 | +json_object * |
4013 | +process_serialise_all (const Process * const * const processes) |
4014 | +{ |
4015 | + json_object *json; |
4016 | + json_object *json_process; |
4017 | + Process dummy = { 0, NULL }; |
4018 | + |
4019 | + nih_assert (processes); |
4020 | + |
4021 | + json = json_object_new_array (); |
4022 | + if (! json) |
4023 | + return NULL; |
4024 | + |
4025 | + for (int i = 0; i < PROCESS_LAST; i++) { |
4026 | + /* We must encode a blank entry for missing array elements |
4027 | + * to ensure correct deserialisation. |
4028 | + */ |
4029 | + json_process = process_serialise (processes[i] |
4030 | + ? processes[i] |
4031 | + : &dummy); |
4032 | + |
4033 | + if (! json_process) |
4034 | + goto error; |
4035 | + |
4036 | + if (json_object_array_add (json, json_process) < 0) |
4037 | + goto error; |
4038 | + } |
4039 | + |
4040 | + return json; |
4041 | + |
4042 | +error: |
4043 | + json_object_put (json); |
4044 | + return NULL; |
4045 | +} |
4046 | + |
4047 | +/** |
4048 | + * process_deserialise: |
4049 | + * @json: JSON-serialised Process object to deserialise. |
4050 | + * |
4051 | + * Convert @json into a Process object. |
4052 | + * |
4053 | + * Returns: Process object, or NULL on error. |
4054 | + **/ |
4055 | +Process * |
4056 | +process_deserialise (json_object *json, const void *parent) |
4057 | +{ |
4058 | + Process *process = NULL; |
4059 | + |
4060 | + nih_assert (json); |
4061 | + |
4062 | + if (! state_check_json_type (json, object)) |
4063 | + goto error; |
4064 | + |
4065 | + process = NIH_MUST (process_new (parent)); |
4066 | + |
4067 | + if (! state_get_json_int_var_to_obj (json, process, script)) |
4068 | + goto error; |
4069 | + |
4070 | + if (! state_get_json_string_var_to_obj (json, process, command)) |
4071 | + goto error; |
4072 | + |
4073 | + /* All Process slots have to be serialised in the JSON to |
4074 | + * guarantee ordering on deserialisation. |
4075 | + * |
4076 | + * However, here we've found a Process that was merely |
4077 | + * an ordering placeholder - no command has been defined, |
4078 | + * so ignore it. |
4079 | + */ |
4080 | + if (! process->command) |
4081 | + goto error; |
4082 | + |
4083 | + return process; |
4084 | + |
4085 | +error: |
4086 | + nih_free (process); |
4087 | + return NULL; |
4088 | +} |
4089 | + |
4090 | +/** |
4091 | + * process_deserialise_all: |
4092 | + * |
4093 | + * @json: root of JSON-serialised state, |
4094 | + * @parent: parent of @processes, |
4095 | + * @processes: newly-allocated array of Process objects. |
4096 | + * |
4097 | + * Convert JSON representation of processes back into |
4098 | + * an array of Process objects. |
4099 | + * |
4100 | + * Returns: 0 on success, -1 on error. |
4101 | + **/ |
4102 | +int |
4103 | +process_deserialise_all (json_object *json, const void *parent, |
4104 | + Process **processes) |
4105 | +{ |
4106 | + json_object *json_processes; |
4107 | + int i; |
4108 | + |
4109 | + nih_assert (json); |
4110 | + nih_assert (parent); |
4111 | + nih_assert (processes); |
4112 | + |
4113 | + json_processes = json_object_object_get (json, "process"); |
4114 | + |
4115 | + if (! json_processes) |
4116 | + goto error; |
4117 | + |
4118 | + if (! state_check_json_type (json_processes, array)) |
4119 | + goto error; |
4120 | + |
4121 | + for (i = 0; i < json_object_array_length (json_processes); i++) { |
4122 | + json_object *json_process; |
4123 | + |
4124 | + nih_assert (i <= PROCESS_LAST); |
4125 | + |
4126 | + json_process = json_object_array_get_idx (json_processes, i); |
4127 | + if (! json_process) |
4128 | + goto error; |
4129 | + |
4130 | + if (! state_check_json_type (json_process, object)) |
4131 | + goto error; |
4132 | + |
4133 | + processes[i] = process_deserialise (json_process, parent); |
4134 | + |
4135 | + } |
4136 | + |
4137 | + return 0; |
4138 | + |
4139 | +error: |
4140 | + return -1; |
4141 | +} |
4142 | + |
4143 | +/** |
4144 | + * process_type_enum_to_str: |
4145 | + * |
4146 | + * @type: ProcessType. |
4147 | + * |
4148 | + * Convert ProcessType to a string representation. |
4149 | + * |
4150 | + * Returns: string representation of @type, or NULL if not known. |
4151 | + **/ |
4152 | +const char * |
4153 | +process_type_enum_to_str (ProcessType type) |
4154 | +{ |
4155 | + state_enum_to_str (PROCESS_INVALID, type); |
4156 | + state_enum_to_str (PROCESS_MAIN, type); |
4157 | + state_enum_to_str (PROCESS_PRE_START, type); |
4158 | + state_enum_to_str (PROCESS_POST_START, type); |
4159 | + state_enum_to_str (PROCESS_PRE_STOP, type); |
4160 | + state_enum_to_str (PROCESS_POST_STOP, type); |
4161 | + |
4162 | + return NULL; |
4163 | +} |
4164 | + |
4165 | +/** |
4166 | + * process_type_str_to_enum: |
4167 | + * |
4168 | + * @type: string ProcessType. |
4169 | + * |
4170 | + * Convert @type back into enum value. |
4171 | + * |
4172 | + * Returns: ProcessType representation of @type, or -1 if not known. |
4173 | + **/ |
4174 | +ProcessType |
4175 | +process_type_str_to_enum (const char *type) |
4176 | +{ |
4177 | + nih_assert (type); |
4178 | + |
4179 | + state_str_to_enum (PROCESS_INVALID, type); |
4180 | + state_str_to_enum (PROCESS_MAIN, type); |
4181 | + state_str_to_enum (PROCESS_PRE_START, type); |
4182 | + state_str_to_enum (PROCESS_POST_START, type); |
4183 | + state_str_to_enum (PROCESS_PRE_STOP, type); |
4184 | + state_str_to_enum (PROCESS_POST_STOP, type); |
4185 | + |
4186 | + return -1; |
4187 | +} |
4188 | + |
4189 | |
4190 | === modified file 'init/process.h' |
4191 | --- init/process.h 2009-06-23 09:29:35 +0000 |
4192 | +++ init/process.h 2012-11-14 14:49:20 +0000 |
4193 | @@ -22,20 +22,30 @@ |
4194 | |
4195 | #include <nih/macros.h> |
4196 | |
4197 | +#include <json.h> |
4198 | |
4199 | /** |
4200 | * ProcessType: |
4201 | * |
4202 | * This is used to enumerate the array of process definitions attached to |
4203 | * a job class, and the array of pids attached to a job instance. |
4204 | + * |
4205 | + * Note that PROCESS_INVALID would ideally be -1 but that isn't possible |
4206 | + * since process_type_str_to_enum() would then not be able to distinguish |
4207 | + * between an invalid ProcessType and the default value assigned to a |
4208 | + * ProcessType. It also cannot be zero since that would upset iterating |
4209 | + * through the (non-invalid) entries. |
4210 | **/ |
4211 | typedef enum process_type { |
4212 | - PROCESS_MAIN, |
4213 | + /* initial value denoting no process */ |
4214 | + PROCESS_INVALID = -2, |
4215 | + |
4216 | + PROCESS_MAIN = 0, |
4217 | PROCESS_PRE_START, |
4218 | PROCESS_POST_START, |
4219 | PROCESS_PRE_STOP, |
4220 | PROCESS_POST_STOP, |
4221 | - PROCESS_LAST |
4222 | + PROCESS_LAST, |
4223 | } ProcessType; |
4224 | |
4225 | |
4226 | @@ -65,7 +75,32 @@ |
4227 | |
4228 | const char *process_name (ProcessType process) |
4229 | __attribute__ ((const)); |
4230 | -ProcessType process_from_name (const char *process); |
4231 | + |
4232 | +json_object *process_serialise (const Process *process) |
4233 | + __attribute__ ((malloc, warn_unused_result)); |
4234 | + |
4235 | +Process *process_deserialise (json_object *json, const void *parent) |
4236 | + __attribute__ ((malloc, warn_unused_result)); |
4237 | + |
4238 | +json_object * |
4239 | +process_serialise_all (const Process * const * const processes) |
4240 | + __attribute__ ((malloc, warn_unused_result)); |
4241 | + |
4242 | +ProcessType process_from_name (const char *process) |
4243 | + __attribute__ ((warn_unused_result)); |
4244 | + |
4245 | +int |
4246 | +process_deserialise_all (json_object *json, const void *parent, |
4247 | + Process **processes) |
4248 | + __attribute__ ((warn_unused_result)); |
4249 | + |
4250 | +const char * |
4251 | +process_type_enum_to_str (ProcessType type) |
4252 | + __attribute__ ((warn_unused_result)); |
4253 | + |
4254 | +ProcessType |
4255 | +process_type_str_to_enum (const char *type) |
4256 | + __attribute__ ((warn_unused_result)); |
4257 | |
4258 | NIH_END_EXTERN |
4259 | |
4260 | |
4261 | === modified file 'init/session.c' |
4262 | --- init/session.c 2011-12-09 14:07:11 +0000 |
4263 | +++ init/session.c 2012-11-14 14:49:20 +0000 |
4264 | @@ -47,6 +47,15 @@ |
4265 | #include "conf.h" |
4266 | #include "paths.h" |
4267 | |
4268 | +extern json_object *json_sessions; |
4269 | + |
4270 | +/* Prototypes for static functions */ |
4271 | +static json_object *session_serialise (const Session *session) |
4272 | + __attribute__ ((malloc, warn_unused_result)); |
4273 | + |
4274 | +static Session *session_deserialise (json_object *json) |
4275 | + __attribute__ ((malloc, warn_unused_result)); |
4276 | + |
4277 | |
4278 | /** |
4279 | * sessions: |
4280 | @@ -66,8 +75,7 @@ |
4281 | |
4282 | |
4283 | /* Prototypes for static functions */ |
4284 | -static void session_create_conf_source (Session *sesson); |
4285 | - |
4286 | +static void session_create_conf_source (Session *sesson, int deserialised); |
4287 | |
4288 | /** |
4289 | * session_init: |
4290 | @@ -90,7 +98,7 @@ |
4291 | * |
4292 | * Create a new session. |
4293 | * |
4294 | - * Return new Session, or NULL on error. |
4295 | + * Returns: new Session, or NULL on error. |
4296 | **/ |
4297 | Session * |
4298 | session_new (const void *parent, |
4299 | @@ -137,7 +145,7 @@ |
4300 | * |
4301 | * Create a new session, based on the specified D-Bus message. |
4302 | * |
4303 | - * Return new Session, or NULL on error. |
4304 | + * Returns: new Session, or NULL on error. |
4305 | **/ |
4306 | Session * |
4307 | session_from_dbus (const void *parent, |
4308 | @@ -248,7 +256,7 @@ |
4309 | continue; |
4310 | |
4311 | /* Found a user with the same uid but different |
4312 | - * conf_dir to the existing session user. Either the |
4313 | + * conf_path to the existing session user. Either the |
4314 | * original user has been deleted and a new user created |
4315 | * with the same uid, or the original users home |
4316 | * directory has changed since they first started |
4317 | @@ -273,7 +281,7 @@ |
4318 | } |
4319 | |
4320 | if (! session->conf_path) |
4321 | - session_create_conf_source (session); |
4322 | + session_create_conf_source (session, FALSE); |
4323 | |
4324 | return session; |
4325 | } |
4326 | @@ -282,46 +290,54 @@ |
4327 | /* Didn't find one, make a new one */ |
4328 | session = NIH_MUST (session_new (parent, unix_process_id ? root : NULL, |
4329 | unix_user)); |
4330 | - session_create_conf_source (session); |
4331 | + session_create_conf_source (session, FALSE); |
4332 | |
4333 | return session; |
4334 | } |
4335 | |
4336 | /** |
4337 | * session_create_conf_source: |
4338 | - * @session: Session. |
4339 | + * @session: Session, |
4340 | + * @deserialised: TRUE if ConfSource is to be created from a deserialised |
4341 | + * session object. |
4342 | * |
4343 | - * Create a new ConfSouce object and associate the specified session |
4344 | + * Create a new ConfSource object and associate the specified Session |
4345 | * with it. |
4346 | **/ |
4347 | static void |
4348 | -session_create_conf_source (Session *session) |
4349 | +session_create_conf_source (Session *session, int deserialised) |
4350 | { |
4351 | ConfSource *source; |
4352 | |
4353 | nih_assert (session != NULL); |
4354 | - nih_assert (session->conf_path == NULL); |
4355 | - |
4356 | - if (session->chroot) |
4357 | - session->conf_path = NIH_MUST (nih_strdup (NULL, session->chroot)); |
4358 | - if (session->user) { |
4359 | - struct passwd *pwd; |
4360 | - |
4361 | - pwd = getpwuid (session->user); |
4362 | - if (! pwd) { |
4363 | - nih_error ("%d: %s: %s", session->user, |
4364 | - _("Unable to lookup home directory"), |
4365 | - strerror (errno)); |
4366 | - |
4367 | - nih_free (session->conf_path); |
4368 | - session->conf_path = NULL; |
4369 | - return; |
4370 | + nih_assert (deserialised |
4371 | + ? session->conf_path != NULL |
4372 | + : session->conf_path == NULL); |
4373 | + |
4374 | + session_init (); |
4375 | + |
4376 | + if (! deserialised) { |
4377 | + if (session->chroot) |
4378 | + session->conf_path = NIH_MUST (nih_strdup (NULL, session->chroot)); |
4379 | + if (session->user) { |
4380 | + struct passwd *pwd; |
4381 | + |
4382 | + pwd = getpwuid (session->user); |
4383 | + if (! pwd) { |
4384 | + nih_error ("%d: %s: %s", session->user, |
4385 | + _("Unable to lookup home directory"), |
4386 | + strerror (errno)); |
4387 | + |
4388 | + nih_free (session->conf_path); |
4389 | + session->conf_path = NULL; |
4390 | + return; |
4391 | + } |
4392 | + |
4393 | + NIH_MUST (nih_strcat_sprintf (&session->conf_path, NULL, "%s/%s", |
4394 | + pwd->pw_dir, USERCONFDIR)); |
4395 | + } else { |
4396 | + NIH_MUST (nih_strcat (&session->conf_path, NULL, CONFDIR)); |
4397 | } |
4398 | - |
4399 | - NIH_MUST (nih_strcat_sprintf (&session->conf_path, NULL, "%s/%s", |
4400 | - pwd->pw_dir, USERCONFDIR)); |
4401 | - } else { |
4402 | - NIH_MUST (nih_strcat (&session->conf_path, NULL, CONFDIR)); |
4403 | } |
4404 | |
4405 | source = NIH_MUST (conf_source_new (session, session->conf_path, |
4406 | @@ -339,8 +355,257 @@ |
4407 | nih_free (err); |
4408 | |
4409 | nih_free (source); |
4410 | - nih_free (session->conf_path); |
4411 | - session->conf_path = NULL; |
4412 | return; |
4413 | } |
4414 | } |
4415 | + |
4416 | +/** |
4417 | + * session_serialise: |
4418 | + * @session: session to serialise. |
4419 | + * |
4420 | + * Convert @session into a JSON representation for serialisation. |
4421 | + * |
4422 | + * Caller must free returned value using json_object_put(). |
4423 | + * |
4424 | + * Returns: JSON-serialised Session object, or NULL on error. |
4425 | + **/ |
4426 | +static json_object * |
4427 | +session_serialise (const Session *session) |
4428 | +{ |
4429 | + json_object *json; |
4430 | + json_object *conf_path = NULL; |
4431 | + json_object *chroot = NULL; |
4432 | + json_object *user; |
4433 | + |
4434 | + nih_assert (session); |
4435 | + |
4436 | + session_init (); |
4437 | + |
4438 | + json = json_object_new_object (); |
4439 | + if (! json) |
4440 | + return NULL; |
4441 | + |
4442 | + if (session->chroot) { |
4443 | + chroot = json_object_new_string (session->chroot); |
4444 | + if (! chroot) |
4445 | + goto error; |
4446 | + } |
4447 | + |
4448 | + json_object_object_add (json, "chroot", chroot); |
4449 | + |
4450 | + user = state_new_json_int (session->user); |
4451 | + if (! user) |
4452 | + goto error; |
4453 | + |
4454 | + json_object_object_add (json, "user", user); |
4455 | + |
4456 | + if (session->conf_path) { |
4457 | + conf_path = json_object_new_string (session->conf_path); |
4458 | + if (! conf_path) |
4459 | + goto error; |
4460 | + } |
4461 | + |
4462 | + json_object_object_add (json, "conf_path", conf_path); |
4463 | + |
4464 | + return json; |
4465 | + |
4466 | +error: |
4467 | + json_object_put (json); |
4468 | + return NULL; |
4469 | + |
4470 | +} |
4471 | + |
4472 | +/** |
4473 | + * session_serialise_all: |
4474 | + * |
4475 | + * Convert existing Session objects to JSON representation. |
4476 | + * |
4477 | + * Returns: JSON object containing array of Sessions, or NULL on error. |
4478 | + **/ |
4479 | +json_object * |
4480 | +session_serialise_all (void) |
4481 | +{ |
4482 | + json_object *json; |
4483 | + json_object *json_session; |
4484 | + |
4485 | + session_init (); |
4486 | + |
4487 | + json = json_object_new_array (); |
4488 | + if (! json) |
4489 | + return NULL; |
4490 | + |
4491 | + NIH_LIST_FOREACH (sessions, iter) { |
4492 | + Session *session = (Session *)iter; |
4493 | + |
4494 | + json_session = session_serialise (session); |
4495 | + |
4496 | + if (! json_session) |
4497 | + goto error; |
4498 | + |
4499 | + if (json_object_array_add (json, json_session) < 0) |
4500 | + goto error; |
4501 | + } |
4502 | + |
4503 | + return json; |
4504 | + |
4505 | +error: |
4506 | + json_object_put (json); |
4507 | + return NULL; |
4508 | +} |
4509 | + |
4510 | +/** |
4511 | + * session_deserialise: |
4512 | + * @json: JSON-serialised Session object to deserialise. |
4513 | + * |
4514 | + * Convert @json into a Session object. |
4515 | + * |
4516 | + * Returns: Session object, or NULL on error. |
4517 | + **/ |
4518 | +static Session * |
4519 | +session_deserialise (json_object *json) |
4520 | +{ |
4521 | + Session *session = NULL; |
4522 | + nih_local const char *chroot = NULL; |
4523 | + uid_t user; |
4524 | + |
4525 | + nih_assert (json); |
4526 | + |
4527 | + if (! state_check_json_type (json, object)) |
4528 | + return NULL; |
4529 | + |
4530 | + /* Note no check on value returned since chroot may be NULL */ |
4531 | + if (! state_get_json_string_var (json, "chroot", NULL, chroot)) |
4532 | + return NULL; |
4533 | + |
4534 | + if (! state_get_json_int_var (json, "user", user)) |
4535 | + return NULL; |
4536 | + |
4537 | + /* Create a new session */ |
4538 | + session = NIH_MUST (session_new (NULL, chroot, user)); |
4539 | + |
4540 | + if (! state_get_json_string_var_to_obj (json, session, conf_path)) |
4541 | + goto error; |
4542 | + |
4543 | + return session; |
4544 | + |
4545 | +error: |
4546 | + nih_free (session); |
4547 | + return NULL; |
4548 | +} |
4549 | + |
4550 | +/** |
4551 | + * session_deserialise_all: |
4552 | + * |
4553 | + * @json: root of JSON-serialised state. |
4554 | + * |
4555 | + * Convert JSON representation of sessions back into Session objects. |
4556 | + * |
4557 | + * Returns: 0 on success, -1 on error. |
4558 | + **/ |
4559 | +int |
4560 | +session_deserialise_all (json_object *json) |
4561 | +{ |
4562 | + Session *session; |
4563 | + |
4564 | + nih_assert (json); |
4565 | + |
4566 | + session_init (); |
4567 | + |
4568 | + nih_assert (NIH_LIST_EMPTY (sessions)); |
4569 | + |
4570 | + json_sessions = json_object_object_get (json, "sessions"); |
4571 | + |
4572 | + if (! json_sessions) |
4573 | + goto error; |
4574 | + |
4575 | + if (! state_check_json_type (json_sessions, array)) |
4576 | + goto error; |
4577 | + |
4578 | + for (int i = 0; i < json_object_array_length (json_sessions); i++) { |
4579 | + json_object *json_session; |
4580 | + |
4581 | + json_session = json_object_array_get_idx (json_sessions, i); |
4582 | + if (! json_session) |
4583 | + goto error; |
4584 | + |
4585 | + if (! state_check_json_type (json_session, object)) |
4586 | + goto error; |
4587 | + |
4588 | + session = session_deserialise (json_session); |
4589 | + if (! session) |
4590 | + goto error; |
4591 | + |
4592 | + session_create_conf_source (session, TRUE); |
4593 | + } |
4594 | + |
4595 | + return 0; |
4596 | + |
4597 | +error: |
4598 | + return -1; |
4599 | +} |
4600 | + |
4601 | +/** |
4602 | + * session_get_index: |
4603 | + * |
4604 | + * @session: session. |
4605 | + * |
4606 | + * Determine JSON-serialised array index for specified @session. |
4607 | + * |
4608 | + * Returns: zero-based array index for @session, or -1 on error. |
4609 | + **/ |
4610 | +int |
4611 | +session_get_index (const Session *session) |
4612 | +{ |
4613 | + int i; |
4614 | + |
4615 | + /* Handle NULL session (which is not encoded) */ |
4616 | + if (! session) |
4617 | + return 0; |
4618 | + |
4619 | + /* Sessions are serialised in order, so just return the list |
4620 | + * index. |
4621 | + */ |
4622 | + i = 1; |
4623 | + NIH_LIST_FOREACH (sessions, iter) { |
4624 | + Session *s = (Session *)iter; |
4625 | + |
4626 | + if (s == session) |
4627 | + return i; |
4628 | + |
4629 | + ++i; |
4630 | + } |
4631 | + |
4632 | + return -1; |
4633 | +} |
4634 | + |
4635 | +/** |
4636 | + * session_from_index: |
4637 | + * |
4638 | + * @idx: zero-based index. |
4639 | + * |
4640 | + * Lookup session by index number. |
4641 | + * |
4642 | + * Returns: Session (which may be the NULL session). |
4643 | + **/ |
4644 | +Session * |
4645 | +session_from_index (int idx) |
4646 | +{ |
4647 | + int i; |
4648 | + Session *session; |
4649 | + |
4650 | + nih_assert (idx >= 0); |
4651 | + |
4652 | + /* NULL session */ |
4653 | + if (! idx) |
4654 | + return NULL; |
4655 | + |
4656 | + i = 1; |
4657 | + NIH_LIST_FOREACH (sessions, iter) { |
4658 | + session = (Session *)iter; |
4659 | + |
4660 | + if (i == idx) |
4661 | + return session; |
4662 | + } |
4663 | + |
4664 | + nih_assert_not_reached (); |
4665 | +} |
4666 | |
4667 | === modified file 'init/session.h' |
4668 | --- init/session.h 2011-07-25 14:28:33 +0000 |
4669 | +++ init/session.h 2012-11-14 14:49:20 +0000 |
4670 | @@ -27,6 +27,7 @@ |
4671 | |
4672 | #include <nih-dbus/dbus_message.h> |
4673 | |
4674 | +#include <json.h> |
4675 | |
4676 | /** |
4677 | * Session: |
4678 | @@ -77,12 +78,24 @@ |
4679 | |
4680 | extern NihList *sessions; |
4681 | |
4682 | -void session_init (void); |
4683 | - |
4684 | -Session *session_new (const void *parent, const char *chroot, uid_t user) |
4685 | - __attribute__ ((malloc, warn_unused_result)); |
4686 | - |
4687 | -Session *session_from_dbus (const void *parent, NihDBusMessage *message); |
4688 | +void session_init (void); |
4689 | + |
4690 | +Session * session_new (const void *parent, const char *chroot, uid_t user) |
4691 | + __attribute__ ((malloc, warn_unused_result)); |
4692 | + |
4693 | +Session * session_from_dbus (const void *parent, NihDBusMessage *message); |
4694 | + |
4695 | +json_object * session_serialise_all (void) |
4696 | + __attribute__ ((malloc, warn_unused_result)); |
4697 | + |
4698 | +int session_deserialise_all (json_object *json) |
4699 | + __attribute__ ((warn_unused_result)); |
4700 | + |
4701 | +int session_get_index (const Session *session) |
4702 | + __attribute__ ((warn_unused_result)); |
4703 | + |
4704 | +Session * session_from_index (int idx) |
4705 | + __attribute__ ((warn_unused_result)); |
4706 | |
4707 | NIH_END_EXTERN |
4708 | |
4709 | |
4710 | === added file 'init/state.c' |
4711 | --- init/state.c 1970-01-01 00:00:00 +0000 |
4712 | +++ init/state.c 2012-11-14 14:49:20 +0000 |
4713 | @@ -0,0 +1,2008 @@ |
4714 | +/* upstart |
4715 | + * |
4716 | + * state.c - serialisation and deserialisation support. |
4717 | + * |
4718 | + * Copyright © 2012 Canonical Ltd. |
4719 | + * Author: James Hunt <james.hunt@canonical.com>. |
4720 | + * |
4721 | + * This program is free software; you can redistribute it and/or modify |
4722 | + * it under the terms of the GNU General Public License version 2, as |
4723 | + * published by the Free Software Foundation. |
4724 | + * |
4725 | + * This program is distributed in the hope that it will be useful, |
4726 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4727 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4728 | + * GNU General Public License for more details. |
4729 | + * |
4730 | + * You should have received a copy of the GNU General Public License along |
4731 | + * with this program; if not, write to the Free Software Foundation, Inc., |
4732 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
4733 | + */ |
4734 | + |
4735 | +#include <string.h> |
4736 | +#include <sys/types.h> |
4737 | +#include <signal.h> |
4738 | +#include <unistd.h> |
4739 | +#include <errno.h> |
4740 | +#include <sys/select.h> |
4741 | +#include <unistd.h> |
4742 | +#include <fcntl.h> |
4743 | + |
4744 | +#include <nih/macros.h> |
4745 | +#include <nih/logging.h> |
4746 | +#include <nih/string.h> |
4747 | +#include <nih/io.h> |
4748 | + |
4749 | +#include "paths.h" |
4750 | +#include "state.h" |
4751 | +#include "session.h" |
4752 | +#include "event.h" |
4753 | +#include "job_class.h" |
4754 | +#include "job.h" |
4755 | +#include "environ.h" |
4756 | +#include "blocked.h" |
4757 | +#include "conf.h" |
4758 | +#include "control.h" |
4759 | + |
4760 | +json_object *json_sessions = NULL; |
4761 | +json_object *json_events = NULL; |
4762 | +json_object *json_classes = NULL; |
4763 | + |
4764 | +extern int use_session_bus; |
4765 | + |
4766 | +/** |
4767 | + * args_copy: |
4768 | + * |
4769 | + * Copy of original argv used when re-executing to ensure same |
4770 | + * command-line is used. Required since we clear the actual args for |
4771 | + * ps(1) et al. |
4772 | + */ |
4773 | +char **args_copy = NULL; |
4774 | + |
4775 | +/** |
4776 | + * restart: |
4777 | + * |
4778 | + * This is set to TRUE if we're being re-exec'd by an existing init |
4779 | + * process. |
4780 | + **/ |
4781 | +int restart = FALSE; |
4782 | + |
4783 | +/* Prototypes for static functions */ |
4784 | +static json_object * |
4785 | +state_serialise_blocked (const Blocked *blocked) |
4786 | + __attribute__ ((malloc, warn_unused_result)); |
4787 | + |
4788 | +static Blocked * |
4789 | +state_deserialise_blocked (void *parent, json_object *json, NihList *list) |
4790 | + __attribute__ ((malloc, warn_unused_result)); |
4791 | + |
4792 | +static JobClass * |
4793 | +state_index_to_job_class (int job_class_index) |
4794 | + __attribute__ ((warn_unused_result)); |
4795 | + |
4796 | +static Job * |
4797 | +state_get_job (const char *job_class, const char *job_name) |
4798 | + __attribute__ ((warn_unused_result)); |
4799 | + |
4800 | +/** |
4801 | + * state_read: |
4802 | + * |
4803 | + * @fd: Open file descriptor to read JSON from. |
4804 | + * |
4805 | + * Read JSON-encoded state from specified file descriptor and recreate |
4806 | + * all internal objects based on JSON representation. The read will |
4807 | + * timeout, resulting in a failure after STATE_WAIT_SECS seconds |
4808 | + * indicating a problem with the child. |
4809 | + * |
4810 | + * Returns: 0 on success, or -1 on error. |
4811 | + **/ |
4812 | +int |
4813 | +state_read (int fd) |
4814 | +{ |
4815 | + int nfds; |
4816 | + int ret; |
4817 | + fd_set readfds; |
4818 | + struct timeval timeout; |
4819 | + |
4820 | + nih_assert (fd != -1); |
4821 | + |
4822 | + state_get_timeout (timeout.tv_sec); |
4823 | + timeout.tv_usec = 0; |
4824 | + |
4825 | + nfds = 1 + fd; |
4826 | + |
4827 | + while (TRUE) { |
4828 | + FD_ZERO (&readfds); |
4829 | + FD_SET (fd, &readfds); |
4830 | + |
4831 | + ret = select (nfds, &readfds, NULL, NULL, |
4832 | + timeout.tv_sec < 0 ? NULL : &timeout); |
4833 | + |
4834 | + if (ret < 0 && (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)) |
4835 | + return -1; |
4836 | + |
4837 | + if (FD_ISSET (fd, &readfds)) |
4838 | + break; |
4839 | + } |
4840 | + |
4841 | + nih_assert (ret == 1); |
4842 | + |
4843 | + /* Now, read the data */ |
4844 | + if (state_read_objects (fd) < 0) |
4845 | + return -1; |
4846 | + |
4847 | + return 0; |
4848 | +} |
4849 | + |
4850 | +/** |
4851 | + * state_write: |
4852 | + * |
4853 | + * @fd: Open file descriptor to write JSON to, |
4854 | + * @state_data: JSON string representing internal object state, |
4855 | + * @len: length of @state_data. |
4856 | + * |
4857 | + * Write internal state to specified file descriptor in JSON format. |
4858 | + * |
4859 | + * Signals are assumed to be blocked when this call is made. |
4860 | + * |
4861 | + * Note the timeout - it is possible that the new PID 1 instance may be |
4862 | + * unable to read from its end of the file descriptor, either due to |
4863 | + * some error scenario or more likely due to it not supporting stateful |
4864 | + * re-exec. Hence, we must have a way to detect this and abort the |
4865 | + * child. |
4866 | + * |
4867 | + * Returns: 0 on success, or -1 on error. |
4868 | + **/ |
4869 | +int |
4870 | +state_write (int fd, const char *state_data, size_t len) |
4871 | +{ |
4872 | + int nfds; |
4873 | + int ret; |
4874 | + fd_set writefds; |
4875 | + struct timeval timeout; |
4876 | + |
4877 | + nih_assert (fd != -1); |
4878 | + nih_assert (state_data); |
4879 | + nih_assert (len); |
4880 | + |
4881 | + /* must be called from child process */ |
4882 | + nih_assert (getpid () != (pid_t)1); |
4883 | + |
4884 | + state_get_timeout (timeout.tv_sec); |
4885 | + timeout.tv_usec = 0; |
4886 | + |
4887 | + nfds = 1 + fd; |
4888 | + |
4889 | + while (TRUE) { |
4890 | + FD_ZERO (&writefds); |
4891 | + FD_SET (fd, &writefds); |
4892 | + |
4893 | + ret = select (nfds, NULL, &writefds, NULL, |
4894 | + timeout.tv_sec < 0 ? NULL : &timeout); |
4895 | + |
4896 | + if (ret < 0 && (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)) |
4897 | + return -1; |
4898 | + |
4899 | + if (FD_ISSET (fd, &writefds)) |
4900 | + break; |
4901 | + } |
4902 | + |
4903 | + nih_assert (ret == 1); |
4904 | + |
4905 | + if (state_write_objects (fd, state_data, len) < 0) |
4906 | + return -1; |
4907 | + |
4908 | + return 0; |
4909 | +} |
4910 | + |
4911 | + |
4912 | +/** |
4913 | + * state_read_objects: |
4914 | + * |
4915 | + * @fd: file descriptor to read serialisation data from. |
4916 | + * |
4917 | + * Read serialisation data from specified file descriptor. |
4918 | + * @fd is assumed to be open and readable. |
4919 | + * |
4920 | + * Returns: 0 on success, -1 on error. |
4921 | + **/ |
4922 | +int |
4923 | +state_read_objects (int fd) |
4924 | +{ |
4925 | + ssize_t ret; |
4926 | + int initial_size = 4096; |
4927 | + nih_local NihIoBuffer *buffer = NULL; |
4928 | + nih_local char *buf = NULL; |
4929 | + |
4930 | + nih_assert (fd != -1); |
4931 | + |
4932 | + buffer = nih_io_buffer_new (NULL); |
4933 | + |
4934 | + buf = nih_alloc (NULL, initial_size); |
4935 | + if (! buf) |
4936 | + goto error; |
4937 | + |
4938 | + /* Read the JSON data into the buffer */ |
4939 | + do { |
4940 | + if (nih_io_buffer_resize (buffer, initial_size) < 0) |
4941 | + goto error; |
4942 | + |
4943 | + ret = read (fd, buf, sizeof (buf)); |
4944 | + if (ret < 0) { |
4945 | + if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) |
4946 | + goto error; |
4947 | + continue; |
4948 | + } else if (! ret) |
4949 | + break; |
4950 | + |
4951 | + if (nih_io_buffer_push (buffer, buf, ret) < 0) |
4952 | + goto error; |
4953 | + } while (TRUE); |
4954 | + |
4955 | + /* Recreate internal state from JSON */ |
4956 | + if (state_from_string (buffer->buf) < 0) |
4957 | + goto error; |
4958 | + |
4959 | + return 0; |
4960 | + |
4961 | +error: |
4962 | + return -1; |
4963 | +} |
4964 | + |
4965 | +/** |
4966 | + * state_write_objects: |
4967 | + * |
4968 | + * @fd: file descriptor to write serialisation data on, |
4969 | + * @state_data: JSON string representing internal object state, |
4970 | + * @len: length of @state_data. |
4971 | + * |
4972 | + * Write serialisation data to specified file descriptor. |
4973 | + * @fd is assumed to be open and valid to write to. |
4974 | + * |
4975 | + * Returns: 0 on success, -1 on error. |
4976 | + **/ |
4977 | +int |
4978 | +state_write_objects (int fd, const char *state_data, size_t len) |
4979 | +{ |
4980 | + ssize_t ret; |
4981 | + |
4982 | + nih_assert (fd != -1); |
4983 | + nih_assert (state_data); |
4984 | + nih_assert (len); |
4985 | + |
4986 | + ret = write (fd, state_data, len); |
4987 | + |
4988 | + return (ret < 0 ? -1 : 0); |
4989 | +} |
4990 | + |
4991 | +/** |
4992 | + * state_to_string: |
4993 | + * |
4994 | + * @json_string; newly-allocated string, |
4995 | + * @len: length of @json_string. |
4996 | + * |
4997 | + * Serialise internal data structures to a JSON string. |
4998 | + * |
4999 | + * Returns: 0 on success, -1 on error. |
5000 | + **/ |
Hi James,
Reviewing just the parts of the diff that are new since the last time I looked at the branch.
This part of init/session.c looks strange:
+ if (getenv ("UPSTART_TESTS"))
NihError *err;
+ return;
+
if (conf_source_reload (source) < 0) {
The commit message only says: create_ conf_source( ): Don't reload conf sources when in
- session_
testing mode.
without explaining why.
In init/tests/ test_util. c:
+ * Returns 0 if strings are identical or both NULL, else 1.
+ **/
+int
+string_check (const char *a, const char *b)
+{
+ if ((a == b) && !a)
+ return 0;
+
This '&& !a' seems unnecessary - surely if a == b and are /not/ null, there's no need to call strcmp to confirm that the strings are equal.
=== modified file 'init/blocked.c' type_enum_ to_str (BlockedType ty type_str_ to_enum (const char *type)
state_ str_to_ enum (BLOCKED_JOB, type);
state_ str_to_ enum (BLOCKED_EVENT, type);
state_ str_to_ enum (BLOCKED_ EMIT_METHOD, type); type_str_ to_enum (const char *ty
state_ str_to_ enum (BLOCKED_ INSTANCE_ STOP_METHOD, type);
state_ str_to_ enum (BLOCKED_ INSTANCE_ RESTART_ METHOD, type);
--- init/blocked.c 2012-07-30 14:07:37 +0000
+++ init/blocked.c 2012-11-13 06:01:51 +0000
@@ -131,6 +131,9 @@ blocked_
BlockedType
blocked_
{
+ if (! type)
+ goto error;
+
@@ -141,5 +144,6 @@ blocked_
+error:
return -1;
}
The above is documented as "Made resilient to error conditions". But this doesn't appear to be a real-world error condition at all; the only place in the code that blocked_ type_str_ to_enum( ) is called is in state.c:
if (! state_get_ json_string_ var_strict (json, "type", NULL, blocked_type_str))
goto error;
This means there's no possibility of the function ever being called with a null argument (and likewise for the other str_to_enum functions), so this must be here just for the test suite. I don't think it's a good idea to complicate either the code or the test suite by handling errors that the code is written to never allow! I've pushed a branch to address this at
lp:~vorlon/upstart/stateful-reexec-dont-shape-code-to-impossible-tests and raised an MP for your consideration.
=== modified file 'init/job_ process. c'
while (p && (*p == '\n'))
p++;
--- init/job_process.c 2012-08-25 08:53:37 +0000
+++ init/job_process.c 2012-11-13 10:47:50 +0000
@@ -211,7 +211,7 @@ job_process_run (Job *job,
- if ((! nl) || (! *p)) {
/* Strip off the newline(s) */
if (nl)
*nl = '\0';
+ if ((! nl) || (p && ! *p)) {
This change is documented as "Stop potential NULL-pointer dereference". But here's the full context:
p = nl = strchr (script, '\n');
while (p && (*p == '\n'))
p++;
if ((! nl) || (p && ! *p)) {
/* Strip off the newline(s) */
if (nl)
...