Merge lp:~upstart-devel/upstart/stateful-reexec into lp:upstart

Proposed by James Hunt
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
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.

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://wiki.ubuntu.com/FoundationsTeam/Specs/QuantalUpstartStatefulReexec

To post a comment you must log in.
Revision history for this message
Steve Langasek (vorlon) wrote :
Download full text (5.5 KiB)

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_create_conf_source(): Don't reload conf sources when in
      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'
--- 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_type_enum_to_str (BlockedType ty
 BlockedType
 blocked_type_str_to_enum (const char *type)
 {
+ if (! type)
+ goto error;
+
        state_str_to_enum (BLOCKED_JOB, type);
        state_str_to_enum (BLOCKED_EVENT, type);
        state_str_to_enum (BLOCKED_EMIT_METHOD, type);
@@ -141,5 +144,6 @@ blocked_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);

+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;

        blocked_type = blocked_type_str_to_enum (blocked_type_str);

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'
--- 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,
                while (p && (*p == '\n'))
                        p++;

- if ((! nl) || (! *p)) {
+ if ((! nl) || (p && ! *p)) {
                        /* Strip off the newline(s) */
                        if (nl)
                                *nl = '\0';

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)
     ...

Read more...

review: Approve
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.

Revision history for this message
James Hunt (jamesodhunt) wrote :
Download full text (6.9 KiB)

> 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_create_conf_source(): Don't reload conf sources when in
> 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/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.

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_type_enum_to_str (BlockedType ty
> BlockedType
> blocked_type_str_to_enum (const char *type)
> {
> + if (! type)
> + goto error;
> +
> state_str_to_enum (BLOCKED_JOB, type);
> state_str_to_enum (BLOCKED_EVENT, type);
> state_str_to_enum (BLOCKED_EMIT_METHOD, type);
> @@ -141,5 +144,6 @@ blocked_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);
>
> +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;
>
> blocked_type = blocked_type_str_to_enum (blocked_type_str);
>
> 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_process.c'
> --- 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 (...

Read more...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'ChangeLog'
--- ChangeLog 2012-10-22 13:54:50 +0000
+++ ChangeLog 2012-11-14 14:49:20 +0000
@@ -1,3 +1,19 @@
12012-11-07 James Hunt <james.hunt@ubuntu.com>
2
3 * Added stateful re-exec support such that when Upstart is asked
4 to restart itself using 'telinit u', the new instance of PID 1
5 will retain knowledge of all system jobs and running instances.
6
72012-09-10 James Hunt <james.hunt@ubuntu.com>
8
9 * Merged lp:~jconti/upstart/fix_empty_chroot.
10
112012-05-23 James Hunt <james.hunt@ubuntu.com>
12
13 * init/main.c: Add in "bare" re-exec handling from Ubuntu
14 branch.
15 * init/main.c: Unhide "restart" option.
16
12012-10-22 James Hunt <james.hunt@ubuntu.com>172012-10-22 James Hunt <james.hunt@ubuntu.com>
218
3 * init/parse_job.c: stanza_kill(): Actually save parsed19 * init/parse_job.c: stanza_kill(): Actually save parsed
420
=== modified file 'configure.ac'
--- configure.ac 2012-03-22 11:37:20 +0000
+++ configure.ac 2012-11-14 14:49:20 +0000
@@ -32,6 +32,24 @@
32PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.2.16])32PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.2.16])
33PKG_CHECK_MODULES([UDEV], [libudev >= 146], [have_udev=yes], [have_udev=no])33PKG_CHECK_MODULES([UDEV], [libudev >= 146], [have_udev=yes], [have_udev=no])
3434
35# Reasons for requiring this library version:
36#
37# 1) RFC 4627, the JSON "memo" (it is *NOT* a standard!) helpfully fails
38# to specify the maximum size of an integer type. And yet, JSON is
39# supposedly a subset of ECMA-262, which specifies a Number type to
40# be 64-bits.
41#
42# The loose JSON "memo" might expalain why older versions of JSON-C encode
43# a JSON Number in sizoef(int) bytes which is only 32-bits on 32-bit
44# systems. This is not acceptable for Upstart which needs to be able
45# to encode 'size_t' and 'unsigned long' types which can be larger
46# than 'int'.
47#
48# 2) New json_tokener_parse_verbose() function required to detect invalid
49# JSON (!)
50#
51PKG_CHECK_MODULES([JSON], [json >= 0.10])
52
35AM_CONDITIONAL([HAVE_UDEV], [test "$have_udev" = yes])53AM_CONDITIONAL([HAVE_UDEV], [test "$have_udev" = yes])
3654
37# Checks for header files.55# Checks for header files.
@@ -42,6 +60,21 @@
42AM_PROG_CC_C_O60AM_PROG_CC_C_O
43NIH_C_THREAD61NIH_C_THREAD
4462
63AC_CHECK_SIZEOF(int)
64AC_CHECK_SIZEOF(uid_t)
65AC_CHECK_SIZEOF(pid_t)
66AC_CHECK_SIZEOF(time_t)
67AC_CHECK_SIZEOF(mode_t)
68AC_CHECK_SIZEOF(size_t)
69AC_CHECK_SIZEOF(ssize_t)
70
71# Unlikely to hit this limit for a while, but it pays to be safe.
72for type in int uid_t pid_t time_t mode_t size_t ssize_t
73do
74 eval value="\$ac_cv_sizeof_${type}"
75 test "$value" -gt 8 && AC_MSG_ERROR([type $type is $value bytes, larger than JSON-C can represent])
76done
77
45# Checks for library functions.78# Checks for library functions.
4679
47# Other checks80# Other checks
4881
=== modified file 'extra/man/upstart-udev-bridge.8'
--- extra/man/upstart-udev-bridge.8 2011-12-15 16:11:14 +0000
+++ extra/man/upstart-udev-bridge.8 2012-11-14 14:49:20 +0000
@@ -16,7 +16,10 @@
16events for them.16events for them.
1717
18It emits events which match the pattern "\fIS\fP\-device\-\fIA\fP" where18It emits events which match the pattern "\fIS\fP\-device\-\fIA\fP" where
19\(aqS\(aq is the udev \fIsubsystem\fP and \(aqA\(aq is the udev \fIaction\fP.19\(aqS\(aq is the udev \fIsubsystem\fP and \(aqA\(aq is one of
20\fIadded\fR, \fIchanged\fR or \fIremoved\fR,
21which correspond to the past tense of the udev \(aqaction\(aq.
22
20See \fBudev\fP(7) and for further details.23See \fBudev\fP(7) and for further details.
2124
22Assuming \fI/sys\fP is mounted, possible values for \fIsubsystem\fP for25Assuming \fI/sys\fP is mounted, possible values for \fIsubsystem\fP for
2326
=== modified file 'init/Makefile.am'
--- init/Makefile.am 2012-10-22 13:54:50 +0000
+++ init/Makefile.am 2012-11-14 14:49:20 +0000
@@ -5,7 +5,8 @@
5AM_CFLAGS = \5AM_CFLAGS = \
6 $(NIH_CFLAGS) \6 $(NIH_CFLAGS) \
7 $(NIH_DBUS_CFLAGS) \7 $(NIH_DBUS_CFLAGS) \
8 $(DBUS_CFLAGS)8 $(DBUS_CFLAGS) \
9 $(JSON_CFLAGS)
910
10AM_CPPFLAGS = \11AM_CPPFLAGS = \
11 -DLOCALEDIR="\"$(localedir)\"" \12 -DLOCALEDIR="\"$(localedir)\"" \
@@ -41,6 +42,7 @@
41 environ.c environ.h \42 environ.c environ.h \
42 process.c process.h \43 process.c process.h \
43 session.c session.h \44 session.c session.h \
45 state.c state.h \
44 job_class.c job_class.h \46 job_class.c job_class.h \
45 job_process.c job_process.h \47 job_process.c job_process.h \
46 job.c job.h \48 job.c job.h \
@@ -62,6 +64,7 @@
62 $(NIH_LIBS) \64 $(NIH_LIBS) \
63 $(NIH_DBUS_LIBS) \65 $(NIH_DBUS_LIBS) \
64 $(DBUS_LIBS) \66 $(DBUS_LIBS) \
67 $(JSON_LIBS) \
65 -lrt68 -lrt
6669
6770
@@ -109,7 +112,6 @@
109 --default-interface=com.ubuntu.Upstart0_6.Instance \112 --default-interface=com.ubuntu.Upstart0_6.Instance \
110 --output=$@ $<113 --output=$@ $<
111114
112
113# These have to be built sources because we can't compile object files115# These have to be built sources because we can't compile object files
114# without the header file existing first116# without the header file existing first
115BUILT_SOURCES = \117BUILT_SOURCES = \
@@ -125,6 +127,8 @@
125127
126EXTRA_DIST = init.supp128EXTRA_DIST = init.supp
127129
130test_util_SOURCES = \
131 tests/test_util.c tests/test_util.h
128132
129TESTS = \133TESTS = \
130 test_system \134 test_system \
@@ -134,6 +138,7 @@
134 test_job_process \138 test_job_process \
135 test_job \139 test_job \
136 test_log \140 test_log \
141 test_state \
137 test_event \142 test_event \
138 test_event_operator \143 test_event_operator \
139 test_blocked \144 test_blocked \
@@ -156,41 +161,44 @@
156 environ.o \161 environ.o \
157 $(NIH_LIBS)162 $(NIH_LIBS)
158163
159test_process_SOURCES = tests/test_process.c164test_process_SOURCES = tests/test_process.c $(test_util_SOURCES)
160test_process_LDADD = \165test_process_LDADD = \
161 system.o environ.o process.o \166 system.o environ.o process.o \
162 job_class.o job_process.o job.o event.o event_operator.o blocked.o \167 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
163 parse_job.o parse_conf.o conf.o control.o \168 parse_job.o parse_conf.o conf.o control.o \
164 session.o log.o \169 session.o log.o state.o \
165 com.ubuntu.Upstart.o \170 com.ubuntu.Upstart.o \
166 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \171 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
167 $(NIH_LIBS) \172 $(NIH_LIBS) \
168 $(NIH_DBUS_LIBS) \173 $(NIH_DBUS_LIBS) \
169 $(DBUS_LIBS)174 $(DBUS_LIBS) \
175 $(JSON_LIBS)
170176
171test_job_class_SOURCES = tests/test_job_class.c177test_job_class_SOURCES = tests/test_job_class.c
172test_job_class_LDADD = \178test_job_class_LDADD = \
173 system.o environ.o process.o \179 system.o environ.o process.o \
174 job_class.o job_process.o job.o event.o event_operator.o blocked.o \180 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
175 parse_job.o parse_conf.o conf.o control.o \181 parse_job.o parse_conf.o conf.o control.o \
176 session.o log.o \182 session.o log.o state.o \
177 com.ubuntu.Upstart.o \183 com.ubuntu.Upstart.o \
178 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \184 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
179 $(NIH_LIBS) \185 $(NIH_LIBS) \
180 $(NIH_DBUS_LIBS) \186 $(NIH_DBUS_LIBS) \
181 $(DBUS_LIBS)187 $(DBUS_LIBS) \
188 $(JSON_LIBS)
182189
183test_job_process_SOURCES = tests/test_job_process.c190test_job_process_SOURCES = tests/test_job_process.c $(test_util_SOURCES)
184test_job_process_LDADD = \191test_job_process_LDADD = \
185 system.o environ.o process.o \192 system.o environ.o process.o \
186 job_class.o job_process.o job.o event.o event_operator.o blocked.o \193 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
187 parse_job.o parse_conf.o conf.o control.o \194 parse_job.o parse_conf.o conf.o control.o \
188 session.o log.o \195 session.o log.o state.o \
189 com.ubuntu.Upstart.o \196 com.ubuntu.Upstart.o \
190 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \197 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
191 $(NIH_LIBS) \198 $(NIH_LIBS) \
192 $(NIH_DBUS_LIBS) \199 $(NIH_DBUS_LIBS) \
193 $(DBUS_LIBS) \200 $(DBUS_LIBS) \
201 $(JSON_LIBS) \
194 -lutil202 -lutil
195203
196test_job_SOURCES = tests/test_job.c204test_job_SOURCES = tests/test_job.c
@@ -198,24 +206,40 @@
198 system.o environ.o process.o \206 system.o environ.o process.o \
199 job_class.o job_process.o job.o event.o event_operator.o blocked.o \207 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
200 parse_job.o parse_conf.o conf.o control.o \208 parse_job.o parse_conf.o conf.o control.o \
201 session.o log.o \209 session.o log.o state.o \
202 com.ubuntu.Upstart.o \210 com.ubuntu.Upstart.o \
203 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \211 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
204 $(NIH_LIBS) \212 $(NIH_LIBS) \
205 $(NIH_DBUS_LIBS) \213 $(NIH_DBUS_LIBS) \
206 $(DBUS_LIBS)214 $(DBUS_LIBS) \
215 $(JSON_LIBS)
207216
208test_log_SOURCES = tests/test_log.c217test_log_SOURCES = tests/test_log.c $(test_util_SOURCES)
209test_log_LDADD = \218test_log_LDADD = \
210 system.o environ.o process.o \219 system.o environ.o process.o \
211 job_class.o job_process.o job.o event.o event_operator.o blocked.o \220 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
212 parse_job.o parse_conf.o conf.o control.o \221 parse_job.o parse_conf.o conf.o control.o \
213 session.o log.o \222 session.o log.o state.o \
214 com.ubuntu.Upstart.o \223 com.ubuntu.Upstart.o \
215 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \224 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
216 $(NIH_LIBS) \225 $(NIH_LIBS) \
217 $(NIH_DBUS_LIBS) \226 $(NIH_DBUS_LIBS) \
218 $(DBUS_LIBS) \227 $(DBUS_LIBS) \
228 $(JSON_LIBS) \
229 -lutil
230
231test_state_SOURCES = tests/test_state.c $(test_util_SOURCES)
232test_state_LDADD = \
233 system.o environ.o process.o \
234 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
235 parse_job.o parse_conf.o conf.o control.o \
236 session.o log.o state.o \
237 com.ubuntu.Upstart.o \
238 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
239 $(NIH_LIBS) \
240 $(NIH_DBUS_LIBS) \
241 $(DBUS_LIBS) \
242 $(JSON_LIBS) \
219 -lutil243 -lutil
220244
221test_event_SOURCES = tests/test_event.c245test_event_SOURCES = tests/test_event.c
@@ -223,84 +247,91 @@
223 system.o environ.o process.o \247 system.o environ.o process.o \
224 job_class.o job_process.o job.o event.o event_operator.o blocked.o \248 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
225 parse_job.o parse_conf.o conf.o control.o \249 parse_job.o parse_conf.o conf.o control.o \
226 session.o log.o \250 session.o log.o state.o \
227 com.ubuntu.Upstart.o \251 com.ubuntu.Upstart.o \
228 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \252 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
229 $(NIH_LIBS) \253 $(NIH_LIBS) \
230 $(NIH_DBUS_LIBS) \254 $(NIH_DBUS_LIBS) \
231 $(DBUS_LIBS)255 $(DBUS_LIBS) \
256 $(JSON_LIBS)
232257
233test_event_operator_SOURCES = tests/test_event_operator.c258test_event_operator_SOURCES = tests/test_event_operator.c
234test_event_operator_LDADD = \259test_event_operator_LDADD = \
235 system.o environ.o process.o \260 system.o environ.o process.o \
236 job_class.o job_process.o job.o event.o event_operator.o blocked.o \261 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 \262 parse_job.o parse_conf.o conf.o control.o \
238 session.o log.o \263 session.o log.o state.o \
239 com.ubuntu.Upstart.o \264 com.ubuntu.Upstart.o \
240 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \265 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
241 $(NIH_LIBS) \266 $(NIH_LIBS) \
242 $(NIH_DBUS_LIBS) \267 $(NIH_DBUS_LIBS) \
243 $(DBUS_LIBS)268 $(DBUS_LIBS) \
269 $(JSON_LIBS)
244270
245test_blocked_SOURCES = tests/test_blocked.c271test_blocked_SOURCES = tests/test_blocked.c
246test_blocked_LDADD = \272test_blocked_LDADD = \
247 system.o environ.o process.o \273 system.o environ.o process.o \
248 job_class.o job_process.o job.o event.o event_operator.o blocked.o \274 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
249 parse_job.o parse_conf.o conf.o control.o \275 parse_job.o parse_conf.o conf.o control.o \
250 session.o log.o \276 session.o log.o state.o \
251 com.ubuntu.Upstart.o \277 com.ubuntu.Upstart.o \
252 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \278 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
253 $(NIH_LIBS) \279 $(NIH_LIBS) \
254 $(NIH_DBUS_LIBS) \280 $(NIH_DBUS_LIBS) \
255 $(DBUS_LIBS)281 $(DBUS_LIBS) \
282 $(JSON_LIBS)
256283
257test_parse_job_SOURCES = tests/test_parse_job.c284test_parse_job_SOURCES = tests/test_parse_job.c
258test_parse_job_LDADD = \285test_parse_job_LDADD = \
259 system.o environ.o process.o \286 system.o environ.o process.o \
260 job_class.o job_process.o job.o event.o event_operator.o blocked.o \287 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
261 parse_job.o parse_conf.o conf.o control.o \288 parse_job.o parse_conf.o conf.o control.o \
262 session.o log.o \289 session.o log.o state.o \
263 com.ubuntu.Upstart.o \290 com.ubuntu.Upstart.o \
264 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \291 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
265 $(NIH_LIBS) \292 $(NIH_LIBS) \
266 $(NIH_DBUS_LIBS) \293 $(NIH_DBUS_LIBS) \
267 $(DBUS_LIBS)294 $(DBUS_LIBS) \
295 $(JSON_LIBS)
268296
269test_parse_conf_SOURCES = tests/test_parse_conf.c297test_parse_conf_SOURCES = tests/test_parse_conf.c
270test_parse_conf_LDADD = \298test_parse_conf_LDADD = \
271 system.o environ.o process.o \299 system.o environ.o process.o \
272 job_class.o job_process.o job.o event.o event_operator.o blocked.o \300 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
273 parse_job.o parse_conf.o conf.o control.o \301 parse_job.o parse_conf.o conf.o control.o \
274 session.o log.o \302 session.o log.o state.o \
275 com.ubuntu.Upstart.o \303 com.ubuntu.Upstart.o \
276 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \304 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
277 $(NIH_LIBS) \305 $(NIH_LIBS) \
278 $(NIH_DBUS_LIBS) \306 $(NIH_DBUS_LIBS) \
279 $(DBUS_LIBS)307 $(DBUS_LIBS) \
308 $(JSON_LIBS)
280309
281test_conf_SOURCES = tests/test_conf.c310test_conf_SOURCES = tests/test_conf.c $(test_util_SOURCES)
282test_conf_LDADD = \311test_conf_LDADD = \
283 system.o environ.o process.o \312 system.o environ.o process.o \
284 job_class.o job_process.o job.o event.o event_operator.o blocked.o \313 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
285 parse_job.o parse_conf.o conf.o control.o \314 parse_job.o parse_conf.o conf.o control.o \
286 session.o log.o \315 session.o log.o state.o \
287 com.ubuntu.Upstart.o \316 com.ubuntu.Upstart.o \
288 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \317 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
289 $(NIH_LIBS) \318 $(NIH_LIBS) \
290 $(NIH_DBUS_LIBS) \319 $(NIH_DBUS_LIBS) \
291 $(DBUS_LIBS)320 $(DBUS_LIBS) \
321 $(JSON_LIBS)
292322
293test_control_SOURCES = tests/test_control.c323test_control_SOURCES = tests/test_control.c
294test_control_LDADD = \324test_control_LDADD = \
295 system.o environ.o process.o \325 system.o environ.o process.o \
296 job_class.o job_process.o job.o event.o event_operator.o blocked.o \326 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
297 parse_job.o parse_conf.o conf.o control.o \327 parse_job.o parse_conf.o conf.o control.o \
298 session.o log.o \328 session.o log.o state.o \
299 com.ubuntu.Upstart.o \329 com.ubuntu.Upstart.o \
300 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \330 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
301 $(NIH_LIBS) \331 $(NIH_LIBS) \
302 $(NIH_DBUS_LIBS) \332 $(NIH_DBUS_LIBS) \
303 $(DBUS_LIBS)333 $(DBUS_LIBS) \
334 $(JSON_LIBS)
304335
305336
306install-data-local:337install-data-local:
307338
=== modified file 'init/blocked.c'
--- init/blocked.c 2009-06-23 09:29:35 +0000
+++ init/blocked.c 2012-11-14 14:49:20 +0000
@@ -92,3 +92,56 @@
9292
93 return blocked;93 return blocked;
94}94}
95
96
97/**
98 * blocked_enum_to_str:
99 *
100 * @type: BlockedType.
101 *
102 * Convert Blocked to a string representation.
103 *
104 * Returns: string representation of @type, or NULL if not known.
105 **/
106const char *
107blocked_type_enum_to_str (BlockedType type)
108{
109 state_enum_to_str (BLOCKED_JOB, type);
110 state_enum_to_str (BLOCKED_EVENT, type);
111 state_enum_to_str (BLOCKED_EMIT_METHOD, type);
112 state_enum_to_str (BLOCKED_JOB_START_METHOD, type);
113 state_enum_to_str (BLOCKED_JOB_STOP_METHOD, type);
114 state_enum_to_str (BLOCKED_JOB_RESTART_METHOD, type);
115 state_enum_to_str (BLOCKED_INSTANCE_START_METHOD, type);
116 state_enum_to_str (BLOCKED_INSTANCE_STOP_METHOD, type);
117 state_enum_to_str (BLOCKED_INSTANCE_RESTART_METHOD, type);
118
119 return NULL;
120}
121
122/**
123 * blocked_type_str_to_enum:
124 *
125 * @type: string BlockedType value.
126 *
127 * Convert @type back into enum value.
128 *
129 * Returns: BlockedType representation of @type, or -1 if not known.
130 **/
131BlockedType
132blocked_type_str_to_enum (const char *type)
133{
134 nih_assert (type);
135
136 state_str_to_enum (BLOCKED_JOB, type);
137 state_str_to_enum (BLOCKED_EVENT, type);
138 state_str_to_enum (BLOCKED_EMIT_METHOD, type);
139 state_str_to_enum (BLOCKED_JOB_START_METHOD, type);
140 state_str_to_enum (BLOCKED_JOB_STOP_METHOD, type);
141 state_str_to_enum (BLOCKED_JOB_RESTART_METHOD, type);
142 state_str_to_enum (BLOCKED_INSTANCE_START_METHOD, type);
143 state_str_to_enum (BLOCKED_INSTANCE_STOP_METHOD, type);
144 state_str_to_enum (BLOCKED_INSTANCE_RESTART_METHOD, type);
145
146 return -1;
147}
95148
=== modified file 'init/blocked.h'
--- init/blocked.h 2009-06-23 09:29:35 +0000
+++ init/blocked.h 2012-11-14 14:49:20 +0000
@@ -79,6 +79,14 @@
79Blocked *blocked_new (const void *parent, BlockedType type, void *data)79Blocked *blocked_new (const void *parent, BlockedType type, void *data)
80 __attribute__ ((warn_unused_result, malloc));80 __attribute__ ((warn_unused_result, malloc));
8181
82const char *
83blocked_type_enum_to_str (BlockedType type)
84 __attribute__ ((warn_unused_result));
85
86BlockedType
87blocked_type_str_to_enum (const char *type)
88 __attribute__ ((warn_unused_result));
89
82NIH_END_EXTERN90NIH_END_EXTERN
8391
84#endif /* INIT_BLOCKED_H */92#endif /* INIT_BLOCKED_H */
8593
=== modified file 'init/conf.c'
--- init/conf.c 2011-06-06 17:05:11 +0000
+++ init/conf.c 2012-11-14 14:49:20 +0000
@@ -952,8 +952,8 @@
952 **/952 **/
953static int953static int
954conf_reload_path (ConfSource *source,954conf_reload_path (ConfSource *source,
955 const char *path,955 const char *path,
956 const char *override_path)956 const char *override_path)
957{957{
958 ConfFile *file = NULL;958 ConfFile *file = NULL;
959 nih_local char *buf = NULL;959 nih_local char *buf = NULL;
@@ -1209,52 +1209,58 @@
1209}1209}
12101210
1211void1211void
1212debug_show_job_class (const JobClass *job)1212debug_show_job_class (const JobClass *class)
1213{1213{
1214 int i;1214 nih_local char *start_on = NULL;
1215 char **env = (char **)job->env;1215 nih_local char *stop_on = NULL;
1216 char **export = (char **)job->export;
12171216
1218 nih_assert (job);1217 nih_assert (class);
12191218
1220 nih_debug ("JobClass %p: name='%s', path='%s', task=%d, "1219 nih_debug ("JobClass %p: name='%s', path='%s', task=%d, "
1221 "respawn=%d, console=%x, deleted=%d, debug=%d",1220 "respawn=%d, console=%x, deleted=%d, debug=%d",
1222 job, job->name, job->path, job->task,1221 class, class->name, class->path, class->task,
1223 job->respawn, job->console, job->deleted, job->debug);1222 class->respawn, class->console, class->deleted, class->debug);
12241223
1225 nih_debug ("\tstart_on=%p, stop_on=%p, emits=%p, process=%p",1224 if (class->start_on) start_on = event_operator_collapse (class->start_on);
1226 job->start_on, job->stop_on, job->emits, job->process);1225 if (class->stop_on) stop_on = event_operator_collapse (class->stop_on);
1226
1227 nih_debug ("\tstart_on=%p (%s), stop_on=%p (%s), emits=%p, process=%p",
1228 class->start_on, start_on ? start_on : "",
1229 class->stop_on, stop_on ? stop_on : "",
1230 class->emits, class->process);
1231
1232 for (int i = 0; i < PROCESS_LAST; i++) {
1233 if (class->process[i]) {
1234 nih_debug ("Process[%d]=%p: script=%d, cmd='%s'",
1235 i, class->process[i],
1236 class->process[i]->script,
1237 class->process[i]->command);
1238 } else {
1239 nih_debug ("Process[%d]=%p",
1240 i, class->process[i]);
1241 }
1242
1243
1244 }
12271245
1228 nih_debug ("\tauthor='%s', description='%s'",1246 nih_debug ("\tauthor='%s', description='%s'",
1229 job->author, job->description);1247 class->author, class->description);
12301248
1231 if (env && *env) {1249 if (class->env && *class->env) {
1232 nih_debug ("\tenv:");1250 nih_local char *env = state_collapse_env ((const char **)class->env);
1233 i = 0;1251 nih_debug ("\tenv:%s", env);
1234 while ( *env ) {
1235 nih_debug ("\t\tenv[%d]='%s' (len=%u+1)",
1236 i, *env, strlen (*env));
1237 env++;
1238 ++i;
1239 }
1240 } else {1252 } else {
1241 nih_debug ("\tenv: none.");1253 nih_debug ("\tenv: none.");
1242 }1254 }
12431255
12441256 if (class->export && *class->export) {
1245 if (export && *export) {1257 nih_local char *export = state_collapse_env ((const char **)class->export);
1246 nih_debug ("\texport:");1258 nih_debug ("\texport:%s", export);
1247 i = 0;1259 } else {
1248 while ( *export ) {
1249 nih_debug ("\t\tenv[%d]='%s' (len=%u+1)",
1250 i, *export, strlen (*export));
1251 export++;
1252 ++i;
1253 }
1254 }
1255 else {
1256 nih_debug ("\texport: none");1260 nih_debug ("\texport: none");
1257 }1261 }
1262
1263 debug_show_jobs (class->instances);
1258}1264}
12591265
1260void1266void
@@ -1269,6 +1275,58 @@
1269}1275}
12701276
1271void1277void
1278debug_show_job (const Job *job)
1279{
1280 nih_local char *env = NULL;
1281 nih_local char *start_env = NULL;
1282 nih_local char *stop_env = NULL;
1283 nih_local char *stop_on = NULL;
1284
1285 nih_assert (job);
1286
1287 if (job->env)
1288 env = state_collapse_env ((const char **)job->env);
1289
1290 if (job->start_env)
1291 start_env = state_collapse_env ((const char **)job->start_env);
1292
1293 if (job->stop_env)
1294 stop_env = state_collapse_env ((const char **)job->stop_env);
1295
1296 if (job->stop_on)
1297 stop_on = event_operator_collapse (job->stop_on);
1298
1299 nih_debug ("Job %p: name=%s, class=%p (%s), path=%s, env='%s'"
1300 "start_env='%s', stop_env='%s', stop_on='%s', "
1301 "goal=%d, state=%d, failed=%d, exit_status=%d",
1302 job,
1303 job->name ? job->name : "",
1304 job->class, job->class->name,
1305 job->path,
1306 env ? env : "",
1307 start_env ? start_env : "",
1308 stop_env ? stop_env : "",
1309 stop_on ? stop_on : "",
1310 job->goal,
1311 job->state,
1312 job->failed,
1313 job->exit_status);
1314}
1315
1316void
1317debug_show_jobs (const NihHash *instances)
1318{
1319 nih_assert (instances);
1320
1321 nih_debug ("jobs:");
1322
1323 NIH_HASH_FOREACH (instances, iter) {
1324 Job *job = (Job *)iter;
1325 debug_show_job (job);
1326 }
1327}
1328
1329void
1272debug_show_event (const Event *event)1330debug_show_event (const Event *event)
1273{1331{
1274 nih_assert (event);1332 nih_assert (event);
@@ -1308,7 +1366,7 @@
1308 source, source->path, source->type, source->flag);1366 source, source->path, source->type, source->flag);
13091367
1310 nih_debug ("ConfSource %p files (%d):", source,1368 nih_debug ("ConfSource %p files (%d):", source,
1311 debug_count_hash_entries (source->files));1369 (int)debug_count_hash_entries (source->files));
13121370
1313 NIH_HASH_FOREACH (source->files, file_iter) {1371 NIH_HASH_FOREACH (source->files, file_iter) {
1314 ConfFile *file = (ConfFile *)file_iter;1372 ConfFile *file = (ConfFile *)file_iter;
13151373
=== modified file 'init/conf.h'
--- init/conf.h 2011-06-06 12:52:08 +0000
+++ init/conf.h 2012-11-14 14:49:20 +0000
@@ -39,9 +39,9 @@
39 * what they define within themselves.39 * what they define within themselves.
40 **/40 **/
41typedef enum conf_source_type {41typedef enum conf_source_type {
42 CONF_FILE,42 CONF_FILE, /* solitary file */
43 CONF_DIR,43 CONF_DIR, /* FIXME: */
44 CONF_JOB_DIR,44 CONF_JOB_DIR, /* directory tree of jobs */
45} ConfSourceType;45} ConfSourceType;
4646
4747
@@ -133,6 +133,7 @@
133#ifdef DEBUG133#ifdef DEBUG
134134
135/* used for debugging only */135/* used for debugging only */
136#include "job.h"
136137
137size_t138size_t
138debug_count_hash_entries (const NihHash *hash);139debug_count_hash_entries (const NihHash *hash);
@@ -142,7 +143,7 @@
142 __attribute__ ((unused));143 __attribute__ ((unused));
143144
144void145void
145debug_show_job_class (const JobClass *job)146debug_show_job_class (const JobClass *class)
146 __attribute__ ((unused));147 __attribute__ ((unused));
147148
148void149void
@@ -150,6 +151,13 @@
150 __attribute__ ((unused));151 __attribute__ ((unused));
151152
152void153void
154debug_show_job (const Job *job)
155 __attribute__ ((unused));
156
157void
158debug_show_jobs (const NihHash *instances)
159 __attribute__ ((unused));
160void
153debug_show_event (const Event *event)161debug_show_event (const Event *event)
154 __attribute__ ((unused));162 __attribute__ ((unused));
155163
156164
=== modified file 'init/control.c'
--- init/control.c 2012-03-16 21:02:13 +0000
+++ init/control.c 2012-11-14 14:49:20 +0000
@@ -54,6 +54,7 @@
54#include "conf.h"54#include "conf.h"
55#include "control.h"55#include "control.h"
56#include "errors.h"56#include "errors.h"
57#include "state.h"
5758
58#include "com.ubuntu.Upstart.h"59#include "com.ubuntu.Upstart.h"
5960
@@ -62,6 +63,8 @@
62static void control_disconnected (DBusConnection *conn);63static void control_disconnected (DBusConnection *conn);
63static void control_register_all (DBusConnection *conn);64static void control_register_all (DBusConnection *conn);
6465
66static void control_bus_flush (void);
67
65/**68/**
66 * use_session_bus:69 * use_session_bus:
67 *70 *
@@ -188,7 +191,8 @@
188void191void
189control_server_close (void)192control_server_close (void)
190{193{
191 nih_assert (control_server != NULL);194 if (! control_server)
195 return;
192196
193 dbus_server_disconnect (control_server);197 dbus_server_disconnect (control_server);
194 dbus_server_unref (control_server);198 dbus_server_unref (control_server);
@@ -292,7 +296,7 @@
292 *296 *
293 * This function is called when the connection to the D-Bus system bus,297 * This function is called when the connection to the D-Bus system bus,
294 * or a client connection to our D-Bus server, is dropped and our reference298 * or a client connection to our D-Bus server, is dropped and our reference
295 * is about to be list. We clear the connection from our current list299 * is about to be lost. We clear the connection from our current list
296 * and drop the control_bus global if relevant.300 * and drop the control_bus global if relevant.
297 **/301 **/
298static void302static void
@@ -301,6 +305,10 @@
301 nih_assert (conn != NULL);305 nih_assert (conn != NULL);
302306
303 if (conn == control_bus) {307 if (conn == control_bus) {
308 DBusError error;
309
310 dbus_error_init (&error);
311
304 nih_warn (_("Disconnected from system bus"));312 nih_warn (_("Disconnected from system bus"));
305313
306 control_bus = NULL;314 control_bus = NULL;
@@ -368,6 +376,8 @@
368 nih_assert (message != NULL);376 nih_assert (message != NULL);
369377
370 nih_info (_("Reloading configuration"));378 nih_info (_("Reloading configuration"));
379
380 /* This can only be called after deserialisation */
371 conf_reload ();381 conf_reload ();
372382
373 return 0;383 return 0;
@@ -806,3 +816,136 @@
806816
807 return 0;817 return 0;
808}818}
819
820/**
821 * control_bus_flush:
822 *
823 * Drain any remaining messages in the D-Bus queue.
824 **/
825static void
826control_bus_flush (void)
827{
828 control_init ();
829
830 if (! control_bus)
831 return;
832
833 while (dbus_connection_dispatch (control_bus) == DBUS_DISPATCH_DATA_REMAINS)
834 ;
835}
836
837/**
838 * control_prepare_reexec:
839 *
840 * Prepare for a re-exec by allowing the bus connection to be retained
841 * over re-exec and clearing all queued messages.
842 **/
843void
844control_prepare_reexec (void)
845{
846 control_init ();
847
848 /* Necessary to disallow further commands but also to allow the
849 * new instance to open the control server.
850 */
851 if (control_server)
852 control_server_close ();
853
854 control_bus_flush ();
855
856}
857
858
859/**
860 * control_conn_to_index:
861 *
862 * @event: event.
863 *
864 * Convert a control (DBusConnection) connection to an index number
865 * the list of control connections.
866 *
867 * Returns: connection index, or -1 on error.
868 **/
869int
870control_conn_to_index (const DBusConnection *connection)
871{
872 int conn_index = 0;
873 int found = FALSE;
874
875 nih_assert (connection);
876
877 NIH_LIST_FOREACH (control_conns, iter) {
878 NihListEntry *entry = (NihListEntry *)iter;
879 DBusConnection *conn = (DBusConnection *)entry->data;
880
881 if (connection == conn) {
882 found = TRUE;
883 break;
884 }
885
886 conn_index++;
887 }
888 if (! found)
889 return -1;
890
891 return conn_index;
892}
893
894/**
895 * control_conn_from_index:
896 *
897 * @conn_index: control connection index number.
898 *
899 * Lookup control connection based on index number.
900 *
901 * Returns: existing connection on success, or NULL if connection
902 * not found.
903 **/
904DBusConnection *
905control_conn_from_index (int conn_index)
906{
907 int i = 0;
908
909 nih_assert (conn_index >= 0);
910 nih_assert (control_conns);
911
912 NIH_LIST_FOREACH (control_conns, iter) {
913 NihListEntry *entry = (NihListEntry *)iter;
914 DBusConnection *conn = (DBusConnection *)entry->data;
915
916 if (i == conn_index)
917 return conn;
918 i++;
919 }
920
921 return NULL;
922}
923
924/**
925 * control_bus_release_name:
926 *
927 * Unregister well-known D-Bus name.
928 *
929 * Returns: 0 on success, -1 on raised error.
930 **/
931int
932control_bus_release_name (void)
933{
934 DBusError error;
935 int ret;
936
937 if (! control_bus)
938 return 0;
939
940 dbus_error_init (&error);
941 ret = dbus_bus_release_name (control_bus,
942 DBUS_SERVICE_UPSTART,
943 &error);
944 if (ret < 0) {
945 nih_dbus_error_raise (error.name, error.message);
946 dbus_error_free (&error);
947 return -1;
948 }
949
950 return 0;
951}
809952
=== modified file 'init/control.h'
--- init/control.h 2011-06-06 17:05:11 +0000
+++ init/control.h 2012-11-14 14:49:20 +0000
@@ -23,10 +23,13 @@
23#include <dbus/dbus.h>23#include <dbus/dbus.h>
2424
25#include <nih/macros.h>25#include <nih/macros.h>
26#include <nih/list.h>
2627
27#include <nih-dbus/dbus_connection.h>28#include <nih-dbus/dbus_connection.h>
28#include <nih-dbus/dbus_message.h>29#include <nih-dbus/dbus_message.h>
2930
31#include <json.h>
32
30/**33/**
31 * USE_SESSION_BUS_ENV:34 * USE_SESSION_BUS_ENV:
32 *35 *
@@ -90,6 +93,18 @@
9093
91void control_handle_bus_type (void);94void control_handle_bus_type (void);
9295
96void control_prepare_reexec (void);
97
98int control_conn_to_index (const DBusConnection *connection)
99 __attribute__ ((warn_unused_result));
100
101DBusConnection *
102control_conn_from_index (int conn_index)
103 __attribute__ ((warn_unused_result));
104
105int control_bus_release_name (void)
106 __attribute__ ((warn_unused_result));
107
93NIH_END_EXTERN108NIH_END_EXTERN
94109
95#endif /* INIT_CONTROL_H */110#endif /* INIT_CONTROL_H */
96111
=== modified file 'init/event.c'
--- init/event.c 2011-06-06 17:05:11 +0000
+++ init/event.c 2012-11-14 14:49:20 +0000
@@ -52,6 +52,14 @@
52static void event_pending_handle_jobs (Event *event);52static void event_pending_handle_jobs (Event *event);
53static void event_finished (Event *event);53static void event_finished (Event *event);
5454
55static const char * event_progress_enum_to_str (EventProgress progress)
56 __attribute__ ((warn_unused_result));
57
58static EventProgress
59event_progress_str_to_enum (const char *name)
60 __attribute__ ((warn_unused_result));
61
62extern json_object *json_events;
5563
56/**64/**
57 * events:65 * events:
@@ -501,3 +509,326 @@
501509
502 nih_free (event);510 nih_free (event);
503}511}
512
513/**
514 * event_serialise:
515 * @event: event to serialise.
516 *
517 * Convert @event into a JSON representation for serialisation.
518 * Caller must free returned value using json_object_put().
519 *
520 * Note that event->blocking is NOT serialised. Instead, those objects
521 * which event->blocking refers to encode the fact that they are blocked
522 * on the event (index in the JSON) in question. Deserialisation
523 * restores event->blocking via a 2-pass technique. event->blockers is
524 * recorded to allow a double-check.
525 *
526 * Returns: JSON-serialised Event object, or NULL on error.
527 **/
528json_object *
529event_serialise (const Event *event)
530{
531 json_object *json;
532 int session_index;
533
534 nih_assert (event);
535 nih_assert (event->name);
536
537 json = json_object_new_object ();
538 if (! json)
539 return NULL;
540
541 session_index = session_get_index (event->session);
542 if (session_index < 0)
543 goto error;
544
545 if (! state_set_json_int_var (json, "session", session_index))
546 goto error;
547
548 if (! state_set_json_string_var_from_obj (json, event, name))
549 goto error;
550
551 if (event->env) {
552 if (! state_set_json_str_array_from_obj (json, event, env))
553 goto error;
554 }
555
556 if (! state_set_json_int_var_from_obj (json, event, fd))
557 goto error;
558
559 if (! state_set_json_enum_var (json,
560 event_progress_enum_to_str,
561 "progress", event->progress))
562 goto error;
563
564 if (! state_set_json_int_var_from_obj (json, event, failed))
565 goto error;
566
567 if (! state_set_json_int_var_from_obj (json, event, blockers))
568 goto error;
569
570 if (! NIH_LIST_EMPTY (&event->blocking)) {
571 json_object *json_blocking;
572
573 json_blocking = state_serialise_blocking (&event->blocking);
574 if (! json_blocking)
575 goto error;
576
577 json_object_object_add (json, "blocking", json_blocking);
578 }
579
580 return json;
581
582error:
583 json_object_put (json);
584 return NULL;
585}
586
587/**
588 * event_serialise_all:
589 *
590 * Convert existing Event objects to JSON representation.
591 *
592 * Returns: JSON object containing array of Events, or NULL on error.
593 **/
594json_object *
595event_serialise_all (void)
596{
597 json_object *json;
598
599 event_init ();
600
601 json = json_object_new_array ();
602 if (! json)
603 return NULL;
604
605 NIH_LIST_FOREACH (events, iter) {
606 Event *event = (Event *)iter;
607 json_object *json_event;
608
609 json_event = event_serialise (event);
610
611 if (! json_event)
612 goto error;
613
614 json_object_array_add (json, json_event);
615 }
616
617 return json;
618
619error:
620 json_object_put (json);
621 return NULL;
622}
623
624/**
625 * event_deserialise:
626 * @json: JSON-serialised Event object to deserialise.
627 *
628 * Convert @json into an Event object.
629 *
630 * Returns: Event object, or NULL on error.
631 **/
632Event *
633event_deserialise (json_object *json)
634{
635 json_object *json_env;
636 Event *event = NULL;
637 nih_local char *name = NULL;
638 char **env = NULL;
639 int session_index = -1;
640
641 nih_assert (json);
642
643 if (! state_check_json_type (json, object))
644 return NULL;
645
646 if (! state_get_json_string_var_strict (json, "name", NULL, name))
647 goto error;
648
649 if (json_object_object_get (json, "env")) {
650 if (! state_get_json_var_full (json, "env", array, json_env))
651 goto error;
652
653 if (! state_deserialise_str_array (NULL, json_env, &env))
654 goto error;
655 }
656
657 event = event_new (NULL, name, env);
658 if (! event)
659 return NULL;
660
661 if (! state_get_json_int_var_to_obj (json, event, fd))
662 goto error;
663
664 if (! state_get_json_int_var (json, "session", session_index))
665 goto error;
666
667 /* can't check return value here (as all values are legitimate) */
668 event->session = session_from_index (session_index);
669
670 if (! state_get_json_enum_var (json,
671 event_progress_str_to_enum,
672 "progress", event->progress))
673 goto error;
674
675 if (! state_set_json_int_var_from_obj (json, event, failed))
676 goto error;
677
678 return event;
679
680error:
681 nih_free (event);
682 return NULL;
683}
684
685/**
686 * event_deserialise_all:
687 *
688 * @json: root of JSON-serialised state.
689 *
690 * Convert JSON representation of events back into Event objects.
691 *
692 * Returns: 0 on success, -1 on error.
693 **/
694int
695event_deserialise_all (json_object *json)
696{
697 Event *event;
698
699 nih_assert (json);
700
701 event_init ();
702
703 nih_assert (NIH_LIST_EMPTY (events));
704 json_events = json_object_object_get (json, "events");
705
706 if (! json_events)
707 goto error;
708
709 if (! state_check_json_type (json_events, array))
710 goto error;
711
712 for (int i = 0; i < json_object_array_length (json_events); i++) {
713 json_object *json_event;
714
715 json_event = json_object_array_get_idx (json_events, i);
716 if (! json_event)
717 goto error;
718
719 if (! state_check_json_type (json_event, object))
720 goto error;
721
722 event = event_deserialise (json_event);
723 if (! event)
724 goto error;
725 }
726
727 return 0;
728
729error:
730 return -1;
731}
732
733/**
734 * event_progress_enum_to_str:
735 *
736 * @progress: event progress.
737 *
738 * Convert EventProgress to a string representation.
739 *
740 * Returns: string representation of @progress, or NULL if not known.
741 **/
742static const char *
743event_progress_enum_to_str (EventProgress progress)
744{
745 state_enum_to_str (EVENT_PENDING, progress);
746 state_enum_to_str (EVENT_HANDLING, progress);
747 state_enum_to_str (EVENT_FINISHED, progress);
748
749 return NULL;
750}
751
752/**
753 * event_progress_str_to_enum:
754 *
755 * @: name of EventOperator value.
756 *
757 * Convert string representation of EventProgress into a
758 * real EventProgress value.
759 *
760 * Returns: EventProgress representing @progress, or -1 if not known.
761 **/
762static EventProgress
763event_progress_str_to_enum (const char *progress)
764{
765 state_str_to_enum (EVENT_PENDING, progress);
766 state_str_to_enum (EVENT_HANDLING, progress);
767 state_str_to_enum (EVENT_FINISHED, progress);
768
769 return -1;
770}
771
772/**
773 * event_to_index:
774 *
775 * @event: event.
776 *
777 * Convert an Event to an index number within
778 * the list of events.
779 *
780 * Returns: event index, or -1 on error.
781 **/
782int
783event_to_index (const Event *event)
784{
785 int event_index = 0;
786 int found = FALSE;
787
788 nih_assert (event);
789 event_init ();
790
791 NIH_LIST_FOREACH (events, iter) {
792 Event *tmp = (Event *)iter;
793
794 if (tmp == event) {
795 found = TRUE;
796 break;
797 }
798
799 event_index++;
800 }
801
802 if (! found)
803 return -1;
804
805 return event_index;
806}
807
808/**
809 * event_from_index:
810 *
811 * @event_index: event index number.
812 *
813 * Lookup Event based on index number.
814 *
815 * Returns: existing Event on success, or NULL if event not found.
816 **/
817Event *
818event_from_index (int event_index)
819{
820 int i = 0;
821
822 nih_assert (event_index >= 0);
823 event_init ();
824
825 NIH_LIST_FOREACH (events, iter) {
826 Event *event = (Event *)iter;
827
828 if (i == event_index)
829 return event;
830 i++;
831 }
832
833 return NULL;
834}
504835
=== modified file 'init/event.h'
--- init/event.h 2011-06-06 17:05:11 +0000
+++ init/event.h 2012-11-14 14:49:20 +0000
@@ -24,7 +24,9 @@
24#include <nih/list.h>24#include <nih/list.h>
2525
26#include "session.h"26#include "session.h"
27#include "state.h"
2728
29#include <json.h>
2830
29/**31/**
30 * EventProgress:32 * EventProgress:
@@ -45,6 +47,8 @@
45 * @session: session the event is attached to,47 * @session: session the event is attached to,
46 * @name: string name of the event,48 * @name: string name of the event,
47 * @env: NULL-terminated array of environment variables,49 * @env: NULL-terminated array of environment variables,
50 * @fd: open file descriptor associated with a particular
51 * socket-bridge socket (see socket-event(8)),
48 * @progress: progress of event,52 * @progress: progress of event,
49 * @failed: whether this event has failed,53 * @failed: whether this event has failed,
50 * @blockers: number of blockers for finishing,54 * @blockers: number of blockers for finishing,
@@ -93,6 +97,24 @@
9397
94void event_poll (void);98void event_poll (void);
9599
100json_object *event_serialise (const Event *event)
101 __attribute__ ((malloc, warn_unused_result));
102
103Event *event_deserialise (json_object *json)
104 __attribute__ ((malloc, warn_unused_result));
105
106json_object * event_serialise_all (void)
107 __attribute__ ((malloc, warn_unused_result));
108
109int event_deserialise_all (json_object *json)
110 __attribute__ ((warn_unused_result));
111
112int event_to_index (const Event *event)
113 __attribute__ ((warn_unused_result));
114
115Event * event_from_index (int event_index)
116 __attribute__ ((warn_unused_result));
117
96NIH_END_EXTERN118NIH_END_EXTERN
97119
98#endif /* INIT_EVENT_H */120#endif /* INIT_EVENT_H */
99121
=== modified file 'init/event_operator.c'
--- init/event_operator.c 2010-12-10 07:18:34 +0000
+++ init/event_operator.c 2012-11-14 14:49:20 +0000
@@ -552,14 +552,31 @@
552 return *env;552 return *env;
553}553}
554554
555/**
556 * event_operator_fds:
557 * @root: operator tree to update,
558 * @parent: parent object for new array,
559 * @fds: output location for array of ints
560 * @num_fds: number of elements in @fds,
561 * @env: NULL-terminated array of environment variables to add to,
562 * @len: length of @env,
563 * @key: key of variable to contain event names.
564 *
565 * Iterate over tree rooted at @root adding all file descriptor values found
566 * to the dynamically allocated @fds array. In addition, all file
567 * descriptors found are also added to @env will contain a new entry with key @key
568 * whose value is a space-separated list of file descriptor numbers.
569 *
570 * Returns: 1 on success, NULL on failure.
571 **/
555int *572int *
556event_operator_fds (EventOperator *root,573event_operator_fds (EventOperator *root,
557 const void *parent,574 const void *parent,
558 int **fds,575 int **fds,
559 size_t *num_fds,576 size_t *num_fds,
560 char ***env,577 char ***env,
561 size_t *len,578 size_t *len,
562 const char *key)579 const char *key)
563{580{
564 nih_local char *evlist = NULL;581 nih_local char *evlist = NULL;
565582
@@ -696,3 +713,154 @@
696 }713 }
697 }714 }
698}715}
716
717/**
718 * event_operator_collapse:
719 *
720 * @condition: start on/stop on condition.
721 *
722 * Collapsed condition will be fully bracketed. Note that as such it may
723 * not be lexicographically identical to the original expression that
724 * resulted in @condition, but it will be logically identical.
725 *
726 * The condition is reconstructed from the EventOperator tree by using
727 * a post-order traversal (since this allows the tree to be traversed
728 * bottom-to-top). Leaf nodes (EVENT_MATCH) are ignored when visited,
729 * allowing non-leaf nodes (EVENT_AND and EVENT_OR) to simply grab the
730 * value of their children, construct a bracketed expression and add it
731 * to a stack. If a child is a leaf node, the value can be read
732 * directly. If a child is a non-leaf node, the value is obtained by
733 * popping the stack before adding the new value back onto the stack.
734 * When finally the root node is visited, the final expression can be
735 * removed from the stack and returned. A single-node tree (comprising a
736 * lone EVENT_MATCH at the root) is special-cased.
737 *
738 * Returns: newly-allocated flattened string representing @condition
739 * on success, or NULL on error.
740 **/
741char *
742event_operator_collapse (EventOperator *condition)
743{
744 nih_local NihList *stack = NULL;
745 NihListEntry *latest = NULL;
746 NihTree *root;
747
748 nih_assert (condition);
749
750 root = &condition->node;
751
752 stack = NIH_MUST (nih_list_new (NULL));
753
754 NIH_TREE_FOREACH_POST (root, iter) {
755 EventOperator *oper = (EventOperator *)iter;
756 EventOperator *left;
757 EventOperator *right;
758 NihListEntry *expr;
759 NihTree *tree;
760 nih_local char *left_expr = NULL;
761 nih_local char *right_expr = NULL;
762
763 tree = &oper->node;
764
765 left = (EventOperator *)tree->left;
766 right = (EventOperator *)tree->right;
767
768 if (oper->type == EVENT_MATCH) {
769 /* Entire expression comprises a single event,
770 * so push it and leave.
771 */
772 if (tree == root) {
773 nih_local char *env = NULL;
774
775 if (oper->env)
776 env = NIH_MUST (state_collapse_env ((const char **)oper->env));
777
778 expr = NIH_MUST (nih_list_entry_new (stack));
779 expr->str = NIH_MUST (nih_sprintf (expr, "%s%s%s",
780 oper->name,
781 env ? " " : "",
782 env ? env : ""));
783 nih_list_add_after (stack, &expr->entry);
784 break;
785 }
786 else {
787 /* We build the expression from visiting the logical
788 * operators (and their children) only.
789 */
790 continue;
791 }
792 }
793
794 /* oper cannot now be a leaf node, so must have children */
795 nih_assert (left);
796 nih_assert (right);
797
798 expr = NIH_MUST (nih_list_entry_new (stack));
799
800 /* If a child is an EVENT_MATCH, expand its event
801 * details and push onto the stack.
802 * If a child is not an EVENT_MATCH, to obtains it
803 * value, pop the stack.
804 *
805 * Having obtained the child values, construct a new
806 * bracketed expression and push onto the stack.
807 *
808 * Note that we must consider the right child first.
809 * This is because since the tree is traversed
810 * left-child first, any value pushed onto the stack by
811 * a right child is at the top so must be removed before
812 * any left child value. Failure to do this results in
813 * tree reflection which although logically equivalent
814 * to the original could confuse as the resultant
815 * expression will look rather different.
816 */
817 if (right->type != EVENT_MATCH) {
818 nih_assert (! NIH_LIST_EMPTY (stack));
819
820 latest = (NihListEntry *)nih_list_remove (stack->next);
821 right_expr = NIH_MUST (nih_strdup (NULL, latest->str));
822 } else {
823 nih_local char *env = NULL;
824
825 if (right->env)
826 env = NIH_MUST (state_collapse_env ((const char **)right->env));
827
828 right_expr = NIH_MUST (nih_sprintf (NULL, "%s%s%s",
829 right->name,
830 env ? " " : "",
831 env ? env : ""));
832 }
833
834 if (left->type != EVENT_MATCH) {
835 nih_assert (! NIH_LIST_EMPTY (stack));
836
837 latest = (NihListEntry *)nih_list_remove (stack->next);
838 left_expr = NIH_MUST (nih_strdup (NULL, latest->str));
839 } else {
840 nih_local char *env = NULL;
841
842 if (left->env)
843 env = NIH_MUST (state_collapse_env ((const char **)left->env));
844
845 left_expr = NIH_MUST (nih_sprintf (NULL, "%s%s%s",
846 left->name,
847 env ? " " : "",
848 env ? env : ""));
849 }
850
851 expr->str = NIH_MUST (nih_sprintf (expr, "(%s %s %s)",
852 left_expr,
853 oper->type == EVENT_OR ? "or" : "and",
854 right_expr));
855
856 nih_list_add_after (stack, &expr->entry);
857 }
858
859 nih_assert (! NIH_LIST_EMPTY (stack));
860
861 latest = (NihListEntry *)nih_list_remove (stack->next);
862
863 nih_assert (NIH_LIST_EMPTY (stack));
864
865 return NIH_MUST (nih_strdup (NULL, latest->str));
866}
699867
=== modified file 'init/event_operator.h'
--- init/event_operator.h 2010-12-10 07:18:34 +0000
+++ init/event_operator.h 2012-11-14 14:49:20 +0000
@@ -96,10 +96,10 @@
96 const void *parent, size_t *len,96 const void *parent, size_t *len,
97 const char *key);97 const char *key);
98int *98int *
99event_operator_fds (EventOperator *root,99event_operator_fds (EventOperator *root,
100 const void *parent,100 const void *parent,
101 int **fds,101 int **fds,
102 size_t *num_fds,102 size_t *num_fds,
103 char ***env,103 char ***env,
104 size_t *len,104 size_t *len,
105 const char *key);105 const char *key);
@@ -108,6 +108,9 @@
108108
109void event_operator_reset (EventOperator *root);109void event_operator_reset (EventOperator *root);
110110
111char *event_operator_collapse (EventOperator *condition)
112 __attribute__ ((malloc, warn_unused_result));
113
111NIH_END_EXTERN114NIH_END_EXTERN
112115
113#endif /* INIT_EVENT_OPERATOR_H */116#endif /* INIT_EVENT_OPERATOR_H */
114117
=== modified file 'init/job.c'
--- init/job.c 2012-03-07 12:13:28 +0000
+++ init/job.c 2012-11-14 14:49:20 +0000
@@ -23,7 +23,6 @@
23# include <config.h>23# include <config.h>
24#endif /* HAVE_CONFIG_H */24#endif /* HAVE_CONFIG_H */
2525
26
27#include <sys/types.h>26#include <sys/types.h>
2827
29#include <errno.h>28#include <errno.h>
@@ -55,10 +54,43 @@
55#include "event_operator.h"54#include "event_operator.h"
56#include "blocked.h"55#include "blocked.h"
57#include "control.h"56#include "control.h"
57#include "parse_job.h"
58#include "state.h"
5859
59#include "com.ubuntu.Upstart.Job.h"60#include "com.ubuntu.Upstart.Job.h"
60#include "com.ubuntu.Upstart.Instance.h"61#include "com.ubuntu.Upstart.Instance.h"
6162
63/* Prototypes for static functions */
64static const char *
65job_goal_enum_to_str (JobGoal goal)
66 __attribute__ ((warn_unused_result));
67
68static JobGoal job_goal_str_to_enum (const char *goal)
69 __attribute__ ((warn_unused_result));
70
71static const char *
72job_state_enum_to_str (JobState state)
73 __attribute__ ((warn_unused_result));
74
75static JobState
76job_state_str_to_enum (const char *state)
77 __attribute__ ((warn_unused_result));
78
79static const char *
80job_trace_state_enum_to_str (TraceState state)
81 __attribute__ ((warn_unused_result));
82
83static TraceState
84job_trace_state_str_to_enum (const char *state)
85 __attribute__ ((warn_unused_result));
86
87static json_object *
88job_serialise_kill_timer (NihTimer *timer)
89 __attribute__ ((malloc, warn_unused_result));
90
91static NihTimer *
92job_deserialise_kill_timer (json_object *json)
93 __attribute__ ((malloc, warn_unused_result));
6294
63/**95/**
64 * job_new:96 * job_new:
@@ -119,6 +151,7 @@
119 job->stop_env = NULL;151 job->stop_env = NULL;
120152
121 job->stop_on = NULL;153 job->stop_on = NULL;
154
122 if (class->stop_on) {155 if (class->stop_on) {
123 job->stop_on = event_operator_copy (job, class->stop_on);156 job->stop_on = event_operator_copy (job, class->stop_on);
124 if (! job->stop_on)157 if (! job->stop_on)
@@ -152,10 +185,10 @@
152 nih_list_init (&job->blocking);185 nih_list_init (&job->blocking);
153186
154 job->kill_timer = NULL;187 job->kill_timer = NULL;
155 job->kill_process = -1;188 job->kill_process = PROCESS_INVALID;
156189
157 job->failed = FALSE;190 job->failed = FALSE;
158 job->failed_process = -1;191 job->failed_process = PROCESS_INVALID;
159 job->exit_status = 0;192 job->exit_status = 0;
160193
161 job->respawn_time = 0;194 job->respawn_time = 0;
@@ -262,7 +295,7 @@
262 * will finish naturally, so all we need do is change the goal and295 * will finish naturally, so all we need do is change the goal and
263 * we'll change direction through the state machine at that point.296 * we'll change direction through the state machine at that point.
264 *297 *
265 * The exceptions are the natural rest sates of waiting and a298 * The exceptions are the natural rest states of waiting and a
266 * running process; these need induction to get them moving.299 * running process; these need induction to get them moving.
267 */300 */
268 switch (goal) {301 switch (goal) {
@@ -358,7 +391,7 @@
358391
359 /* Clear any old failed information */392 /* Clear any old failed information */
360 job->failed = FALSE;393 job->failed = FALSE;
361 job->failed_process = -1;394 job->failed_process = PROCESS_INVALID;
362 job->exit_status = 0;395 job->exit_status = 0;
363396
364 job->blocker = job_emit_event (job);397 job->blocker = job_emit_event (job);
@@ -882,7 +915,7 @@
882 * if it was a respawn failure, we use the special "respawn"915 * if it was a respawn failure, we use the special "respawn"
883 * argument instead of the process name,916 * argument instead of the process name,
884 */917 */
885 if ((job->failed_process != (ProcessType)-1)918 if ((job->failed_process != PROCESS_INVALID)
886 && (job->exit_status != -1)) {919 && (job->exit_status != -1)) {
887 NIH_MUST (environ_set (&env, NULL, &len, TRUE,920 NIH_MUST (environ_set (&env, NULL, &len, TRUE,
888 "PROCESS=%s",921 "PROCESS=%s",
@@ -907,7 +940,7 @@
907 NIH_MUST (environ_set (&env, NULL, &len, TRUE,940 NIH_MUST (environ_set (&env, NULL, &len, TRUE,
908 "EXIT_STATUS=%d", job->exit_status));941 "EXIT_STATUS=%d", job->exit_status));
909 }942 }
910 } else if (job->failed_process != (ProcessType)-1) {943 } else if (job->failed_process != PROCESS_INVALID) {
911 NIH_MUST (environ_set (&env, NULL, &len, TRUE,944 NIH_MUST (environ_set (&env, NULL, &len, TRUE,
912 "PROCESS=%s",945 "PROCESS=%s",
913 process_name (job->failed_process)));946 process_name (job->failed_process)));
@@ -1492,3 +1525,689 @@
14921525
1493 return 0;1526 return 0;
1494}1527}
1528
1529/**
1530 * job_serialise:
1531 * @job: job serialise.
1532 *
1533 * Convert @job into a JSON representation for serialisation.
1534 * Caller must free returned value using json_object_put().
1535 *
1536 * Note that the 'class' element is not encoded - it is assumed the
1537 * caller will encode the returned JSON Job object as a child of the
1538 * parent JSON-encoded JobClass object so a reference is not required.
1539 *
1540 * Returns: JSON-serialised Job object, or NULL on error.
1541 **/
1542json_object *
1543job_serialise (const Job *job)
1544{
1545 json_object *json;
1546 json_object *json_pid;
1547 json_object *json_fds;
1548 json_object *json_logs;
1549 nih_local char *stop_on = NULL;
1550
1551 nih_assert (job);
1552
1553 json = json_object_new_object ();
1554 if (! json)
1555 return NULL;
1556
1557 if (! state_set_json_string_var_from_obj (json, job, name))
1558 goto error;
1559
1560 if (! state_set_json_string_var_from_obj (json, job, path))
1561 goto error;
1562
1563 if (! state_set_json_enum_var (json,
1564 job_goal_enum_to_str,
1565 "goal", job->goal))
1566 goto error;
1567
1568 if (! state_set_json_enum_var (json,
1569 job_state_enum_to_str,
1570 "state", job->state))
1571 goto error;
1572
1573 if (! state_set_json_str_array_from_obj (json, job, env))
1574 goto error;
1575
1576 if (! state_set_json_str_array_from_obj (json, job, start_env))
1577 goto error;
1578
1579 if (! state_set_json_str_array_from_obj (json, job, stop_env))
1580 goto error;
1581
1582 if (job->stop_on) {
1583 stop_on = event_operator_collapse (job->stop_on);
1584 if (! stop_on)
1585 goto error;
1586
1587 if (! state_set_json_string_var (json, "stop_on", stop_on))
1588 goto error;
1589 }
1590
1591 json_fds = state_serialise_int_array (int, job->fds, job->num_fds);
1592 if (! json_fds)
1593 goto error;
1594
1595 json_object_object_add (json, "fds", json_fds);
1596
1597 json_pid = state_serialise_int_array (pid_t, job->pid,
1598 PROCESS_LAST);
1599 if (! json_pid)
1600 goto error;
1601
1602 json_object_object_add (json, "pid", json_pid);
1603
1604 /* Encode the blocking event as an index number which represents
1605 * the event's position in the JSON events array.
1606 */
1607 if (job->blocker) {
1608 int event_index;
1609
1610 event_index = event_to_index (job->blocker);
1611 if (event_index < 0)
1612 goto error;
1613
1614 /* For consistency, it would be preferable to encode the
1615 * event name, but the index is actually better since it is
1616 * simple and unambiguous - encoding the name would also require
1617 * us to encode the env to make the event unique.
1618 */
1619 if (! state_set_json_int_var (json, "blocker", event_index))
1620 goto error;
1621
1622 }
1623
1624 if (! NIH_LIST_EMPTY (&job->blocking)) {
1625 json_object *json_blocking;
1626
1627 json_blocking = state_serialise_blocking (&job->blocking);
1628 if (! json_blocking)
1629 goto error;
1630
1631 json_object_object_add (json, "blocking", json_blocking);
1632 }
1633
1634 /* conditionally encode kill timer */
1635 if (job->kill_timer) {
1636 json_object *kill_timer;
1637
1638 kill_timer = job_serialise_kill_timer (job->kill_timer);
1639
1640 if (! kill_timer)
1641 goto error;
1642
1643 json_object_object_add (json, "kill_timer", kill_timer);
1644 }
1645
1646 if (! state_set_json_enum_var (json,
1647 process_type_enum_to_str,
1648 "kill_process", job->kill_process))
1649 goto error;
1650
1651 if (! state_set_json_int_var_from_obj (json, job, failed))
1652 goto error;
1653
1654 if (! state_set_json_enum_var (json,
1655 process_type_enum_to_str,
1656 "failed_process", job->failed_process))
1657 goto error;
1658
1659 if (! state_set_json_int_var_from_obj (json, job, exit_status))
1660 goto error;
1661
1662 if (! state_set_json_int_var_from_obj (json, job, respawn_time))
1663 goto error;
1664
1665 if (! state_set_json_int_var_from_obj (json, job, respawn_count))
1666 goto error;
1667
1668 if (! state_set_json_int_var_from_obj (json, job, trace_forks))
1669 goto error;
1670
1671 if (! state_set_json_enum_var (json,
1672 job_trace_state_enum_to_str,
1673 "trace_state", job->trace_state))
1674 goto error;
1675
1676 json_logs = json_object_new_array ();
1677
1678 if (! json_logs)
1679 return json;
1680
1681 for (int process = 0; process < PROCESS_LAST; process++) {
1682 json_object *json_log;
1683
1684 json_log = log_serialise (job->log[process]);
1685 if (! json_log)
1686 goto error;
1687
1688 if (json_object_array_add (json_logs, json_log) < 0)
1689 goto error;
1690 }
1691
1692 json_object_object_add (json, "log", json_logs);
1693
1694 return json;
1695
1696error:
1697 json_object_put (json);
1698 return NULL;
1699}
1700
1701/**
1702 * job_serialise_all:
1703 *
1704 * Convert existing Session objects to JSON representation.
1705 *
1706 * Returns: JSON object containing array of Job objects, or NULL on error.
1707 **/
1708json_object *
1709job_serialise_all (const NihHash *jobs)
1710{
1711 int count = 0;
1712 json_object *json;
1713
1714 nih_assert (jobs);
1715
1716 json = json_object_new_array ();
1717 if (! json)
1718 return NULL;
1719
1720 NIH_HASH_FOREACH (jobs, iter) {
1721 json_object *json_job;
1722 Job *job = (Job *)iter;
1723
1724 count++;
1725 json_job = job_serialise (job);
1726
1727 if (! json_job)
1728 goto error;
1729
1730 json_object_array_add (json, json_job);
1731 }
1732
1733 /* Raise an error to avoid serialising job classes with
1734 * no associated jobs.
1735 */
1736 if (! count)
1737 goto error;
1738
1739 return json;
1740
1741error:
1742 json_object_put (json);
1743 return NULL;
1744}
1745
1746/**
1747 * job_deserialise:
1748 * @parent: job class for JSON-encoded jobs,
1749 * @json: JSON-serialised Job object to deserialise.
1750 *
1751 * XXX: All events must have been deserialised prior to this function
1752 * XXX: being called.
1753 *
1754 * Returns: Job object, or NULL on error.
1755 **/
1756Job *
1757job_deserialise (JobClass *parent, json_object *json)
1758{
1759 nih_local char *name = NULL;
1760 Job *job = NULL;
1761 json_object *json_kill_timer;
1762 json_object *blocker;
1763 json_object *json_fds;
1764 json_object *json_pid;
1765 json_object *json_logs;
1766 size_t len;
1767 int ret;
1768
1769 nih_assert (parent);
1770 nih_assert (json);
1771
1772 if (! state_check_json_type (json, object))
1773 return NULL;
1774
1775 if (! state_get_json_string_var_strict (json, "name", NULL, name))
1776 goto error;
1777
1778 job = NIH_MUST (job_new (parent, name));
1779
1780 if (! job)
1781 return NULL;
1782
1783 if (! state_get_json_string_var_to_obj_strict (json, job, path))
1784 goto error;
1785
1786 if (! state_get_json_enum_var (json,
1787 job_goal_str_to_enum,
1788 "goal", job->goal))
1789 goto error;
1790
1791 if (! state_get_json_enum_var (json,
1792 job_state_str_to_enum,
1793 "state", job->state))
1794 goto error;
1795
1796 if (! state_get_json_env_array_to_obj (json, job, env))
1797 goto error;
1798
1799 if (! state_get_json_env_array_to_obj (json, job, start_env))
1800 goto error;
1801
1802 if (! state_get_json_env_array_to_obj (json, job, stop_env))
1803 goto error;
1804
1805 if (json_object_object_get (json, "stop_on")) {
1806 nih_local char *stop_on = NULL;
1807
1808 if (! state_get_json_string_var_strict (json, "stop_on", NULL, stop_on))
1809 goto error;
1810
1811 if (*stop_on) {
1812 nih_local JobClass *tmp = NULL;
1813
1814 tmp = NIH_MUST (job_class_new (NULL, "tmp", NULL));
1815
1816 tmp->stop_on = parse_on_simple (tmp, "stop", stop_on);
1817 if (! tmp->stop_on) {
1818 NihError *err;
1819
1820 err = nih_error_get ();
1821
1822 nih_error ("%s %s: %s",
1823 _("BUG"),
1824 _("instance 'stop on' parse error"),
1825 err->message);
1826
1827 nih_free (err);
1828
1829 goto error;
1830 }
1831
1832 nih_free (job->stop_on);
1833 job->stop_on = event_operator_copy (job, tmp->stop_on);
1834 if (! job->stop_on)
1835 goto error;
1836 }
1837 }
1838
1839 /* fds and num_fds handled by caller */
1840 /* pid handled by caller */
1841
1842 /* blocking is handled by state_deserialise_blocking() */
1843
1844 blocker = json_object_object_get (json, "blocker");
1845
1846 if (blocker) {
1847 int event_index = -1;
1848
1849 if (! state_get_json_int_var (json, "blocker", event_index))
1850 goto error;
1851 job->blocker = event_from_index (event_index);
1852
1853 if (! job->blocker)
1854 goto error;
1855 }
1856
1857 if (! state_get_json_enum_var (json,
1858 process_type_str_to_enum,
1859 "kill_process", job->kill_process))
1860 goto error;
1861
1862 /* Check to see if a kill timer exists first since we do not
1863 * want to end up creating a real but empty timer.
1864 */
1865 json_kill_timer = json_object_object_get (json, "kill_timer");
1866
1867 if (json_kill_timer) {
1868 /* Found a partial kill timer, so create a new one and
1869 * adjust its due time. By the time the main loop gets
1870 * called, the due time will probably be in the past
1871 * such that the job will be stopped.
1872 *
1873 * To be completely fair we should:
1874 *
1875 * - encode the time at the point of serialisation in a
1876 * JSON 'meta' header.
1877 * - query the time post-deserialisation and calculate
1878 * the delta (being the time to perform the stateful
1879 * re-exec).
1880 * - add that time to all jobs with active kill timers
1881 * to give their processes the full amount of time to
1882 * end.
1883 */
1884 nih_local NihTimer *kill_timer = job_deserialise_kill_timer (json_kill_timer);
1885 if (! kill_timer)
1886 goto error;
1887
1888 nih_assert (job->kill_process);
1889 job_process_set_kill_timer (job, job->kill_process,
1890 kill_timer->timeout);
1891 job_process_adj_kill_timer (job, kill_timer->due);
1892 }
1893
1894 if (! state_get_json_int_var_to_obj (json, job, failed))
1895 goto error;
1896
1897 if (! state_get_json_enum_var (json,
1898 process_type_str_to_enum,
1899 "failed_process", job->failed_process))
1900 goto error;
1901
1902 if (! state_get_json_int_var_to_obj (json, job, exit_status))
1903 goto error;
1904
1905 if (! state_get_json_int_var_to_obj (json, job, respawn_time))
1906 goto error;
1907
1908 if (! state_get_json_int_var_to_obj (json, job, respawn_count))
1909 goto error;
1910
1911 json_fds = json_object_object_get (json, "fds");
1912 if (! json_fds)
1913 goto error;
1914
1915 ret = state_deserialise_int_array (job, json_fds,
1916 int, &job->fds, &job->num_fds);
1917 if (ret < 0)
1918 goto error;
1919
1920 json_pid = json_object_object_get (json, "pid");
1921 if (! json_pid)
1922 goto error;
1923
1924 ret = state_deserialise_int_array (job, json_pid,
1925 pid_t, &job->pid, &len);
1926 if (ret < 0)
1927 goto error;
1928
1929 if (len != PROCESS_LAST)
1930 goto error;
1931
1932 if (! state_get_json_int_var_to_obj (json, job, trace_forks))
1933 goto error;
1934
1935 if (! state_get_json_enum_var (json,
1936 job_trace_state_str_to_enum,
1937 "trace_state", job->trace_state))
1938 goto error;
1939
1940 json_logs = json_object_object_get (json, "log");
1941
1942 if (! json_logs)
1943 goto error;
1944
1945 if (! state_check_json_type (json_logs, array))
1946 goto error;
1947
1948 for (int process = 0; process < PROCESS_LAST; process++) {
1949 json_object *json_log;
1950
1951 json_log = json_object_array_get_idx (json_logs, process);
1952 if (! json_log)
1953 goto error;
1954
1955 /* NULL if there was no log configured, or we failed to
1956 * deserialise it; either way, this should be non-fatal.
1957 */
1958 job->log[process] = log_deserialise (job->log, json_log);
1959 }
1960
1961 return job;
1962
1963error:
1964 nih_free (job);
1965 return NULL;
1966}
1967
1968/**
1969 * job_deserialise_all:
1970 *
1971 * @parent: job class for JSON-encoded jobs,
1972 * @json: root of JSON-serialised state.
1973 *
1974 * Convert JSON representation of jobs back into Job objects associated
1975 * with @parent.
1976 *
1977 * Returns: 0 on success, -1 on error.
1978 **/
1979int
1980job_deserialise_all (JobClass *parent, json_object *json)
1981{
1982 json_object *json_jobs;
1983 Job *job;
1984
1985 nih_assert (parent);
1986 nih_assert (json);
1987
1988 json_jobs = json_object_object_get (json, "jobs");
1989
1990 if (! json_jobs)
1991 goto error;
1992
1993 if (! state_check_json_type (json_jobs, array))
1994 goto error;
1995
1996 for (int i = 0; i < json_object_array_length (json_jobs); i++) {
1997 json_object *json_job;
1998
1999 json_job = json_object_array_get_idx (json_jobs, i);
2000 if (! json_job)
2001 goto error;
2002
2003 if (! state_check_json_type (json_job, object))
2004 goto error;
2005
2006 job = job_deserialise (parent, json_job);
2007 if (! job)
2008 goto error;
2009 }
2010
2011 return 0;
2012
2013error:
2014 return -1;
2015}
2016
2017/**
2018 * job_goal_enum_to_str:
2019 *
2020 * @goal: JobGoal.
2021 *
2022 * Convert JobGoal to a string representation.
2023 *
2024 * Returns: string representation of @goal, or NULL if not known.
2025 **/
2026static const char *
2027job_goal_enum_to_str (JobGoal goal)
2028{
2029 state_enum_to_str (JOB_STOP, goal);
2030 state_enum_to_str (JOB_START, goal);
2031 state_enum_to_str (JOB_RESPAWN, goal);
2032
2033 return NULL;
2034}
2035
2036/**
2037 * job_goal_str_to_enum:
2038 *
2039 * @goal: string JobGoal value.
2040 *
2041 * Convert @goal back into enum value.
2042 *
2043 * Returns: JobGoal representation of @goal, or -1 if not known.
2044 **/
2045static JobGoal
2046job_goal_str_to_enum (const char *goal)
2047{
2048 state_str_to_enum (JOB_STOP, goal);
2049 state_str_to_enum (JOB_START, goal);
2050 state_str_to_enum (JOB_RESPAWN, goal);
2051
2052 return -1;
2053}
2054
2055/**
2056 * job_state_enum_to_str:
2057 *
2058 * @state: JobState.
2059 *
2060 * Convert JobState to a string representation.
2061 *
2062 * Returns: string representation of @state, or NULL if not known.
2063 **/
2064static const char *
2065job_state_enum_to_str (JobState state)
2066{
2067 state_enum_to_str (JOB_WAITING, state);
2068 state_enum_to_str (JOB_STARTING, state);
2069 state_enum_to_str (JOB_PRE_START, state);
2070 state_enum_to_str (JOB_SPAWNED, state);
2071 state_enum_to_str (JOB_POST_START, state);
2072 state_enum_to_str (JOB_RUNNING, state);
2073 state_enum_to_str (JOB_PRE_STOP, state);
2074 state_enum_to_str (JOB_STOPPING, state);
2075 state_enum_to_str (JOB_KILLED, state);
2076 state_enum_to_str (JOB_POST_STOP, state);
2077
2078 return NULL;
2079}
2080
2081/**
2082 * job_state_str_to_enum:
2083 *
2084 * @state: string JobState value.
2085 *
2086 * Convert @state back into enum value.
2087 *
2088 * Returns: JobState representation of @state, or -1 if not known.
2089 **/
2090static JobState
2091job_state_str_to_enum (const char *state)
2092{
2093 state_str_to_enum (JOB_WAITING, state);
2094 state_str_to_enum (JOB_STARTING, state);
2095 state_str_to_enum (JOB_PRE_START, state);
2096 state_str_to_enum (JOB_SPAWNED, state);
2097 state_str_to_enum (JOB_POST_START, state);
2098 state_str_to_enum (JOB_RUNNING, state);
2099 state_str_to_enum (JOB_PRE_STOP, state);
2100 state_str_to_enum (JOB_STOPPING, state);
2101 state_str_to_enum (JOB_KILLED, state);
2102 state_str_to_enum (JOB_POST_STOP, state);
2103
2104 return -1;
2105}
2106
2107/**
2108 * job_state_enum_to_str:
2109 *
2110 * @state: TraceState.
2111 *
2112 * Convert TraceState to a string representation.
2113 *
2114 * Returns: string representation of @state, or NULL if not known.
2115 **/
2116static const char *
2117job_trace_state_enum_to_str (TraceState state)
2118{
2119 state_enum_to_str (TRACE_NONE, state);
2120 state_enum_to_str (TRACE_NEW, state);
2121 state_enum_to_str (TRACE_NEW_CHILD, state);
2122 state_enum_to_str (TRACE_NORMAL, state);
2123
2124 return NULL;
2125}
2126
2127/**
2128 * job_trace_state_str_to_enum:
2129 *
2130 * @state: string TraceState value.
2131 *
2132 * Convert @state back into enum value.
2133 *
2134 * Returns: TraceState representation of @state, or -1 if not known.
2135 **/
2136static TraceState
2137job_trace_state_str_to_enum (const char *state)
2138{
2139 state_str_to_enum (TRACE_NONE, state);
2140 state_str_to_enum (TRACE_NEW, state);
2141 state_str_to_enum (TRACE_NEW_CHILD, state);
2142 state_str_to_enum (TRACE_NORMAL, state);
2143
2144 return -1;
2145}
2146
2147/**
2148 * job_serialise_kill_timer:
2149 *
2150 * @timer: NihTimer to serialise.
2151 *
2152 * Serialise @timer into JSON.
2153 *
2154 * Returns: JSON-serialised NihTimer object, or NULL on error.
2155 **/
2156static json_object *
2157job_serialise_kill_timer (NihTimer *timer)
2158{
2159 json_object *json;
2160
2161 nih_assert (timer);
2162
2163 json = json_object_new_object ();
2164 if (! json)
2165 return NULL;
2166
2167 if (! state_set_json_int_var_from_obj (json, timer, timeout))
2168 goto error;
2169
2170 if (! state_set_json_int_var_from_obj (json, timer, due))
2171 goto error;
2172
2173 return json;
2174
2175error:
2176 json_object_put (json);
2177 return NULL;
2178}
2179
2180/**
2181 * job_deserialise_kill_timer:
2182 *
2183 * @json: JSON representation of NihTimer.
2184 *
2185 * Deserialise @json back into an NihTimer.
2186 *
2187 * Returns: NihTimer on NULL on error.
2188 **/
2189static NihTimer *
2190job_deserialise_kill_timer (json_object *json)
2191{
2192 NihTimer *timer;
2193
2194 nih_assert (json);
2195
2196 timer = nih_new (NULL, NihTimer);
2197 if (! timer)
2198 return NULL;
2199
2200 memset (timer, '\0', sizeof (NihTimer));
2201
2202 if (! state_get_json_int_var_to_obj (json, timer, due))
2203 goto error;
2204
2205 if (! state_get_json_int_var_to_obj (json, timer, timeout))
2206 goto error;
2207
2208 return timer;
2209
2210error:
2211 nih_free (timer);
2212 return NULL;
2213}
14952214
=== modified file 'init/job.h'
--- init/job.h 2012-03-07 12:13:28 +0000
+++ init/job.h 2012-11-14 14:49:20 +0000
@@ -103,6 +103,9 @@
103 * @start_env: environment to use next time the job is started,103 * @start_env: environment to use next time the job is started,
104 * @stop_env: environment to add for the next pre-stop script,104 * @stop_env: environment to add for the next pre-stop script,
105 * @stop_on: event operator expression that can stop this job.105 * @stop_on: event operator expression that can stop this job.
106 * @fds: array of file descriptors associated with events in parent
107 * JobClasses @start_on condition,
108 * @num_fds: number of elements in @fds,
106 * @pid: current process ids,109 * @pid: current process ids,
107 * @blocker: emitted event we're waiting to finish,110 * @blocker: emitted event we're waiting to finish,
108 * @blocking: list of events we're blocking from finishing,111 * @blocking: list of events we're blocking from finishing,
@@ -136,8 +139,8 @@
136 char **stop_env;139 char **stop_env;
137 EventOperator *stop_on;140 EventOperator *stop_on;
138141
139 int *fds;142 int *fds;
140 size_t num_fds;143 size_t num_fds;
141144
142 pid_t *pid;145 pid_t *pid;
143 Event *blocker;146 Event *blocker;
@@ -208,6 +211,15 @@
208 JobProcessesElement ***processes)211 JobProcessesElement ***processes)
209 __attribute__ ((warn_unused_result));212 __attribute__ ((warn_unused_result));
210213
214json_object *job_serialise (const Job *job);
215Job *job_deserialise (JobClass *parent, json_object *json);
216
217json_object *job_serialise_all (const NihHash *jobs)
218 __attribute__ ((malloc, warn_unused_result));
219
220int job_deserialise_all (JobClass *parent, json_object *json)
221 __attribute__ ((warn_unused_result));
222
211NIH_END_EXTERN223NIH_END_EXTERN
212224
213#endif /* INIT_JOB_H */225#endif /* INIT_JOB_H */
214226
=== modified file 'init/job_class.c'
--- init/job_class.c 2012-03-01 11:55:19 +0000
+++ init/job_class.c 2012-11-14 14:49:20 +0000
@@ -52,14 +52,18 @@
52#include "blocked.h"52#include "blocked.h"
53#include "conf.h"53#include "conf.h"
54#include "control.h"54#include "control.h"
55#include "parse_job.h"
5556
56#include "com.ubuntu.Upstart.h"57#include "com.ubuntu.Upstart.h"
57#include "com.ubuntu.Upstart.Job.h"58#include "com.ubuntu.Upstart.Job.h"
5859
60#include <json.h>
61
62extern json_object *json_classes;
5963
60/* Prototypes for static functions */64/* Prototypes for static functions */
61static void job_class_add (JobClass *class);65static void job_class_add (JobClass *class);
62static int job_class_remove (JobClass *class, const Session *session);66static int job_class_remove (JobClass *class, const Session *session);
6367
64/**68/**
65 * default_console:69 * default_console:
@@ -356,6 +360,30 @@
356}360}
357361
358/**362/**
363 * job_class_add_safe:
364 * @class: new class to select.
365 *
366 * Adds @class to the hash table iff no existing entry of the
367 * same name exists.
368 **/
369void
370job_class_add_safe (JobClass *class)
371{
372 JobClass *existing = NULL;
373
374 nih_assert (class);
375 nih_assert (class->name);
376
377 control_init ();
378
379 existing = (JobClass *)nih_hash_search (job_classes, class->name, NULL);
380
381 nih_assert (! existing);
382
383 job_class_add (class);
384}
385
386/**
359 * job_class_remove:387 * job_class_remove:
360 * @class: class to remove,388 * @class: class to remove,
361 * @session: Session of @class.389 * @session: Session of @class.
@@ -720,7 +748,7 @@
720 * If the instance goal is already start,748 * If the instance goal is already start,
721 * the com.ubuntu.Upstart.Error.AlreadyStarted D-Bus error will be returned749 * the com.ubuntu.Upstart.Error.AlreadyStarted D-Bus error will be returned
722 * immediately. If the instance fails to start, the750 * immediately. If the instance fails to start, the
723 * com.ubuntu.Upstart.Error.JobFailed D-BUs error will be returned when the751 * com.ubuntu.Upstart.Error.JobFailed D-Bus error will be returned when the
724 * problem occurs.752 * problem occurs.
725 *753 *
726 * When @wait is TRUE the method call will not return until the job has754 * When @wait is TRUE the method call will not return until the job has
@@ -1112,6 +1140,39 @@
1112 return 0;1140 return 0;
1113}1141}
11141142
1143/**
1144 * job_class_get:
1145 *
1146 * @name: name of job class,
1147 * @session: session of job class.
1148 *
1149 * Obtain JobClass with name @name and session @session.
1150 *
1151 * Returns: JobClass, or NULL if no matching job class found.
1152 **/
1153JobClass *
1154job_class_get (const char *name, Session *session)
1155{
1156 JobClass *class = NULL;
1157 NihList *prev = NULL;
1158
1159 nih_assert (name);
1160
1161 do {
1162 class = (JobClass *)nih_hash_search (job_classes, name, prev);
1163 if (! class)
1164 return NULL;
1165 if (class && class->session == session)
1166 return class;
1167
1168 nih_assert (class);
1169
1170 prev = (NihList *)class;
1171 } while (TRUE);
1172
1173 nih_assert_not_reached ();
1174}
1175
11151176
1116/**1177/**
1117 * job_class_get_name:1178 * job_class_get_name:
@@ -1500,3 +1561,651 @@
15001561
1501 return 0;1562 return 0;
1502}1563}
1564
1565
1566/**
1567 * job_class_serialise:
1568 * @class: job class to serialise.
1569 *
1570 * Convert @class into a JSON representation for serialisation.
1571 * Caller must free returned value using json_object_put().
1572 *
1573 * Returns: JSON-serialised JobClass object, or NULL on error.
1574 **/
1575json_object *
1576job_class_serialise (const JobClass *class)
1577{
1578 json_object *json;
1579 json_object *json_export;
1580 json_object *json_emits;
1581 json_object *json_processes;
1582 json_object *json_normalexit;
1583 json_object *json_limits;
1584 json_object *json_jobs;
1585 nih_local char *start_on = NULL;
1586 nih_local char *stop_on = NULL;
1587 int session_index;
1588
1589 nih_assert (class);
1590 nih_assert (job_classes);
1591
1592 json = json_object_new_object ();
1593 if (! json)
1594 return NULL;
1595
1596 session_index = session_get_index (class->session);
1597 if (session_index < 0)
1598 goto error;
1599
1600 if (! state_set_json_int_var (json, "session", session_index))
1601 goto error;
1602
1603 if (! state_set_json_string_var_from_obj (json, class, name))
1604 goto error;
1605
1606 if (! state_set_json_string_var_from_obj (json, class, path))
1607 goto error;
1608
1609 if (! state_set_json_string_var_from_obj (json, class, instance))
1610 goto error;
1611
1612 json_jobs = job_serialise_all (class->instances);
1613
1614 if (! json_jobs)
1615 goto error;
1616
1617 json_object_object_add (json, "jobs", json_jobs);
1618
1619 if (! state_set_json_string_var_from_obj (json, class, description))
1620 goto error;
1621
1622 if (! state_set_json_string_var_from_obj (json, class, author))
1623 goto error;
1624
1625 if (! state_set_json_string_var_from_obj (json, class, version))
1626 goto error;
1627
1628 if (! state_set_json_str_array_from_obj (json, class, env))
1629 goto error;
1630
1631 json_export = class->export
1632 ? state_serialise_str_array (class->export)
1633 : json_object_new_array ();
1634
1635 if (! json_export)
1636 goto error;
1637 json_object_object_add (json, "export", json_export);
1638
1639 if (class->start_on) {
1640 start_on = event_operator_collapse (class->start_on);
1641 if (! start_on)
1642 goto error;
1643
1644 if (! state_set_json_string_var (json, "start_on", start_on))
1645 goto error;
1646 }
1647
1648 if (class->stop_on) {
1649 stop_on = event_operator_collapse (class->stop_on);
1650 if (! stop_on)
1651 goto error;
1652
1653 if (! state_set_json_string_var (json, "stop_on", stop_on))
1654 goto error;
1655 }
1656
1657 json_emits = class->emits
1658 ? state_serialise_str_array (class->emits)
1659 : json_object_new_array ();
1660
1661 if (! json_emits)
1662 goto error;
1663 json_object_object_add (json, "emits", json_emits);
1664
1665 json_processes = process_serialise_all (
1666 (const Process const * const * const)class->process);
1667 if (! json_processes)
1668 goto error;
1669 json_object_object_add (json, "process", json_processes);
1670
1671 if (! state_set_json_enum_var (json,
1672 job_class_expect_type_enum_to_str,
1673 "expect", class->expect))
1674 goto error;
1675
1676 if (! state_set_json_int_var_from_obj (json, class, task))
1677 goto error;
1678
1679 if (! state_set_json_int_var_from_obj (json, class, kill_timeout))
1680 goto error;
1681
1682 if (! state_set_json_int_var_from_obj (json, class, kill_signal))
1683 goto error;
1684
1685 if (! state_set_json_int_var_from_obj (json, class, respawn))
1686 goto error;
1687
1688 if (! state_set_json_int_var_from_obj (json, class, respawn_limit))
1689 goto error;
1690
1691 if (! state_set_json_int_var_from_obj (json, class, respawn_interval))
1692 goto error;
1693
1694 json_normalexit = state_serialise_int_array (int, class->normalexit,
1695 class->normalexit_len);
1696 if (! json_normalexit)
1697 goto error;
1698
1699 json_object_object_add (json, "normalexit", json_normalexit);
1700
1701 if (! state_set_json_enum_var (json,
1702 job_class_console_type_enum_to_str,
1703 "console", class->console))
1704 goto error;
1705
1706 if (! state_set_json_int_var_from_obj (json, class, umask))
1707 goto error;
1708
1709 if (! state_set_json_int_var_from_obj (json, class, nice))
1710 goto error;
1711
1712 if (! state_set_json_int_var_from_obj (json, class, oom_score_adj))
1713 goto error;
1714
1715 json_limits = state_rlimit_serialise_all (class->limits);
1716 if (! json_limits)
1717 goto error;
1718 json_object_object_add (json, "limits", json_limits);
1719
1720 if (! state_set_json_string_var_from_obj (json, class, chroot))
1721 goto error;
1722
1723 if (! state_set_json_string_var_from_obj (json, class, chdir))
1724 goto error;
1725
1726 if (! state_set_json_string_var_from_obj (json, class, setuid))
1727 goto error;
1728
1729 if (! state_set_json_string_var_from_obj (json, class, setgid))
1730 goto error;
1731
1732 if (! state_set_json_int_var_from_obj (json, class, deleted))
1733 goto error;
1734
1735 if (! state_set_json_int_var_from_obj (json, class, debug))
1736 goto error;
1737
1738 if (! state_set_json_string_var_from_obj (json, class, usage))
1739 goto error;
1740
1741 return json;
1742
1743error:
1744 json_object_put (json);
1745 return NULL;
1746}
1747
1748/**
1749 * job_class_serialise_all:
1750 *
1751 * Convert existing JobClass objects to JSON representation.
1752 *
1753 * Returns: JSON object containing array of JobClass objects,
1754 * or NULL on error.
1755 **/
1756json_object *
1757job_class_serialise_all (void)
1758{
1759 json_object *json;
1760
1761 job_class_init ();
1762
1763 json = json_object_new_array ();
1764 if (! json)
1765 return NULL;
1766
1767 NIH_HASH_FOREACH (job_classes, iter) {
1768 json_object *json_class;
1769 JobClass *class = (JobClass *)iter;
1770
1771 json_class = job_class_serialise (class);
1772
1773 /* No object returned means the class doesn't need to be
1774 * serialised. Even if this is a real failure, it's always
1775 * better to serialise as much of the state as possible.
1776 */
1777 if (! json_class)
1778 continue;
1779
1780 json_object_array_add (json, json_class);
1781 }
1782
1783 return json;
1784}
1785
1786/**
1787 * job_class_deserialise:
1788 * @json: JSON-serialised JobClass object to deserialise.
1789 *
1790 * Create JobClass from provided JSON and add to the
1791 * job classes table.
1792 *
1793 * Returns: JobClass object, or NULL on error.
1794 **/
1795JobClass *
1796job_class_deserialise (json_object *json)
1797{
1798 json_object *json_normalexit;
1799 JobClass *class = NULL;
1800 int session_index = -1;
1801 int ret;
1802 nih_local char *name = NULL;
1803 nih_local char *path = NULL;
1804
1805 nih_assert (json);
1806 nih_assert (job_classes);
1807
1808 if (! state_check_json_type (json, object))
1809 goto error;
1810
1811 if (! state_get_json_int_var (json, "session", session_index))
1812 goto error;
1813
1814 if (session_index < 0)
1815 goto error;
1816
1817 if (! state_get_json_string_var_strict (json, "name", NULL, name))
1818 goto error;
1819
1820 class = NIH_MUST (job_class_new (NULL, name,
1821 session_from_index (session_index)));
1822 if (! class)
1823 goto error;
1824
1825 if (class->session != NULL) {
1826 nih_warn ("XXX: WARNING (%s:%d): deserialisation of "
1827 "user jobs and chroot sessions not currently supported",
1828 __func__, __LINE__);
1829 goto error;
1830 }
1831
1832 /* job_class_new() sets path */
1833 if (! state_get_json_string_var_strict (json, "path", NULL, path))
1834 goto error;
1835
1836 nih_assert (! strcmp (class->path, path));
1837
1838 /* Discard default instance as we're about to be handed a fresh
1839 * string from the JSON.
1840 */
1841 nih_free (class->instance);
1842
1843 if (! state_get_json_string_var_to_obj (json, class, instance))
1844 goto error;
1845
1846 if (! state_get_json_string_var_to_obj (json, class, description))
1847 goto error;
1848
1849 if (! state_get_json_string_var_to_obj (json, class, author))
1850 goto error;
1851
1852 if (! state_get_json_string_var_to_obj (json, class, version))
1853 goto error;
1854
1855 if (! state_get_json_env_array_to_obj (json, class, env))
1856 goto error;
1857
1858 if (! state_get_json_env_array_to_obj (json, class, export))
1859 goto error;
1860
1861 /* start and stop conditions are optional */
1862 if (json_object_object_get (json, "start_on")) {
1863 nih_local char *start_on = NULL;
1864
1865 if (! state_get_json_string_var_strict (json, "start_on", NULL, start_on))
1866 goto error;
1867
1868 if (*start_on) {
1869 class->start_on = parse_on_simple (class, "start", start_on);
1870 if (! class->start_on) {
1871 NihError *err;
1872
1873 err = nih_error_get ();
1874
1875 nih_error ("%s %s: %s",
1876 _("BUG"),
1877 _("'start on' parse error"),
1878 err->message);
1879
1880 nih_free (err);
1881
1882 goto error;
1883 }
1884 }
1885 }
1886
1887 if (json_object_object_get (json, "stop_on")) {
1888 nih_local char *stop_on = NULL;
1889
1890 if (! state_get_json_string_var_strict (json, "stop_on", NULL, stop_on))
1891 goto error;
1892
1893 if (*stop_on) {
1894 class->stop_on = parse_on_simple (class, "stop", stop_on);
1895 if (! class->stop_on) {
1896 NihError *err;
1897
1898 err = nih_error_get ();
1899
1900 nih_error ("%s %s: %s",
1901 _("BUG"),
1902 _("'stop on' parse error"),
1903 err->message);
1904
1905 nih_free (err);
1906
1907 goto error;
1908 }
1909 }
1910 }
1911
1912 if (! state_get_json_str_array_to_obj (json, class, emits))
1913 goto error;
1914
1915 if (! state_get_json_enum_var (json,
1916 job_class_expect_type_str_to_enum,
1917 "expect", class->expect))
1918 goto error;
1919
1920 if (! state_get_json_int_var_to_obj (json, class, task))
1921 goto error;
1922
1923 if (! state_get_json_int_var_to_obj (json, class, kill_timeout))
1924 goto error;
1925
1926 if (! state_get_json_int_var_to_obj (json, class, kill_signal))
1927 goto error;
1928
1929 if (! state_get_json_int_var_to_obj (json, class, respawn))
1930 goto error;
1931
1932 if (! state_get_json_int_var_to_obj (json, class, respawn_limit))
1933 goto error;
1934
1935 if (! state_get_json_int_var_to_obj (json, class, respawn_interval))
1936 goto error;
1937
1938 if (! state_get_json_enum_var (json,
1939 job_class_console_type_str_to_enum,
1940 "console", class->console))
1941 goto error;
1942
1943 if (! state_get_json_int_var_to_obj (json, class, umask))
1944 goto error;
1945
1946 if (! state_get_json_int_var_to_obj (json, class, nice))
1947 goto error;
1948
1949 if (! state_get_json_int_var_to_obj (json, class, oom_score_adj))
1950 goto error;
1951
1952 if (! state_get_json_string_var_to_obj (json, class, chroot))
1953 goto error;
1954
1955 if (! state_get_json_string_var_to_obj (json, class, chdir))
1956 goto error;
1957
1958 if (! state_get_json_string_var_to_obj (json, class, setuid))
1959 goto error;
1960
1961 if (! state_get_json_string_var_to_obj (json, class, setgid))
1962 goto error;
1963
1964 if (! state_get_json_int_var_to_obj (json, class, deleted))
1965 goto error;
1966
1967 if (! state_get_json_int_var_to_obj (json, class, debug))
1968 goto error;
1969
1970 if (! state_get_json_string_var_to_obj (json, class, usage))
1971 goto error;
1972
1973 json_normalexit = json_object_object_get (json, "normalexit");
1974 if (! json_normalexit)
1975 goto error;
1976
1977 ret = state_deserialise_int_array (class, json_normalexit,
1978 int, &class->normalexit, &class->normalexit_len);
1979 if (ret < 0)
1980 goto error;
1981
1982 if (state_rlimit_deserialise_all (json, class, &class->limits) < 0)
1983 goto error;
1984
1985 if (process_deserialise_all (json, class->process, class->process) < 0)
1986 goto error;
1987
1988 /* Force class to be known.
1989 *
1990 * We cannot use job_class_*consider() since the
1991 * JobClasses have no associated ConfFile.
1992 */
1993 job_class_add_safe (class);
1994
1995 /* Any jobs must be added after the class is registered
1996 * (since you cannot add a job to a partially-created
1997 * class).
1998 */
1999 if (job_deserialise_all (class, json) < 0)
2000 goto error;
2001
2002 return class;
2003
2004error:
2005 nih_free (class);
2006 return NULL;
2007}
2008
2009/**
2010 * job_class_deserialise_all:
2011 *
2012 * @json: root of JSON-serialised state.
2013 *
2014 * Convert JSON representation of JobClasses back into JobClass objects.
2015 *
2016 * Returns: 0 on success, -1 on error.
2017 **/
2018int
2019job_class_deserialise_all (json_object *json)
2020{
2021 JobClass *class = NULL;
2022
2023 nih_assert (json);
2024
2025 job_class_init ();
2026
2027 json_classes = json_object_object_get (json, "job_classes");
2028
2029 if (! json_classes)
2030 goto error;
2031
2032 if (! state_check_json_type (json_classes, array))
2033 goto error;
2034
2035 for (int i = 0; i < json_object_array_length (json_classes); i++) {
2036 json_object *json_class;
2037
2038 json_class = json_object_array_get_idx (json_classes, i);
2039 if (! json_class)
2040 goto error;
2041
2042 if (! state_check_json_type (json_class, object))
2043 goto error;
2044
2045 class = job_class_deserialise (json_class);
2046 if (! class)
2047 goto error;
2048
2049 /* FIXME:
2050 *
2051 * If user sessions exist (ie 'initctl --session list'
2052 * has been run), we get this failure:
2053 *
2054 * serialised path='/com/ubuntu/Upstart/jobs/1000/bang'
2055 * path set by job_class_new()='/com/ubuntu/Upstart/jobs/_/1000/bang'
2056 *
2057 */
2058
2059 }
2060
2061 return 0;
2062
2063error:
2064 if (class)
2065 nih_free (class);
2066
2067 return -1;
2068}
2069
2070
2071/**
2072 * job_class_expect_type_enum_to_str:
2073 *
2074 * @expect: ExpectType.
2075 *
2076 * Convert ExpectType to a string representation.
2077 *
2078 * Returns: string representation of @expect, or NULL if not known.
2079 **/
2080const char *
2081job_class_expect_type_enum_to_str (ExpectType expect)
2082{
2083 state_enum_to_str (EXPECT_NONE, expect);
2084 state_enum_to_str (EXPECT_STOP, expect);
2085 state_enum_to_str (EXPECT_DAEMON, expect);
2086 state_enum_to_str (EXPECT_FORK, expect);
2087
2088 return NULL;
2089}
2090
2091/**
2092 * job_class_expect_type_str_to_enum:
2093 *
2094 * @expect: string ExpectType value.
2095 *
2096 * Convert @expect back into an enum value.
2097 *
2098 * Returns: ExpectType representing @expect, or -1 if not known.
2099 **/
2100ExpectType
2101job_class_expect_type_str_to_enum (const char *expect)
2102{
2103 nih_assert (expect);
2104
2105 state_str_to_enum (EXPECT_NONE, expect);
2106 state_str_to_enum (EXPECT_STOP, expect);
2107 state_str_to_enum (EXPECT_DAEMON, expect);
2108 state_str_to_enum (EXPECT_FORK, expect);
2109
2110 return -1;
2111}
2112
2113/**
2114 * job_class_console_type_enum_to_str:
2115 *
2116 * @console: ConsoleType.
2117 *
2118 * Convert ConsoleType to a string representation.
2119 *
2120 * Returns: string representation of @console, or NULL if not known.
2121 **/
2122const char *
2123job_class_console_type_enum_to_str (ConsoleType console)
2124{
2125 state_enum_to_str (CONSOLE_NONE, console);
2126 state_enum_to_str (CONSOLE_OUTPUT, console);
2127 state_enum_to_str (CONSOLE_OWNER, console);
2128 state_enum_to_str (CONSOLE_LOG, console);
2129
2130 return NULL;
2131}
2132
2133/**
2134 * job_class_console_type_str_to_enum:
2135 *
2136 * @console: string ConsoleType value.
2137 *
2138 * Convert @console back into enum value.
2139 *
2140 * Returns: ExpectType representing @console, or -1 if not known.
2141 **/
2142ConsoleType
2143job_class_console_type_str_to_enum (const char *console)
2144{
2145 if (! console)
2146 goto error;
2147
2148 state_str_to_enum (CONSOLE_NONE, console);
2149 state_str_to_enum (CONSOLE_OUTPUT, console);
2150 state_str_to_enum (CONSOLE_OWNER, console);
2151 state_str_to_enum (CONSOLE_LOG, console);
2152
2153error:
2154 return -1;
2155}
2156
2157/**
2158 * job_class_prepare_reexec:
2159 *
2160 * Prepare for a re-exec by clearing the CLOEXEC bit on all log object
2161 * file descriptors associated with their parent jobs.
2162 **/
2163void
2164job_class_prepare_reexec (void)
2165{
2166 job_class_init ();
2167
2168 NIH_HASH_FOREACH (job_classes, iter) {
2169 JobClass *class = (JobClass *)iter;
2170
2171 NIH_HASH_FOREACH (class->instances, job_iter) {
2172 Job *job = (Job *)job_iter;
2173
2174 nih_assert (job->log);
2175
2176 for (int process = 0; process < PROCESS_LAST; process++) {
2177 int fd;
2178 Log *log;
2179
2180 log = job->log[process];
2181
2182 /* No associated job process or logger has detected
2183 * remote end of pty has closed.
2184 */
2185 if (! log || ! log->io)
2186 continue;
2187
2188 nih_assert (log->io->watch);
2189
2190 fd = log->io->watch->fd;
2191 if (fd < 0)
2192 continue;
2193
2194 if (state_toggle_cloexec (fd, FALSE) < 0)
2195 goto error;
2196
2197 fd = log->fd;
2198 if (fd < 0)
2199 continue;
2200
2201 if (state_toggle_cloexec (fd, FALSE) < 0)
2202 goto error;
2203 }
2204 }
2205 }
2206
2207 return;
2208
2209error:
2210 nih_warn (_("unable to clear CLOEXEC bit on log fd"));
2211}
15032212
=== modified file 'init/job_class.h'
--- init/job_class.h 2012-02-16 15:45:41 +0000
+++ init/job_class.h 2012-11-14 14:49:20 +0000
@@ -196,7 +196,7 @@
196 int task;196 int task;
197197
198 time_t kill_timeout;198 time_t kill_timeout;
199 int kill_signal;199 int kill_signal;
200200
201 int respawn;201 int respawn;
202 int respawn_limit;202 int respawn_limit;
@@ -238,6 +238,8 @@
238int job_class_consider (JobClass *class);238int job_class_consider (JobClass *class);
239int job_class_reconsider (JobClass *class);239int job_class_reconsider (JobClass *class);
240240
241void job_class_add_safe (JobClass *class);
242
241void job_class_register (JobClass *class,243void job_class_register (JobClass *class,
242 DBusConnection *conn, int signal);244 DBusConnection *conn, int signal);
243void job_class_unregister (JobClass *class,245void job_class_unregister (JobClass *class,
@@ -306,9 +308,42 @@
306 NihDBusMessage *message,308 NihDBusMessage *message,
307 char **usage);309 char **usage);
308310
311const char *
312job_class_console_type_enum_to_str (ConsoleType console)
313 __attribute__ ((warn_unused_result));
314
315ConsoleType
316job_class_console_type_str_to_enum (const char *name)
317 __attribute__ ((warn_unused_result));
318
319const char *
320job_class_expect_type_enum_to_str (ExpectType expect)
321 __attribute__ ((warn_unused_result));
322
323ExpectType
324job_class_expect_type_str_to_enum (const char *name)
325 __attribute__ ((warn_unused_result));
326
309ConsoleType job_class_console_type (const char *console)327ConsoleType job_class_console_type (const char *console)
310 __attribute__ ((warn_unused_result));328 __attribute__ ((warn_unused_result));
311329
330json_object *job_class_serialise (const JobClass *class)
331 __attribute__ ((warn_unused_result, malloc));
332
333JobClass *job_class_deserialise (json_object *json)
334 __attribute__ ((malloc, warn_unused_result));
335
336json_object * job_class_serialise_all (void)
337 __attribute__ ((warn_unused_result, malloc));
338
339int job_class_deserialise_all (json_object *json)
340 __attribute__ ((warn_unused_result));
341
342JobClass * job_class_get (const char *name, Session *session)
343 __attribute__ ((warn_unused_result));
344
345void job_class_prepare_reexec (void);
346
312NIH_END_EXTERN347NIH_END_EXTERN
313348
314#endif /* INIT_JOB_CLASS_H */349#endif /* INIT_JOB_CLASS_H */
315350
=== modified file 'init/job_process.c'
--- init/job_process.c 2012-08-02 08:57:20 +0000
+++ init/job_process.c 2012-11-14 14:49:20 +0000
@@ -637,7 +637,7 @@
637 */637 */
638 setsid ();638 setsid ();
639639
640 /* Set the process environment from the function paramters. */640 /* Set the process environment from the function parameters. */
641 environ = (char **)env;641 environ = (char **)env;
642642
643 /* Handle unprivileged user job by dropping privileges to643 /* Handle unprivileged user job by dropping privileges to
@@ -1226,7 +1226,7 @@
1226 nih_assert (job != NULL);1226 nih_assert (job != NULL);
1227 nih_assert (job->pid[process] > 0);1227 nih_assert (job->pid[process] > 0);
1228 nih_assert (job->kill_timer == NULL);1228 nih_assert (job->kill_timer == NULL);
1229 nih_assert (job->kill_process = -1);1229 nih_assert (job->kill_process == PROCESS_INVALID);
12301230
1231 nih_info (_("Sending %s signal to %s %s process (%d)"),1231 nih_info (_("Sending %s signal to %s %s process (%d)"),
1232 nih_signal_to_name (job->class->kill_signal),1232 nih_signal_to_name (job->class->kill_signal),
@@ -1246,13 +1246,50 @@
1246 return;1246 return;
1247 }1247 }
12481248
1249 job_process_set_kill_timer (job, process, job->class->kill_timeout);
1250}
1251
1252/**
1253 * job_process_set_kill_timer:
1254 * @job: job to set kill timer for,
1255 * @process: process to be killed,
1256 * @timeout: timeout to apply for timer.
1257 *
1258 * Set kill timer for specified @job @process with timeout @timeout.
1259 **/
1260void
1261job_process_set_kill_timer (Job *job,
1262 ProcessType process,
1263 time_t timeout)
1264{
1265 nih_assert (job);
1266 nih_assert (timeout);
1267
1249 job->kill_process = process;1268 job->kill_process = process;
1250 job->kill_timer = NIH_MUST (nih_timer_add_timeout (1269 job->kill_timer = NIH_MUST (nih_timer_add_timeout (
1251 job, job->class->kill_timeout,1270 job, timeout,
1252 (NihTimerCb)job_process_kill_timer, job));1271 (NihTimerCb)job_process_kill_timer, job));
1253}1272}
12541273
1255/**1274/**
1275 * job_process_adj_kill_timer:
1276 *
1277 * @job: job whose kill timer is to be modified,
1278 * @due: new due time to set for job kill timer.
1279 *
1280 * Adjust due time for @job's kill timer to @due.
1281 **/
1282void
1283job_process_adj_kill_timer (Job *job, time_t due)
1284{
1285 nih_assert (job);
1286 nih_assert (job->kill_timer);
1287 nih_assert (due);
1288
1289 job->kill_timer->due = due;
1290}
1291
1292/**
1256 * job_process_kill_timer:1293 * job_process_kill_timer:
1257 * @job: job to kill process of,1294 * @job: job to kill process of,
1258 * @timer: timer that caused us to be called.1295 * @timer: timer that caused us to be called.
@@ -1270,13 +1307,13 @@
1270 nih_assert (job != NULL);1307 nih_assert (job != NULL);
1271 nih_assert (timer != NULL);1308 nih_assert (timer != NULL);
1272 nih_assert (job->kill_timer == timer);1309 nih_assert (job->kill_timer == timer);
1273 nih_assert (job->kill_process != (ProcessType)-1);1310 nih_assert (job->kill_process != PROCESS_INVALID);
12741311
1275 process = job->kill_process;1312 process = job->kill_process;
1276 nih_assert (job->pid[process] > 0);1313 nih_assert (job->pid[process] > 0);
12771314
1278 job->kill_timer = NULL;1315 job->kill_timer = NULL;
1279 job->kill_process = -1;1316 job->kill_process = PROCESS_INVALID;
12801317
1281 nih_info (_("Sending %s signal to %s %s process (%d)"),1318 nih_info (_("Sending %s signal to %s %s process (%d)"),
1282 "KILL",1319 "KILL",
@@ -1547,7 +1584,7 @@
1547 job_name (job));1584 job_name (job));
15481585
1549 failed = FALSE;1586 failed = FALSE;
1550 job_failed (job, -1, 0);1587 job_failed (job, PROCESS_INVALID, 0);
1551 } else {1588 } else {
1552 nih_warn (_("%s %s process ended, respawning"),1589 nih_warn (_("%s %s process ended, respawning"),
1553 job_name (job),1590 job_name (job),
@@ -1631,7 +1668,7 @@
1631 if (job->kill_timer) {1668 if (job->kill_timer) {
1632 nih_unref (job->kill_timer, job);1669 nih_unref (job->kill_timer, job);
1633 job->kill_timer = NULL;1670 job->kill_timer = NULL;
1634 job->kill_process = -1;1671 job->kill_process = PROCESS_INVALID;
1635 }1672 }
16361673
1637 if (job->class->console == CONSOLE_LOG && job->log[process]) {1674 if (job->class->console == CONSOLE_LOG && job->log[process]) {
16381675
=== modified file 'init/job_process.h'
--- init/job_process.h 2012-08-02 08:57:20 +0000
+++ init/job_process.h 2012-11-14 14:49:20 +0000
@@ -143,6 +143,12 @@
143char *job_process_log_path (Job *job, int user_job)143char *job_process_log_path (Job *job, int user_job)
144 __attribute__ ((malloc, warn_unused_result));144 __attribute__ ((malloc, warn_unused_result));
145145
146void job_process_set_kill_timer (Job *job,
147 ProcessType process,
148 time_t timeout);
149
150void job_process_adj_kill_timer (Job *job, time_t due);
151
146NIH_END_EXTERN152NIH_END_EXTERN
147153
148#endif /* INIT_JOB_PROCESS_H */154#endif /* INIT_JOB_PROCESS_H */
149155
=== modified file 'init/log.c'
--- init/log.c 2012-03-16 21:06:11 +0000
+++ init/log.c 2012-11-14 14:49:20 +0000
@@ -48,8 +48,13 @@
48/**48/**
49 * log_unflushed_files:49 * log_unflushed_files:
50 *50 *
51 * List of known sources of configuration; each item is an51 * List of NihListEntry objects containing Log objects which are no
52 * NihListEntry.52 * longer associated with Job processes.
53 *
54 * All the entries in the list contain unflushed Log data.
55 *
56 * Used to capture job process output early in the boot process for
57 * jobs that end before the log partition is mounted and writeable.
53 **/58 **/
54NihList *log_unflushed_files = NULL;59NihList *log_unflushed_files = NULL;
5560
@@ -393,7 +398,7 @@
393 *398 *
394 * Opens log file associated with @log if not already open.399 * Opens log file associated with @log if not already open.
395 *400 *
396 * Returns 0 on success, -1 on failure.401 * Returns: 0 on success, -1 on failure.
397 **/402 **/
398static int403static int
399log_file_open (Log *log)404log_file_open (Log *log)
@@ -505,7 +510,7 @@
505 * a corrupted log file should space later become510 * a corrupted log file should space later become
506 * available.511 * available.
507 *512 *
508 * Returns 0 on success, -1 on failure.513 * Returns: 0 on success, -1 on failure.
509 **/514 **/
510static int515static int
511log_file_write (Log *log, const char *buf, size_t len)516log_file_write (Log *log, const char *buf, size_t len)
@@ -638,6 +643,8 @@
638 if (nih_io_buffer_resize (io->recv_buf, LOG_READ_SIZE) < 0)643 if (nih_io_buffer_resize (io->recv_buf, LOG_READ_SIZE) < 0)
639 break;644 break;
640645
646 errno = 0;
647
641 /* Append to buffer */648 /* Append to buffer */
642 len = read (io->watch->fd,649 len = read (io->watch->fd,
643 io->recv_buf->buf + io->recv_buf->len,650 io->recv_buf->buf + io->recv_buf->len,
@@ -680,22 +687,23 @@
680 * causes the loop to be exited.687 * causes the loop to be exited.
681 */688 */
682 if (len <= 0) {689 if (len <= 0) {
690 /* Job process has ended and we've drained all the data the job
691 * produced, so remote end must have closed.
692 *
693 * This cannot be handled entirely by log_io_error_handler()
694 * since the job may produce some output prior to disks being
695 * writeable, then end without producing further output.
696 * In this scenario the error handler is never called.
697 *
698 */
699 if (saved && saved != EAGAIN && saved != EWOULDBLOCK)
700 log->remote_closed = 1;
701
683 close (log->fd);702 close (log->fd);
684 log->fd = -1;703 log->fd = -1;
685 break;704 break;
686 }705 }
687 }706 }
688
689 /* Job process has ended and we've drained all the data the job
690 * produced, so remote end must have closed.
691 *
692 * This cannot be handled entirely by log_io_error_handler()
693 * since the job may produce some output prior to disks being
694 * writeable, then end without producing further output.
695 * In this scenario the error handler is never called.
696 *
697 */
698 log->remote_closed = 1;
699}707}
700708
701/**709/**
@@ -810,3 +818,193 @@
810818
811 return 0;819 return 0;
812}820}
821
822/**
823 * log_serialise:
824 * @log: log to serialise.
825 *
826 * Convert @log into a JSON representation for serialisation.
827 * Caller must free returned value using json_object_put().
828 *
829 * Returns: JSON-serialised Log object, or NULL on error.
830 **/
831json_object *
832log_serialise (Log *log)
833{
834 json_object *json;
835 nih_local char *unflushed_hex = NULL;
836
837 json = json_object_new_object ();
838 if (! json)
839 return NULL;
840
841 if (! log || (! log->io && log->unflushed && ! log->unflushed->len)) {
842 /* Create a "placeholder" log object for non-existent
843 * log objects and for those that are no longer usable.
844 */
845 if (! state_set_json_string_var (json, "path", NULL))
846 goto error;
847 return json;
848 }
849
850 /* Attempt to flush any cached data */
851 if (log->unflushed->len) {
852 /* Don't check return values since if this fails and
853 * unflushed data remains, we encode it below.
854 */
855 if (log->fd < 0)
856 (void)log_file_open (log);
857 if (log->fd != -1)
858 (void)log_file_write (log, NULL, 0);
859 }
860
861 nih_assert (log->io);
862
863 if (! state_set_json_int_var_from_obj (json, log, fd))
864 goto error;
865
866 nih_assert (log->io->watch);
867
868 if (! state_set_json_int_var (json, "io_watch_fd", log->io->watch->fd))
869 goto error;
870
871 if (! state_set_json_string_var_from_obj (json, log, path))
872 goto error;
873
874 /* log->io itself is not encoded */
875
876 if (! state_set_json_int_var_from_obj (json, log, uid))
877 goto error;
878
879 /* Encode unflushed data as hex to ensure any embedded
880 * nulls are handled.
881 */
882 if (log->unflushed->len) {
883 unflushed_hex = state_data_to_hex (NULL,
884 log->unflushed->buf,
885 log->unflushed->len);
886
887 if (! unflushed_hex)
888 goto error;
889
890 if (! state_set_json_string_var (json, "unflushed", unflushed_hex))
891 goto error;
892 }
893
894 if (! state_set_json_int_var_from_obj (json, log, detached))
895 goto error;
896
897 if (! state_set_json_int_var_from_obj (json, log, remote_closed))
898 goto error;
899
900 if (! state_set_json_int_var_from_obj (json, log, open_errno))
901 goto error;
902
903 return json;
904
905error:
906 json_object_put (json);
907 return NULL;
908}
909
910/**
911 * log_deserialise:
912 * @json: JSON-serialised Log object to deserialise.
913 *
914 * Convert @json into a Log object.
915 *
916 * Returns: Log object, or NULL on error.
917 **/
918Log *
919log_deserialise (const void *parent,
920 json_object *json)
921{
922 Log *log;
923 nih_local char *unflushed_hex = NULL;
924 nih_local char *unflushed = NULL;
925 int ret;
926 size_t len;
927 json_object *json_unflushed;
928 nih_local char *path = NULL;
929 int io_watch_fd = -1;
930 uid_t uid = (uid_t)-1;
931
932 nih_assert (json);
933
934 log_unflushed_init ();
935
936 if (! state_check_json_type (json, object))
937 return NULL;
938
939 if (! state_get_json_string_var (json, "path", NULL, path))
940 return NULL;
941
942 if (! path) {
943 /* placeholder log object */
944 return NULL;
945 }
946
947 if (! state_get_json_int_var (json, "io_watch_fd", io_watch_fd))
948 return NULL;
949
950 nih_assert (io_watch_fd != -1);
951
952 /* re-apply CLOEXEC flag to stop job fd being leaked to children */
953 if (state_toggle_cloexec (io_watch_fd, TRUE) < 0)
954 return NULL;
955
956 if (! state_get_json_int_var (json, "uid", uid))
957 return NULL;
958
959 log = log_new (parent, path, io_watch_fd, uid);
960 if (! log)
961 return NULL;
962
963 if (! state_get_json_int_var_to_obj (json, log, fd))
964 goto error;
965
966 /* Re-apply CLOEXEC flag to stop log file fd being leaked to children.
967 *
968 * Note we discard return value since if this fails,
969 * we would never close the fd.
970 */
971 if (log->fd != -1)
972 (void)state_toggle_cloexec (log->fd, TRUE);
973
974 log->unflushed = nih_io_buffer_new (log);
975 if (! log->unflushed)
976 goto error;
977
978 json_unflushed = json_object_object_get (json, "unflushed");
979 if (json_unflushed) {
980 if (! state_get_json_string_var_strict (json, "unflushed", NULL, unflushed_hex))
981 goto error;
982
983 ret = state_hex_to_data (NULL,
984 unflushed_hex,
985 strlen (unflushed_hex),
986 &unflushed,
987 &len);
988
989 if (ret < 0)
990 goto error;
991
992 if (nih_io_buffer_push (log->unflushed, unflushed, len) < 0)
993 goto error;
994 }
995
996 if (! state_get_json_int_var_to_obj (json, log, detached))
997 goto error;
998
999 if (! state_get_json_int_var_to_obj (json, log, remote_closed))
1000 goto error;
1001
1002 if (! state_get_json_int_var_to_obj (json, log, open_errno))
1003 goto error;
1004
1005 return log;
1006
1007error:
1008 nih_free (log);
1009 return NULL;
1010}
8131011
=== modified file 'init/log.h'
--- init/log.h 2012-02-14 16:47:34 +0000
+++ init/log.h 2012-11-14 14:49:20 +0000
@@ -29,6 +29,8 @@
29#include <nih/logging.h>29#include <nih/logging.h>
30#include <nih/error.h>30#include <nih/error.h>
3131
32#include "state.h"
33
32/** LOG_DEFAULT_UMASK:34/** LOG_DEFAULT_UMASK:
33 *35 *
34 * The default file creation mask for log files.36 * The default file creation mask for log files.
@@ -55,7 +57,7 @@
55 * @io: NihIo associated with jobs stdout and stderr,57 * @io: NihIo associated with jobs stdout and stderr,
56 * @uid: User ID of caller,58 * @uid: User ID of caller,
57 * @unflushed: Unflushed data,59 * @unflushed: Unflushed data,
58 * @detached: TRUE if log is no longer associated with a parent,(job),60 * @detached: TRUE if log is no longer associated with a parent (job),
59 * @remote_closed: TRUE if remote end of pty has been closed,61 * @remote_closed: TRUE if remote end of pty has been closed,
60 * @open_errno: value of errno immediately after last attempt to open @path.62 * @open_errno: value of errno immediately after last attempt to open @path.
61 **/63 **/
@@ -79,11 +81,17 @@
79 __attribute__ ((warn_unused_result, malloc));81 __attribute__ ((warn_unused_result, malloc));
80void log_io_reader (Log *log, NihIo *io, const char *buf, size_t len);82void log_io_reader (Log *log, NihIo *io, const char *buf, size_t len);
81void log_io_error_handler (Log *log, NihIo *io);83void log_io_error_handler (Log *log, NihIo *io);
82int log_destroy (Log *log);84int log_destroy (Log *log)
83int log_handle_unflushed (void *parent, Log *log);85 __attribute__ ((warn_unused_result));
84int log_clear_unflushed (void);86int log_handle_unflushed (void *parent, Log *log)
87 __attribute__ ((warn_unused_result));
88int log_clear_unflushed (void)
89 __attribute__ ((warn_unused_result));
85void log_unflushed_init (void);90void log_unflushed_init (void);
8691json_object * log_serialise (Log *log)
92 __attribute__ ((warn_unused_result, malloc));
93Log * log_deserialise (const void *parent, json_object *json)
94 __attribute__ ((warn_unused_result, malloc));
8795
88NIH_END_EXTERN96NIH_END_EXTERN
8997
9098
=== modified file 'init/main.c'
--- init/main.c 2012-08-07 16:39:00 +0000
+++ init/main.c 2012-11-14 14:49:20 +0000
@@ -60,12 +60,16 @@
60#include "event.h"60#include "event.h"
61#include "conf.h"61#include "conf.h"
62#include "control.h"62#include "control.h"
63#include "state.h"
6364
6465
65/* Prototypes for static functions */66/* Prototypes for static functions */
66#ifndef DEBUG67#ifndef DEBUG
67static int logger_kmsg (NihLogLevel priority, const char *message);68static int logger_kmsg (NihLogLevel priority, const char *message);
68static void crash_handler (int signum);69static void crash_handler (int signum);
70#endif /* DEBUG */
71static void term_handler (void *data, NihSignal *signal);
72#ifndef DEBUG
69static void cad_handler (void *data, NihSignal *signal);73static void cad_handler (void *data, NihSignal *signal);
70static void kbd_handler (void *data, NihSignal *signal);74static void kbd_handler (void *data, NihSignal *signal);
71static void pwr_handler (void *data, NihSignal *signal);75static void pwr_handler (void *data, NihSignal *signal);
@@ -79,20 +83,12 @@
7983
8084
81/**85/**
82 * argv0:86 * state_fd:
83 *87 *
84 * Path to program executed, used for re-executing the init binary from the88 * File descriptor to read serialised state from when performing
85 * same location we were executed from.89 * stateful re-exec. If value is not -1, attempt stateful re-exec.
86 **/90 **/
87static const char *argv0 = NULL;91static int state_fd = -1;
88
89/**
90 * restart:
91 *
92 * This is set to TRUE if we're being re-exec'd by an existing init
93 * process.
94 **/
95static int restart = FALSE;
9692
97/**93/**
98 * conf_dir:94 * conf_dir:
@@ -147,7 +143,13 @@
147 { 0, "no-startup-event", N_("do not emit any startup event (for testing)"),143 { 0, "no-startup-event", N_("do not emit any startup event (for testing)"),
148 NULL, NULL, &disable_startup_event, NULL },144 NULL, NULL, &disable_startup_event, NULL },
149145
150 { 0, "restart", NULL, NULL, NULL, &restart, NULL },146 /* Must be specified for both stateful and stateless re-exec */
147 { 0, "restart", N_("flag a re-exec has occurred"),
148 NULL, NULL, &restart, NULL },
149
150 /* Required for stateful re-exec */
151 { 0, "state-fd", N_("specify file descriptor to read serialisation data from"),
152 NULL, "FD", &state_fd, nih_option_int },
151153
152 { 0, "session", N_("use D-Bus session bus rather than system bus (for testing)"),154 { 0, "session", N_("use D-Bus session bus rather than system bus (for testing)"),
153 NULL, NULL, &use_session_bus, NULL },155 NULL, NULL, &use_session_bus, NULL },
@@ -166,11 +168,12 @@
166main (int argc,168main (int argc,
167 char *argv[])169 char *argv[])
168{170{
169 char **args;171 char **args = NULL;
170 int ret;172 int ret;
171173
172 argv0 = argv[0];174 args_copy = NIH_MUST (nih_str_array_copy (NULL, NULL, argv));
173 nih_main_init (argv0);175
176 nih_main_init (args_copy[0]);
174177
175 nih_option_set_synopsis (_("Process management daemon."));178 nih_option_set_synopsis (_("Process management daemon."));
176 nih_option_set_help (179 nih_option_set_help (
@@ -295,8 +298,9 @@
295 */298 */
296 if (system_setup_console (CONSOLE_OUTPUT, (! restart)) < 0) {299 if (system_setup_console (CONSOLE_OUTPUT, (! restart)) < 0) {
297 NihError *err;300 NihError *err;
298 301
299 err = nih_error_get ();302 err = nih_error_get ();
303
300 nih_warn ("%s: %s", _("Unable to initialize console, will try /dev/null"),304 nih_warn ("%s: %s", _("Unable to initialize console, will try /dev/null"),
301 err->message);305 err->message);
302 nih_free (err);306 nih_free (err);
@@ -411,7 +415,16 @@
411 /* SIGUSR1 instructs us to reconnect to D-Bus */415 /* SIGUSR1 instructs us to reconnect to D-Bus */
412 nih_signal_set_handler (SIGUSR1, nih_signal_handler);416 nih_signal_set_handler (SIGUSR1, nih_signal_handler);
413 NIH_MUST (nih_signal_add_handler (NULL, SIGUSR1, usr1_handler, NULL));417 NIH_MUST (nih_signal_add_handler (NULL, SIGUSR1, usr1_handler, NULL));
418
414 }419 }
420
421 /* SIGTERM instructs us to re-exec ourselves; this should be the
422 * last in the list to ensure that all other signals are handled
423 * before a SIGTERM.
424 */
425 nih_signal_set_handler (SIGTERM, nih_signal_handler);
426 NIH_MUST (nih_signal_add_handler (NULL, SIGTERM, term_handler, NULL));
427
415#endif /* DEBUG */428#endif /* DEBUG */
416429
417430
@@ -456,6 +469,48 @@
456 }469 }
457470
458471
472 if (restart) {
473 if (state_fd == -1) {
474 nih_warn ("%s",
475 _("Stateful re-exec supported but stateless re-exec requested"));
476 } else if (state_read (state_fd) < 0) {
477 nih_local char *arg = NULL;
478
479 /* Stateful re-exec has failed so try once more by
480 * degrading to stateless re-exec, which even in
481 * the case of low-memory scenarios will work.
482 */
483
484 /* Inform the child we've given up on stateful
485 * re-exec.
486 */
487 close (state_fd);
488
489 nih_error ("%s - %s",
490 _("Failed to read serialisation data"),
491 _("reverting to stateless re-exec"));
492
493 /* Remove any existing (but now stale) state fd
494 * args which will effectively disable stateful
495 * re-exec.
496 */
497 clean_args (&args_copy);
498
499 /* Attempt stateless re-exec */
500 perform_reexec ();
501
502 nih_error ("%s",
503 _("Both stateful and stateless re-execs failed"));
504
505 /* Out of options */
506 nih_assert_not_reached ();
507 } else {
508 close (state_fd);
509
510 nih_info ("Stateful re-exec completed");
511 }
512 }
513
459 /* Read configuration */514 /* Read configuration */
460 NIH_MUST (conf_source_new (NULL, CONFFILE, CONF_FILE));515 NIH_MUST (conf_source_new (NULL, CONFFILE, CONF_FILE));
461 NIH_MUST (conf_source_new (NULL, conf_dir, CONF_JOB_DIR));516 NIH_MUST (conf_source_new (NULL, conf_dir, CONF_JOB_DIR));
@@ -479,7 +534,8 @@
479 }534 }
480535
481 /* Open connection to the appropriate D-Bus bus; we normally expect this to536 /* Open connection to the appropriate D-Bus bus; we normally expect this to
482 * fail and will try again later - don't let ENOMEM stop us though.537 * fail (since dbus-daemon probably isn't running yet) and will try again
538 * later - don't let ENOMEM stop us though.
483 */539 */
484 while (control_bus_open () < 0) {540 while (control_bus_open () < 0) {
485 NihError *err;541 NihError *err;
@@ -529,9 +585,17 @@
529 }585 }
530586
531 } else {587 } else {
532 sigset_t mask;588 sigset_t mask;
533589
534 /* We're ok to receive signals again */590 /* We have been re-exec'd. Don't emit an initial event
591 * as only Upstart is restarting - we don't want to restart
592 * the system (another reason being that we don't yet support
593 * upstart-in-initramfs to upstart-in-root-filesystem
594 * state-passing transitions).
595 */
596
597 /* We're ok to receive signals again so restore signals
598 * disabled by the term_handler */
535 sigemptyset (&mask);599 sigemptyset (&mask);
536 sigprocmask (SIG_SETMASK, &mask, NULL);600 sigprocmask (SIG_SETMASK, &mask, NULL);
537 }601 }
@@ -631,7 +695,7 @@
631{695{
632 pid_t pid;696 pid_t pid;
633697
634 nih_assert (argv0 != NULL);698 nih_assert (args_copy[0] != NULL);
635699
636 pid = fork ();700 pid = fork ();
637 if (pid == 0) {701 if (pid == 0) {
@@ -685,7 +749,30 @@
685 /* Goodbye, cruel world. */749 /* Goodbye, cruel world. */
686 exit (signum);750 exit (signum);
687}751}
688752#endif
753
754/**
755 * term_handler:
756 * @data: unused,
757 * @signal: signal caught.
758 *
759 * This is called when we receive the TERM signal, which instructs us
760 * to reexec ourselves.
761 **/
762static void
763term_handler (void *data,
764 NihSignal *signal)
765{
766 nih_assert (args_copy[0] != NULL);
767 nih_assert (signal != NULL);
768
769 nih_warn (_("Re-executing %s"), args_copy[0]);
770
771 stateful_reexec ();
772}
773
774
775#ifndef DEBUG
689/**776/**
690 * cad_handler:777 * cad_handler:
691 * @data: unused,778 * @data: unused,
@@ -835,7 +922,7 @@
835 * NihOption setter function to handle selection of default console922 * NihOption setter function to handle selection of default console
836 * type.923 * type.
837 *924 *
838 * Returns 0 on success, -1 on invalid console type.925 * Returns: 0 on success, -1 on invalid console type.
839 **/926 **/
840static int927static int
841console_type_setter (NihOption *option, const char *arg)928console_type_setter (NihOption *option, const char *arg)
@@ -851,3 +938,4 @@
851938
852 return 0;939 return 0;
853}940}
941
854942
=== modified file 'init/parse_job.c'
--- init/parse_job.c 2012-10-22 13:29:45 +0000
+++ init/parse_job.c 2012-11-14 14:49:20 +0000
@@ -37,7 +37,6 @@
37#include <nih/string.h>37#include <nih/string.h>
38#include <nih/list.h>38#include <nih/list.h>
39#include <nih/signal.h>39#include <nih/signal.h>
40#include <nih/config.h>
41#include <nih/logging.h>40#include <nih/logging.h>
42#include <nih/error.h>41#include <nih/error.h>
4342
@@ -625,6 +624,48 @@
625}624}
626625
627/**626/**
627 * parse_on_simple:
628 * @class: job class being parsed,
629 * @stanza_name: name of stanza type to parse ("start" or "stop"),
630 * @string: string to parse.
631 *
632 * Parse either a "start" or "stop" condition from @string (which must
633 * start with the first byte beyond either "start on" or "stop on".
634 *
635 * Returns: EventOperator at root of expression tree on success, NULL
636 * on raised error.
637 **/
638EventOperator *
639parse_on_simple (JobClass *class, const char *stanza_name, const char *string)
640{
641 EventOperator *root = NULL;
642 NihConfigStanza *stanza = NULL;
643 size_t pos = 0;
644 size_t lineno = 0;
645 size_t len;
646
647 nih_assert (class);
648 nih_assert (stanza_name);
649 nih_assert (string);
650
651 /* Find the appropriate config stanza */
652 for (NihConfigStanza *s = stanzas; s->name; s++) {
653 if (! strcmp (stanza_name, s->name)) {
654 stanza = s;
655 break;
656 }
657 }
658
659 nih_assert (stanza);
660
661 len = strlen (string);
662
663 root = parse_on (class, stanza, string, len, &pos, &lineno);
664
665 return root;
666}
667
668/**
628 * parse_on_operator:669 * parse_on_operator:
629 * @class: job class being parsed,670 * @class: job class being parsed,
630 * @stanza: stanza found,671 * @stanza: stanza found,
631672
=== modified file 'init/parse_job.h'
--- init/parse_job.h 2011-06-06 12:52:08 +0000
+++ init/parse_job.h 2012-11-14 14:49:20 +0000
@@ -21,6 +21,7 @@
21#define INIT_PARSE_JOB_H21#define INIT_PARSE_JOB_H
2222
23#include <nih/macros.h>23#include <nih/macros.h>
24#include <nih/config.h>
2425
25#include "session.h"26#include "session.h"
26#include "job_class.h"27#include "job_class.h"
@@ -34,6 +35,10 @@
34 size_t *pos, size_t *lineno)35 size_t *pos, size_t *lineno)
35 __attribute__ ((warn_unused_result, malloc));36 __attribute__ ((warn_unused_result, malloc));
3637
38EventOperator *
39parse_on_simple (JobClass *class, const char *stanza_name,
40 const char *string)
41 __attribute__ ((warn_unused_result, malloc));
37NIH_END_EXTERN42NIH_END_EXTERN
3843
39#endif /* INIT_PARSE_JOB_H */44#endif /* INIT_PARSE_JOB_H */
4045
=== modified file 'init/process.c'
--- init/process.c 2009-06-23 09:29:35 +0000
+++ init/process.c 2012-11-14 14:49:20 +0000
@@ -29,8 +29,10 @@
29#include <nih/macros.h>29#include <nih/macros.h>
30#include <nih/alloc.h>30#include <nih/alloc.h>
31#include <nih/logging.h>31#include <nih/logging.h>
32#include <nih/string.h>
3233
33#include "process.h"34#include "process.h"
35#include "state.h"
3436
3537
36/**38/**
@@ -116,3 +118,224 @@
116 return -1;118 return -1;
117 }119 }
118}120}
121
122/**
123 * process_serialise:
124 * @process: process to serialise.
125 *
126 * Convert @process into a JSON representation for serialisation.
127 * Caller must free returned value using json_object_put().
128 *
129 * Returns: JSON-serialised Process object, or NULL on error.
130 **/
131json_object *
132process_serialise (const Process *process)
133{
134 json_object *json;
135
136 nih_assert (process);
137
138 json = json_object_new_object ();
139 if (! json)
140 return NULL;
141
142 if (! state_set_json_int_var_from_obj (json, process, script))
143 goto error;
144
145 if (! state_set_json_string_var_from_obj (json, process, command))
146 goto error;
147
148 return json;
149
150error:
151 json_object_put (json);
152 return NULL;
153
154}
155
156/**
157 * process_serialise_all:
158 *
159 * @processes: array of Processes.
160 *
161 * Convert array of Process objects to JSON representation.
162 *
163 * Returns: JSON object containing array of Processes, or NULL on error.
164 */
165json_object *
166process_serialise_all (const Process * const * const processes)
167{
168 json_object *json;
169 json_object *json_process;
170 Process dummy = { 0, NULL };
171
172 nih_assert (processes);
173
174 json = json_object_new_array ();
175 if (! json)
176 return NULL;
177
178 for (int i = 0; i < PROCESS_LAST; i++) {
179 /* We must encode a blank entry for missing array elements
180 * to ensure correct deserialisation.
181 */
182 json_process = process_serialise (processes[i]
183 ? processes[i]
184 : &dummy);
185
186 if (! json_process)
187 goto error;
188
189 if (json_object_array_add (json, json_process) < 0)
190 goto error;
191 }
192
193 return json;
194
195error:
196 json_object_put (json);
197 return NULL;
198}
199
200/**
201 * process_deserialise:
202 * @json: JSON-serialised Process object to deserialise.
203 *
204 * Convert @json into a Process object.
205 *
206 * Returns: Process object, or NULL on error.
207 **/
208Process *
209process_deserialise (json_object *json, const void *parent)
210{
211 Process *process = NULL;
212
213 nih_assert (json);
214
215 if (! state_check_json_type (json, object))
216 goto error;
217
218 process = NIH_MUST (process_new (parent));
219
220 if (! state_get_json_int_var_to_obj (json, process, script))
221 goto error;
222
223 if (! state_get_json_string_var_to_obj (json, process, command))
224 goto error;
225
226 /* All Process slots have to be serialised in the JSON to
227 * guarantee ordering on deserialisation.
228 *
229 * However, here we've found a Process that was merely
230 * an ordering placeholder - no command has been defined,
231 * so ignore it.
232 */
233 if (! process->command)
234 goto error;
235
236 return process;
237
238error:
239 nih_free (process);
240 return NULL;
241}
242
243/**
244 * process_deserialise_all:
245 *
246 * @json: root of JSON-serialised state,
247 * @parent: parent of @processes,
248 * @processes: newly-allocated array of Process objects.
249 *
250 * Convert JSON representation of processes back into
251 * an array of Process objects.
252 *
253 * Returns: 0 on success, -1 on error.
254 **/
255int
256process_deserialise_all (json_object *json, const void *parent,
257 Process **processes)
258{
259 json_object *json_processes;
260 int i;
261
262 nih_assert (json);
263 nih_assert (parent);
264 nih_assert (processes);
265
266 json_processes = json_object_object_get (json, "process");
267
268 if (! json_processes)
269 goto error;
270
271 if (! state_check_json_type (json_processes, array))
272 goto error;
273
274 for (i = 0; i < json_object_array_length (json_processes); i++) {
275 json_object *json_process;
276
277 nih_assert (i <= PROCESS_LAST);
278
279 json_process = json_object_array_get_idx (json_processes, i);
280 if (! json_process)
281 goto error;
282
283 if (! state_check_json_type (json_process, object))
284 goto error;
285
286 processes[i] = process_deserialise (json_process, parent);
287
288 }
289
290 return 0;
291
292error:
293 return -1;
294}
295
296/**
297 * process_type_enum_to_str:
298 *
299 * @type: ProcessType.
300 *
301 * Convert ProcessType to a string representation.
302 *
303 * Returns: string representation of @type, or NULL if not known.
304 **/
305const char *
306process_type_enum_to_str (ProcessType type)
307{
308 state_enum_to_str (PROCESS_INVALID, type);
309 state_enum_to_str (PROCESS_MAIN, type);
310 state_enum_to_str (PROCESS_PRE_START, type);
311 state_enum_to_str (PROCESS_POST_START, type);
312 state_enum_to_str (PROCESS_PRE_STOP, type);
313 state_enum_to_str (PROCESS_POST_STOP, type);
314
315 return NULL;
316}
317
318/**
319 * process_type_str_to_enum:
320 *
321 * @type: string ProcessType.
322 *
323 * Convert @type back into enum value.
324 *
325 * Returns: ProcessType representation of @type, or -1 if not known.
326 **/
327ProcessType
328process_type_str_to_enum (const char *type)
329{
330 nih_assert (type);
331
332 state_str_to_enum (PROCESS_INVALID, type);
333 state_str_to_enum (PROCESS_MAIN, type);
334 state_str_to_enum (PROCESS_PRE_START, type);
335 state_str_to_enum (PROCESS_POST_START, type);
336 state_str_to_enum (PROCESS_PRE_STOP, type);
337 state_str_to_enum (PROCESS_POST_STOP, type);
338
339 return -1;
340}
341
119342
=== modified file 'init/process.h'
--- init/process.h 2009-06-23 09:29:35 +0000
+++ init/process.h 2012-11-14 14:49:20 +0000
@@ -22,20 +22,30 @@
2222
23#include <nih/macros.h>23#include <nih/macros.h>
2424
25#include <json.h>
2526
26/**27/**
27 * ProcessType:28 * ProcessType:
28 *29 *
29 * This is used to enumerate the array of process definitions attached to30 * This is used to enumerate the array of process definitions attached to
30 * a job class, and the array of pids attached to a job instance.31 * a job class, and the array of pids attached to a job instance.
32 *
33 * Note that PROCESS_INVALID would ideally be -1 but that isn't possible
34 * since process_type_str_to_enum() would then not be able to distinguish
35 * between an invalid ProcessType and the default value assigned to a
36 * ProcessType. It also cannot be zero since that would upset iterating
37 * through the (non-invalid) entries.
31 **/38 **/
32typedef enum process_type {39typedef enum process_type {
33 PROCESS_MAIN,40 /* initial value denoting no process */
41 PROCESS_INVALID = -2,
42
43 PROCESS_MAIN = 0,
34 PROCESS_PRE_START,44 PROCESS_PRE_START,
35 PROCESS_POST_START,45 PROCESS_POST_START,
36 PROCESS_PRE_STOP,46 PROCESS_PRE_STOP,
37 PROCESS_POST_STOP,47 PROCESS_POST_STOP,
38 PROCESS_LAST48 PROCESS_LAST,
39} ProcessType;49} ProcessType;
4050
4151
@@ -65,7 +75,32 @@
6575
66const char *process_name (ProcessType process)76const char *process_name (ProcessType process)
67 __attribute__ ((const));77 __attribute__ ((const));
68ProcessType process_from_name (const char *process);78
79json_object *process_serialise (const Process *process)
80 __attribute__ ((malloc, warn_unused_result));
81
82Process *process_deserialise (json_object *json, const void *parent)
83 __attribute__ ((malloc, warn_unused_result));
84
85json_object *
86process_serialise_all (const Process * const * const processes)
87 __attribute__ ((malloc, warn_unused_result));
88
89ProcessType process_from_name (const char *process)
90 __attribute__ ((warn_unused_result));
91
92int
93process_deserialise_all (json_object *json, const void *parent,
94 Process **processes)
95 __attribute__ ((warn_unused_result));
96
97const char *
98process_type_enum_to_str (ProcessType type)
99 __attribute__ ((warn_unused_result));
100
101ProcessType
102process_type_str_to_enum (const char *type)
103 __attribute__ ((warn_unused_result));
69104
70NIH_END_EXTERN105NIH_END_EXTERN
71106
72107
=== modified file 'init/session.c'
--- init/session.c 2011-12-09 14:07:11 +0000
+++ init/session.c 2012-11-14 14:49:20 +0000
@@ -47,6 +47,15 @@
47#include "conf.h"47#include "conf.h"
48#include "paths.h"48#include "paths.h"
4949
50extern json_object *json_sessions;
51
52/* Prototypes for static functions */
53static json_object *session_serialise (const Session *session)
54 __attribute__ ((malloc, warn_unused_result));
55
56static Session *session_deserialise (json_object *json)
57 __attribute__ ((malloc, warn_unused_result));
58
5059
51/**60/**
52 * sessions:61 * sessions:
@@ -66,8 +75,7 @@
6675
6776
68/* Prototypes for static functions */77/* Prototypes for static functions */
69static void session_create_conf_source (Session *sesson);78static void session_create_conf_source (Session *sesson, int deserialised);
70
7179
72/**80/**
73 * session_init:81 * session_init:
@@ -90,7 +98,7 @@
90 *98 *
91 * Create a new session.99 * Create a new session.
92 *100 *
93 * Return new Session, or NULL on error.101 * Returns: new Session, or NULL on error.
94 **/102 **/
95Session *103Session *
96session_new (const void *parent,104session_new (const void *parent,
@@ -137,7 +145,7 @@
137 *145 *
138 * Create a new session, based on the specified D-Bus message.146 * Create a new session, based on the specified D-Bus message.
139 *147 *
140 * Return new Session, or NULL on error.148 * Returns: new Session, or NULL on error.
141 **/149 **/
142Session *150Session *
143session_from_dbus (const void *parent,151session_from_dbus (const void *parent,
@@ -248,7 +256,7 @@
248 continue;256 continue;
249257
250 /* Found a user with the same uid but different258 /* Found a user with the same uid but different
251 * conf_dir to the existing session user. Either the259 * conf_path to the existing session user. Either the
252 * original user has been deleted and a new user created260 * original user has been deleted and a new user created
253 * with the same uid, or the original users home261 * with the same uid, or the original users home
254 * directory has changed since they first started262 * directory has changed since they first started
@@ -273,7 +281,7 @@
273 }281 }
274282
275 if (! session->conf_path)283 if (! session->conf_path)
276 session_create_conf_source (session);284 session_create_conf_source (session, FALSE);
277285
278 return session;286 return session;
279 }287 }
@@ -282,46 +290,54 @@
282 /* Didn't find one, make a new one */290 /* Didn't find one, make a new one */
283 session = NIH_MUST (session_new (parent, unix_process_id ? root : NULL,291 session = NIH_MUST (session_new (parent, unix_process_id ? root : NULL,
284 unix_user));292 unix_user));
285 session_create_conf_source (session);293 session_create_conf_source (session, FALSE);
286294
287 return session;295 return session;
288}296}
289297
290/**298/**
291 * session_create_conf_source:299 * session_create_conf_source:
292 * @session: Session.300 * @session: Session,
301 * @deserialised: TRUE if ConfSource is to be created from a deserialised
302 * session object.
293 *303 *
294 * Create a new ConfSouce object and associate the specified session304 * Create a new ConfSource object and associate the specified Session
295 * with it.305 * with it.
296 **/306 **/
297static void307static void
298session_create_conf_source (Session *session)308session_create_conf_source (Session *session, int deserialised)
299{309{
300 ConfSource *source;310 ConfSource *source;
301311
302 nih_assert (session != NULL);312 nih_assert (session != NULL);
303 nih_assert (session->conf_path == NULL);313 nih_assert (deserialised
304314 ? session->conf_path != NULL
305 if (session->chroot)315 : session->conf_path == NULL);
306 session->conf_path = NIH_MUST (nih_strdup (NULL, session->chroot));316
307 if (session->user) {317 session_init ();
308 struct passwd *pwd;318
309319 if (! deserialised) {
310 pwd = getpwuid (session->user);320 if (session->chroot)
311 if (! pwd) {321 session->conf_path = NIH_MUST (nih_strdup (NULL, session->chroot));
312 nih_error ("%d: %s: %s", session->user,322 if (session->user) {
313 _("Unable to lookup home directory"),323 struct passwd *pwd;
314 strerror (errno));324
315325 pwd = getpwuid (session->user);
316 nih_free (session->conf_path);326 if (! pwd) {
317 session->conf_path = NULL;327 nih_error ("%d: %s: %s", session->user,
318 return;328 _("Unable to lookup home directory"),
329 strerror (errno));
330
331 nih_free (session->conf_path);
332 session->conf_path = NULL;
333 return;
334 }
335
336 NIH_MUST (nih_strcat_sprintf (&session->conf_path, NULL, "%s/%s",
337 pwd->pw_dir, USERCONFDIR));
338 } else {
339 NIH_MUST (nih_strcat (&session->conf_path, NULL, CONFDIR));
319 }340 }
320
321 NIH_MUST (nih_strcat_sprintf (&session->conf_path, NULL, "%s/%s",
322 pwd->pw_dir, USERCONFDIR));
323 } else {
324 NIH_MUST (nih_strcat (&session->conf_path, NULL, CONFDIR));
325 }341 }
326342
327 source = NIH_MUST (conf_source_new (session, session->conf_path,343 source = NIH_MUST (conf_source_new (session, session->conf_path,
@@ -339,8 +355,257 @@
339 nih_free (err);355 nih_free (err);
340356
341 nih_free (source);357 nih_free (source);
342 nih_free (session->conf_path);
343 session->conf_path = NULL;
344 return;358 return;
345 }359 }
346}360}
361
362/**
363 * session_serialise:
364 * @session: session to serialise.
365 *
366 * Convert @session into a JSON representation for serialisation.
367 *
368 * Caller must free returned value using json_object_put().
369 *
370 * Returns: JSON-serialised Session object, or NULL on error.
371 **/
372static json_object *
373session_serialise (const Session *session)
374{
375 json_object *json;
376 json_object *conf_path = NULL;
377 json_object *chroot = NULL;
378 json_object *user;
379
380 nih_assert (session);
381
382 session_init ();
383
384 json = json_object_new_object ();
385 if (! json)
386 return NULL;
387
388 if (session->chroot) {
389 chroot = json_object_new_string (session->chroot);
390 if (! chroot)
391 goto error;
392 }
393
394 json_object_object_add (json, "chroot", chroot);
395
396 user = state_new_json_int (session->user);
397 if (! user)
398 goto error;
399
400 json_object_object_add (json, "user", user);
401
402 if (session->conf_path) {
403 conf_path = json_object_new_string (session->conf_path);
404 if (! conf_path)
405 goto error;
406 }
407
408 json_object_object_add (json, "conf_path", conf_path);
409
410 return json;
411
412error:
413 json_object_put (json);
414 return NULL;
415
416}
417
418/**
419 * session_serialise_all:
420 *
421 * Convert existing Session objects to JSON representation.
422 *
423 * Returns: JSON object containing array of Sessions, or NULL on error.
424 **/
425json_object *
426session_serialise_all (void)
427{
428 json_object *json;
429 json_object *json_session;
430
431 session_init ();
432
433 json = json_object_new_array ();
434 if (! json)
435 return NULL;
436
437 NIH_LIST_FOREACH (sessions, iter) {
438 Session *session = (Session *)iter;
439
440 json_session = session_serialise (session);
441
442 if (! json_session)
443 goto error;
444
445 if (json_object_array_add (json, json_session) < 0)
446 goto error;
447 }
448
449 return json;
450
451error:
452 json_object_put (json);
453 return NULL;
454}
455
456/**
457 * session_deserialise:
458 * @json: JSON-serialised Session object to deserialise.
459 *
460 * Convert @json into a Session object.
461 *
462 * Returns: Session object, or NULL on error.
463 **/
464static Session *
465session_deserialise (json_object *json)
466{
467 Session *session = NULL;
468 nih_local const char *chroot = NULL;
469 uid_t user;
470
471 nih_assert (json);
472
473 if (! state_check_json_type (json, object))
474 return NULL;
475
476 /* Note no check on value returned since chroot may be NULL */
477 if (! state_get_json_string_var (json, "chroot", NULL, chroot))
478 return NULL;
479
480 if (! state_get_json_int_var (json, "user", user))
481 return NULL;
482
483 /* Create a new session */
484 session = NIH_MUST (session_new (NULL, chroot, user));
485
486 if (! state_get_json_string_var_to_obj (json, session, conf_path))
487 goto error;
488
489 return session;
490
491error:
492 nih_free (session);
493 return NULL;
494}
495
496/**
497 * session_deserialise_all:
498 *
499 * @json: root of JSON-serialised state.
500 *
501 * Convert JSON representation of sessions back into Session objects.
502 *
503 * Returns: 0 on success, -1 on error.
504 **/
505int
506session_deserialise_all (json_object *json)
507{
508 Session *session;
509
510 nih_assert (json);
511
512 session_init ();
513
514 nih_assert (NIH_LIST_EMPTY (sessions));
515
516 json_sessions = json_object_object_get (json, "sessions");
517
518 if (! json_sessions)
519 goto error;
520
521 if (! state_check_json_type (json_sessions, array))
522 goto error;
523
524 for (int i = 0; i < json_object_array_length (json_sessions); i++) {
525 json_object *json_session;
526
527 json_session = json_object_array_get_idx (json_sessions, i);
528 if (! json_session)
529 goto error;
530
531 if (! state_check_json_type (json_session, object))
532 goto error;
533
534 session = session_deserialise (json_session);
535 if (! session)
536 goto error;
537
538 session_create_conf_source (session, TRUE);
539 }
540
541 return 0;
542
543error:
544 return -1;
545}
546
547/**
548 * session_get_index:
549 *
550 * @session: session.
551 *
552 * Determine JSON-serialised array index for specified @session.
553 *
554 * Returns: zero-based array index for @session, or -1 on error.
555 **/
556int
557session_get_index (const Session *session)
558{
559 int i;
560
561 /* Handle NULL session (which is not encoded) */
562 if (! session)
563 return 0;
564
565 /* Sessions are serialised in order, so just return the list
566 * index.
567 */
568 i = 1;
569 NIH_LIST_FOREACH (sessions, iter) {
570 Session *s = (Session *)iter;
571
572 if (s == session)
573 return i;
574
575 ++i;
576 }
577
578 return -1;
579}
580
581/**
582 * session_from_index:
583 *
584 * @idx: zero-based index.
585 *
586 * Lookup session by index number.
587 *
588 * Returns: Session (which may be the NULL session).
589 **/
590Session *
591session_from_index (int idx)
592{
593 int i;
594 Session *session;
595
596 nih_assert (idx >= 0);
597
598 /* NULL session */
599 if (! idx)
600 return NULL;
601
602 i = 1;
603 NIH_LIST_FOREACH (sessions, iter) {
604 session = (Session *)iter;
605
606 if (i == idx)
607 return session;
608 }
609
610 nih_assert_not_reached ();
611}
347612
=== modified file 'init/session.h'
--- init/session.h 2011-07-25 14:28:33 +0000
+++ init/session.h 2012-11-14 14:49:20 +0000
@@ -27,6 +27,7 @@
2727
28#include <nih-dbus/dbus_message.h>28#include <nih-dbus/dbus_message.h>
2929
30#include <json.h>
3031
31/**32/**
32 * Session:33 * Session:
@@ -77,12 +78,24 @@
7778
78extern NihList *sessions;79extern NihList *sessions;
7980
80void session_init (void);81void session_init (void);
8182
82Session *session_new (const void *parent, const char *chroot, uid_t user)83Session * session_new (const void *parent, const char *chroot, uid_t user)
83 __attribute__ ((malloc, warn_unused_result));84 __attribute__ ((malloc, warn_unused_result));
8485
85Session *session_from_dbus (const void *parent, NihDBusMessage *message);86Session * session_from_dbus (const void *parent, NihDBusMessage *message);
87
88json_object * session_serialise_all (void)
89 __attribute__ ((malloc, warn_unused_result));
90
91int session_deserialise_all (json_object *json)
92 __attribute__ ((warn_unused_result));
93
94int session_get_index (const Session *session)
95 __attribute__ ((warn_unused_result));
96
97Session * session_from_index (int idx)
98 __attribute__ ((warn_unused_result));
8699
87NIH_END_EXTERN100NIH_END_EXTERN
88101
89102
=== added file 'init/state.c'
--- init/state.c 1970-01-01 00:00:00 +0000
+++ init/state.c 2012-11-14 14:49:20 +0000
@@ -0,0 +1,2008 @@
1/* upstart
2 *
3 * state.c - serialisation and deserialisation support.
4 *
5 * Copyright © 2012 Canonical Ltd.
6 * Author: James Hunt <james.hunt@canonical.com>.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2, as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include <string.h>
23#include <sys/types.h>
24#include <signal.h>
25#include <unistd.h>
26#include <errno.h>
27#include <sys/select.h>
28#include <unistd.h>
29#include <fcntl.h>
30
31#include <nih/macros.h>
32#include <nih/logging.h>
33#include <nih/string.h>
34#include <nih/io.h>
35
36#include "paths.h"
37#include "state.h"
38#include "session.h"
39#include "event.h"
40#include "job_class.h"
41#include "job.h"
42#include "environ.h"
43#include "blocked.h"
44#include "conf.h"
45#include "control.h"
46
47json_object *json_sessions = NULL;
48json_object *json_events = NULL;
49json_object *json_classes = NULL;
50
51extern int use_session_bus;
52
53/**
54 * args_copy:
55 *
56 * Copy of original argv used when re-executing to ensure same
57 * command-line is used. Required since we clear the actual args for
58 * ps(1) et al.
59 */
60char **args_copy = NULL;
61
62/**
63 * restart:
64 *
65 * This is set to TRUE if we're being re-exec'd by an existing init
66 * process.
67 **/
68int restart = FALSE;
69
70/* Prototypes for static functions */
71static json_object *
72state_serialise_blocked (const Blocked *blocked)
73 __attribute__ ((malloc, warn_unused_result));
74
75static Blocked *
76state_deserialise_blocked (void *parent, json_object *json, NihList *list)
77 __attribute__ ((malloc, warn_unused_result));
78
79static JobClass *
80state_index_to_job_class (int job_class_index)
81 __attribute__ ((warn_unused_result));
82
83static Job *
84state_get_job (const char *job_class, const char *job_name)
85 __attribute__ ((warn_unused_result));
86
87/**
88 * state_read:
89 *
90 * @fd: Open file descriptor to read JSON from.
91 *
92 * Read JSON-encoded state from specified file descriptor and recreate
93 * all internal objects based on JSON representation. The read will
94 * timeout, resulting in a failure after STATE_WAIT_SECS seconds
95 * indicating a problem with the child.
96 *
97 * Returns: 0 on success, or -1 on error.
98 **/
99int
100state_read (int fd)
101{
102 int nfds;
103 int ret;
104 fd_set readfds;
105 struct timeval timeout;
106
107 nih_assert (fd != -1);
108
109 state_get_timeout (timeout.tv_sec);
110 timeout.tv_usec = 0;
111
112 nfds = 1 + fd;
113
114 while (TRUE) {
115 FD_ZERO (&readfds);
116 FD_SET (fd, &readfds);
117
118 ret = select (nfds, &readfds, NULL, NULL,
119 timeout.tv_sec < 0 ? NULL : &timeout);
120
121 if (ret < 0 && (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK))
122 return -1;
123
124 if (FD_ISSET (fd, &readfds))
125 break;
126 }
127
128 nih_assert (ret == 1);
129
130 /* Now, read the data */
131 if (state_read_objects (fd) < 0)
132 return -1;
133
134 return 0;
135}
136
137/**
138 * state_write:
139 *
140 * @fd: Open file descriptor to write JSON to,
141 * @state_data: JSON string representing internal object state,
142 * @len: length of @state_data.
143 *
144 * Write internal state to specified file descriptor in JSON format.
145 *
146 * Signals are assumed to be blocked when this call is made.
147 *
148 * Note the timeout - it is possible that the new PID 1 instance may be
149 * unable to read from its end of the file descriptor, either due to
150 * some error scenario or more likely due to it not supporting stateful
151 * re-exec. Hence, we must have a way to detect this and abort the
152 * child.
153 *
154 * Returns: 0 on success, or -1 on error.
155 **/
156int
157state_write (int fd, const char *state_data, size_t len)
158{
159 int nfds;
160 int ret;
161 fd_set writefds;
162 struct timeval timeout;
163
164 nih_assert (fd != -1);
165 nih_assert (state_data);
166 nih_assert (len);
167
168 /* must be called from child process */
169 nih_assert (getpid () != (pid_t)1);
170
171 state_get_timeout (timeout.tv_sec);
172 timeout.tv_usec = 0;
173
174 nfds = 1 + fd;
175
176 while (TRUE) {
177 FD_ZERO (&writefds);
178 FD_SET (fd, &writefds);
179
180 ret = select (nfds, NULL, &writefds, NULL,
181 timeout.tv_sec < 0 ? NULL : &timeout);
182
183 if (ret < 0 && (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK))
184 return -1;
185
186 if (FD_ISSET (fd, &writefds))
187 break;
188 }
189
190 nih_assert (ret == 1);
191
192 if (state_write_objects (fd, state_data, len) < 0)
193 return -1;
194
195 return 0;
196}
197
198
199/**
200 * state_read_objects:
201 *
202 * @fd: file descriptor to read serialisation data from.
203 *
204 * Read serialisation data from specified file descriptor.
205 * @fd is assumed to be open and readable.
206 *
207 * Returns: 0 on success, -1 on error.
208 **/
209int
210state_read_objects (int fd)
211{
212 ssize_t ret;
213 int initial_size = 4096;
214 nih_local NihIoBuffer *buffer = NULL;
215 nih_local char *buf = NULL;
216
217 nih_assert (fd != -1);
218
219 buffer = nih_io_buffer_new (NULL);
220
221 buf = nih_alloc (NULL, initial_size);
222 if (! buf)
223 goto error;
224
225 /* Read the JSON data into the buffer */
226 do {
227 if (nih_io_buffer_resize (buffer, initial_size) < 0)
228 goto error;
229
230 ret = read (fd, buf, sizeof (buf));
231 if (ret < 0) {
232 if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
233 goto error;
234 continue;
235 } else if (! ret)
236 break;
237
238 if (nih_io_buffer_push (buffer, buf, ret) < 0)
239 goto error;
240 } while (TRUE);
241
242 /* Recreate internal state from JSON */
243 if (state_from_string (buffer->buf) < 0)
244 goto error;
245
246 return 0;
247
248error:
249 return -1;
250}
251
252/**
253 * state_write_objects:
254 *
255 * @fd: file descriptor to write serialisation data on,
256 * @state_data: JSON string representing internal object state,
257 * @len: length of @state_data.
258 *
259 * Write serialisation data to specified file descriptor.
260 * @fd is assumed to be open and valid to write to.
261 *
262 * Returns: 0 on success, -1 on error.
263 **/
264int
265state_write_objects (int fd, const char *state_data, size_t len)
266{
267 ssize_t ret;
268
269 nih_assert (fd != -1);
270 nih_assert (state_data);
271 nih_assert (len);
272
273 ret = write (fd, state_data, len);
274
275 return (ret < 0 ? -1 : 0);
276}
277
278/**
279 * state_to_string:
280 *
281 * @json_string; newly-allocated string,
282 * @len: length of @json_string.
283 *
284 * Serialise internal data structures to a JSON string.
285 *
286 * Returns: 0 on success, -1 on error.
287 **/
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches