Merge lp:~jamesodhunt/upstart/allow-multiple-cmdline-confdirs into lp:upstart

Proposed by James Hunt
Status: Merged
Merged at revision: 1484
Proposed branch: lp:~jamesodhunt/upstart/allow-multiple-cmdline-confdirs
Merge into: lp:upstart
Diff against target: 3637 lines (+1985/-1276) (has conflicts)
17 files modified
ChangeLog (+39/-0)
Makefile.am (+1/-1)
configure.ac (+1/-1)
init/Makefile.am (+36/-7)
init/main.c (+58/-26)
init/man/init.8 (+7/-4)
init/state.c (+4/-0)
init/tests/test_job_process.c (+0/-6)
init/tests/test_main.c (+452/-0)
init/tests/test_util.c (+0/-27)
init/tests/test_util.h (+0/-304)
init/xdg.c (+4/-2)
test/Makefile.am (+21/-0)
test/test_util.c (+628/-0)
test/test_util.h (+725/-0)
util/Makefile.am (+3/-10)
util/tests/test_initctl.c (+6/-888)
Text conflict in ChangeLog
Text conflict in init/Makefile.am
To merge this branch: bzr merge lp:~jamesodhunt/upstart/allow-multiple-cmdline-confdirs
Reviewer Review Type Date Requested Status
Dimitri John Ledkov Approve
James Hunt Needs Resubmitting
Review via email: mp+156512@code.launchpad.net

Description of the change

This change is required for the upcoming test framework since it is necessary to allow a Session Init to operate in a known environment (crucially without loading in all the standard Session Jobs provided on, for example, an Ubuntu Raring system).

Note this introduces a small behavioural change in that now if --confdir is specified one or more times for a Session Init, *only* those directories specified will be used (whereas before the single confdir specified would be prepended to the standard list of directories searched).

= Future Work =

It might be worth adding the ability for the list of directories being used to be queried both via a new initctl command (for a running instance), and via an init option to see what user directories *would* be used.

= Change Summary =

* init/main.c:
  - main(): Allow Session Init to accept multiple --confdir values,
    which replace all built-in values (behaviour required for testing).
  - handle_confdir(): Operate on conf_dirs array rather than single
    conf_dir value.
  - conf_dir_setter(): Command-line setter function to add configuration
    file directories to conf_dirs array.
* init/man/init.8: Update on --confdir behaviour.

To post a comment you must log in.
Revision history for this message
Dimitri John Ledkov (xnox) wrote :
Download full text (4.5 KiB)

On 2 April 2013 11:31, James Hunt <email address hidden> wrote:
>
> === modified file 'init/main.c'
> --- init/main.c 2013-03-01 15:13:54 +0000
> +++ init/main.c 2013-04-02 10:30:30 +0000
> @@ -88,6 +88,7 @@
> static void handle_confdir (void);
> static void handle_logdir (void);
> static int console_type_setter (NihOption *option, const char *arg);
> +static int conf_dir_setter (NihOption *option, const char *arg);
>
>
> /**
> @@ -99,12 +100,11 @@
> static int state_fd = -1;
>
> /**
> - * conf_dir:
> - *
> - * Full path to job configuration file directory.
> - *
> + * conf_dirs:
> + *
> + * Array of full paths to job configuration file directories.
> **/
> -static char *conf_dir = NULL;
> +static char **conf_dirs = NULL;
>
> /**
> * initial_event:
> @@ -136,7 +136,7 @@
> **/
> static NihOption options[] = {
> { 0, "confdir", N_("specify alternative directory to load configuration files from"),
> - NULL, "DIR", &conf_dir, NULL },
> + NULL, "DIR", NULL, conf_dir_setter },
>
> { 0, "default-console", N_("default value for console stanza"),
> NULL, "VALUE", NULL, console_type_setter },
> @@ -185,9 +185,10 @@
> char *argv[])
> {
> char **args = NULL;
> - char **dirs = NULL;
> int ret;
>
> + conf_dirs = NIH_MUST (nih_str_array_new (NULL));
> +
> args_copy = NIH_MUST (nih_str_array_copy (NULL, NULL, argv));
>
> nih_main_init (args_copy[0]);
> @@ -532,19 +533,38 @@
> }
>
> /* Read configuration */
> - if (! user_mode)
> + if (! user_mode) {
> + char *conf_dir;
> + int len = 0;
> +
> + nih_assert (conf_dirs[0]);
> +
> + /* Count entries */
> + for (char **d = conf_dirs; d && *d; d++, len++)
> + ;
> +
> + nih_assert (len);
> +
> + /* Use last value specified */
> + conf_dir = conf_dirs[len-1];
> +
> NIH_MUST (conf_source_new (NULL, CONFFILE, CONF_FILE));
>
> - if (conf_dir)
> + nih_debug ("Using configuration directory %s", conf_dir);
> NIH_MUST (conf_source_new (NULL, conf_dir, CONF_JOB_DIR));
> + } else {
> + nih_local char **dirs = NULL;
>
> - if (user_mode) {
> dirs = NIH_MUST (get_user_upstart_dirs ());
> - for (char **d = dirs; d && *d; d++)
> +
> + for (char **d = conf_dirs[0] ? conf_dirs : dirs; d && *d; d++) {
> + nih_debug ("Using configuration directory %s", *d);
> NIH_MUST (conf_source_new (NULL, *d, CONF_JOB_DIR));
> - nih_free (dirs);
> + }
> }
>
> + nih_free (conf_dirs);
> +

Why not allow "system" init to have mulple conf_dirs as well?
Or was it intentional to preserve previous behaviour (last one wins)?
I'm not sure it's worth preserving the behaviour, and I think it is
useful to support multiple confdirs even for system init. It has a
usecase in the phablet / cloud scenarios where rootfs can be read-only
and the res...

Read more...

Revision history for this message
James Hunt (jamesodhunt) wrote :

That's a good question. Yes, this was designed as a minimal change to retain existing behaviour when running as PID 1.

Whilst I'd rather we don't encourage use of --confdir for PID 1, the fact that the option is already there and allows for a custom location today, maybe we should consider allowing this.

That said, allowing multiple conf dirs for PID 1 does increase the foot-shooting ability slightly should one or more of those locations not be available at startup time resulting in a broken boot. By specifying a single --confdir directory, it'll be immediately obvious if that directory is not available at startup (as the system will never boot). Whereas with multiple ---confdirs, it may be less so. Added to which, talk of overlays reminds me of the still-outstanding kernel bug 882147.

Revision history for this message
Stéphane Graber (stgraber) wrote :

I think the change looks good and agree that it's probably a good idea to limit this to Session Init for now.

Should we have some tests for this? I'd think the following cases would be good to test:
 - User, no --confdir => loads all jobs from default paths
 - User, --confdir a => only loads jobs from "a"
 - User, --confdir a --confdir b => loads jobs from "a" and "b"
 - System, no --confdir => loads all jobs from default path
 - System, --confdir a => loads all jobs from "a"
 - System, --confdir a --confdir b => loads all jobs from "b"

1473. By James Hunt

* Makefile.am: Added 'test'.
* configure.ac: Added 'test/Makefile'.
* init/Makefile.am: Depend on test_util archive rather than source
  files.
* init/state.c: Added missing config.h include.
* util/Makefile.am: Depend on test_util archive rather than source
  files.
* util/tests/test_initctl.c: Moved common utility code to
  test/test_util.c and replaced INITCTL_BINARY by get_initctl_binary().
* test/test_util.[ch]: New location for init/tests/test_util.[ch] to
  allow all tests access to common functionality.

1474. By James Hunt

* init/Makefile.am: Added test_main.
* init/tests/test_main.c: New test.
* init/tests/test_job_process.c: strcmp_compar() moved to test_util.c.
* util/tests/test_initctl.c: strcmp_compar() removed.
* init/xdg.c: Disable loading of jobs from SYSTEM_USERCONFDIR if
  UPSTART_NO_SYSTEM_USERCONFDIR envvar set (required for testing).
* test/test_util.c: Added strcmp_compar() and get_session_file().
* util/tests/test_initctl.c: Use get_session_file().

Revision history for this message
James Hunt (jamesodhunt) wrote :

Agreed - new tests added (which necessitated creating test/test_util.[ch] for test code sharing). We now have a test_main test...

Testing --confdir command-line option handling
...Session Init without --confdir
...Session Init with --confdir
...Session Init with multiple --confdir
...Session Init with multiple --confdir and conflicting names
...System Init without --confdir
...System Init with --confdir
...System Init with multiple --confdir
...System Init with multiple --confdir and conflicting names
PASS: test_main

review: Needs Resubmitting
Revision history for this message
Dimitri John Ledkov (xnox) wrote :

The new tests look good to me & pass here.
Merging into trunk results in bzr conflicts, so please resolve them yourself. Apart from that, approved.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2013-05-28 15:12:19 +0000
3+++ ChangeLog 2013-05-30 13:17:28 +0000
4@@ -1,3 +1,4 @@
5+<<<<<<< TREE
6 2013-05-27 Marc Deslauriers <marc.deslauriers@ubuntu.com>
7
8 * init/job.c: Don't check for user mode when trying to load an
9@@ -88,6 +89,44 @@
10 - Corrected bridge name.
11 - Explain that blocking semantics of system jobs not retained.
12
13+=======
14+2013-04-30 James Hunt <james.hunt@ubuntu.com>
15+
16+ * init/Makefile.am: Added test_main.
17+ * init/tests/test_main.c: New test.
18+ * init/tests/test_job_process.c: strcmp_compar() moved to test_util.c.
19+ * util/tests/test_initctl.c: strcmp_compar() removed.
20+ * init/xdg.c: Disable loading of jobs from SYSTEM_USERCONFDIR if
21+ "UPSTART_NO_SYSTEM_USERCONFDIR" envvar set (required for testing).
22+ * test/test_util.c: Added strcmp_compar() and get_session_file().
23+ * util/tests/test_initctl.c: Use get_session_file().
24+
25+2013-04-30 James Hunt <james.hunt@ubuntu.com>
26+
27+ * Makefile.am: Added 'test'.
28+ * configure.ac: Added 'test/Makefile'.
29+ * init/Makefile.am: Depend on test_util archive rather than source
30+ files.
31+ * init/state.c: Added missing config.h include.
32+ * util/Makefile.am: Depend on test_util archive rather than source
33+ files.
34+ * util/tests/test_initctl.c: Moved common utility code to
35+ test/test_util.c and replaced INITCTL_BINARY by get_initctl_binary().
36+ * test/test_util.[ch]: New location for init/tests/test_util.[ch] to
37+ allow all tests access to common functionality.
38+
39+2013-04-02 James Hunt <james.hunt@ubuntu.com>
40+
41+ * init/main.c:
42+ - main(): Allow Session Init to accept multiple --confdir values,
43+ which replace all built-in values (behaviour required for testing).
44+ - handle_confdir(): Operate on conf_dirs array rather than single
45+ conf_dir value.
46+ - conf_dir_setter(): Command-line setter function to add configuration
47+ file directories to conf_dirs array.
48+ * init/man/init.8: Update on --confdir behaviour.
49+
50+>>>>>>> MERGE-SOURCE
51 2013-03-28 James Hunt <james.hunt@ubuntu.com>
52
53 * scripts/upstart-monitor.py: on_button_press_event():
54
55=== modified file 'Makefile.am'
56--- Makefile.am 2013-03-04 11:44:49 +0000
57+++ Makefile.am 2013-05-30 13:17:28 +0000
58@@ -1,6 +1,6 @@
59 ## Process this file with automake to produce Makefile.in
60
61-SUBDIRS = intl dbus init util extra conf doc contrib po scripts
62+SUBDIRS = test intl dbus init util extra conf doc contrib po scripts
63
64 EXTRA_DIST = HACKING
65
66
67=== modified file 'configure.ac'
68--- configure.ac 2013-03-22 14:34:15 +0000
69+++ configure.ac 2013-05-30 13:17:28 +0000
70@@ -101,6 +101,6 @@
71 AC_CONFIG_FILES([ Makefile intl/Makefile
72 dbus/Makefile init/Makefile util/Makefile conf/Makefile
73 extra/Makefile doc/Makefile contrib/Makefile po/Makefile.in
74- scripts/Makefile scripts/data/Makefile ])
75+ scripts/Makefile scripts/data/Makefile test/Makefile ])
76 AC_CONFIG_HEADERS([config.h])
77 AC_OUTPUT
78
79=== modified file 'init/Makefile.am'
80--- init/Makefile.am 2013-05-15 13:21:54 +0000
81+++ init/Makefile.am 2013-05-30 13:17:28 +0000
82@@ -16,8 +16,8 @@
83 -DCONFDIR="\"$(initconfdir)\"" \
84 -DSBINDIR="\"$(sbindir)\"" \
85 -I$(top_builddir) -I$(top_srcdir) -iquote$(builddir) -iquote$(srcdir) \
86- -I$(top_srcdir)/intl
87-
88+ -I$(top_srcdir)/intl \
89+ -I$(top_srcdir)/test -iquote$(top_srcdir)/test
90
91 dist_man_MANS = \
92 man/init.8 \
93@@ -137,9 +137,14 @@
94 TEST_DATA_FILES = \
95 $(TEST_DATA_DIR)/upstart-1.6.json
96
97+<<<<<<< TREE
98 test_util_SOURCES = \
99 tests/test_util.c tests/test_util.h
100
101+=======
102+EXTRA_DIST = init.supp $(TEST_DATA_FILES)
103+
104+>>>>>>> MERGE-SOURCE
105 TESTS = \
106 test_system \
107 test_environ \
108@@ -156,7 +161,8 @@
109 test_parse_conf \
110 test_conf_static \
111 test_xdg \
112- test_control
113+ test_control \
114+ test_main
115
116 EXTRA_DIST = init.supp $(TEST_DATA_FILES) tests/test_conf_preload.sh.in
117
118@@ -182,7 +188,7 @@
119 environ.o \
120 $(NIH_LIBS)
121
122-test_process_SOURCES = tests/test_process.c $(test_util_SOURCES)
123+test_process_SOURCES = tests/test_process.c
124 test_process_LDADD = \
125 system.o environ.o process.o \
126 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
127@@ -190,6 +196,7 @@
128 session.o log.o state.o xdg.o apparmor.o \
129 com.ubuntu.Upstart.o \
130 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
131+ $(top_builddir)/test/libtest_util.a \
132 $(NIH_LIBS) \
133 $(NIH_DBUS_LIBS) \
134 $(DBUS_LIBS) \
135@@ -210,7 +217,7 @@
136 $(JSON_LIBS) \
137 -lrt
138
139-test_job_process_SOURCES = tests/test_job_process.c $(test_util_SOURCES)
140+test_job_process_SOURCES = tests/test_job_process.c
141 test_job_process_LDADD = \
142 system.o environ.o process.o \
143 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
144@@ -218,6 +225,7 @@
145 session.o log.o state.o xdg.o apparmor.o \
146 com.ubuntu.Upstart.o \
147 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
148+ $(top_builddir)/test/libtest_util.a \
149 $(NIH_LIBS) \
150 $(NIH_DBUS_LIBS) \
151 $(DBUS_LIBS) \
152@@ -238,7 +246,7 @@
153 $(JSON_LIBS) \
154 -lrt
155
156-test_log_SOURCES = tests/test_log.c $(test_util_SOURCES)
157+test_log_SOURCES = tests/test_log.c
158 test_log_LDADD = \
159 system.o environ.o process.o \
160 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
161@@ -246,13 +254,14 @@
162 session.o log.o state.o xdg.o apparmor.o \
163 com.ubuntu.Upstart.o \
164 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
165+ $(top_builddir)/test/libtest_util.a \
166 $(NIH_LIBS) \
167 $(NIH_DBUS_LIBS) \
168 $(DBUS_LIBS) \
169 $(JSON_LIBS) \
170 -lrt -lutil
171
172-test_state_SOURCES = tests/test_state.c $(test_util_SOURCES)
173+test_state_SOURCES = tests/test_state.c
174 test_state_LDADD = \
175 system.o environ.o process.o \
176 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
177@@ -260,6 +269,7 @@
178 session.o log.o state.o xdg.o apparmor.o \
179 com.ubuntu.Upstart.o \
180 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
181+ $(top_builddir)/test/libtest_util.a \
182 $(NIH_LIBS) \
183 $(NIH_DBUS_LIBS) \
184 $(DBUS_LIBS) \
185@@ -336,12 +346,16 @@
186 $(JSON_LIBS) \
187 -lrt
188
189+<<<<<<< TREE
190 check_LTLIBRARIES = tests/libwrap_inotify.la
191 tests_libwrap_inotify_la_SOURCES = tests/wrap_inotify.c
192 tests_libwrap_inotify_la_LIBADD = -lrt -ldl $(AM_LIBADD)
193 tests_libwrap_inotify_la_LDFLAGS = avoid-version -module -shared -export-dynamic -rpath /nowhere -ldl
194
195 test_conf_SOURCES = tests/test_conf.c $(test_util_SOURCES) $(check_LTLIBRARIES)
196+=======
197+test_conf_SOURCES = tests/test_conf.c
198+>>>>>>> MERGE-SOURCE
199 test_conf_LDADD = \
200 system.o environ.o process.o \
201 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
202@@ -349,6 +363,7 @@
203 session.o log.o state.o xdg.o apparmor.o \
204 com.ubuntu.Upstart.o \
205 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
206+ $(top_builddir)/test/libtest_util.a \
207 $(NIH_LIBS) \
208 $(NIH_DBUS_LIBS) \
209 $(DBUS_LIBS) \
210@@ -390,6 +405,20 @@
211 $(JSON_LIBS) \
212 -lrt
213
214+test_main_SOURCES = tests/test_main.c
215+test_main_LDADD = \
216+ system.o environ.o process.o \
217+ job_class.o job_process.o job.o event.o event_operator.o blocked.o \
218+ parse_job.o parse_conf.o conf.o control.o quiesce.o \
219+ session.o log.o state.o xdg.o \
220+ com.ubuntu.Upstart.o \
221+ com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
222+ $(top_builddir)/test/libtest_util.a \
223+ $(NIH_LIBS) \
224+ $(NIH_DBUS_LIBS) \
225+ $(DBUS_LIBS) \
226+ $(JSON_LIBS) \
227+ -lrt
228
229 install-data-local:
230 $(MKDIR_P) $(DESTDIR)$(initconfdir)
231
232=== modified file 'init/main.c'
233--- init/main.c 2013-04-22 10:30:09 +0000
234+++ init/main.c 2013-05-30 13:17:28 +0000
235@@ -88,6 +88,7 @@
236 static void handle_confdir (void);
237 static void handle_logdir (void);
238 static int console_type_setter (NihOption *option, const char *arg);
239+static int conf_dir_setter (NihOption *option, const char *arg);
240
241
242 /**
243@@ -99,12 +100,11 @@
244 static int state_fd = -1;
245
246 /**
247- * conf_dir:
248- *
249- * Full path to job configuration file directory.
250- *
251+ * conf_dirs:
252+ *
253+ * Array of full paths to job configuration file directories.
254 **/
255-static char *conf_dir = NULL;
256+static char **conf_dirs = NULL;
257
258 /**
259 * initial_event:
260@@ -136,7 +136,7 @@
261 **/
262 static NihOption options[] = {
263 { 0, "confdir", N_("specify alternative directory to load configuration files from"),
264- NULL, "DIR", &conf_dir, NULL },
265+ NULL, "DIR", NULL, conf_dir_setter },
266
267 { 0, "default-console", N_("default value for console stanza"),
268 NULL, "VALUE", NULL, console_type_setter },
269@@ -185,9 +185,10 @@
270 char *argv[])
271 {
272 char **args = NULL;
273- char **dirs = NULL;
274 int ret;
275
276+ conf_dirs = NIH_MUST (nih_str_array_new (NULL));
277+
278 args_copy = NIH_MUST (nih_str_array_copy (NULL, NULL, argv));
279
280 nih_main_init (args_copy[0]);
281@@ -532,19 +533,38 @@
282 }
283
284 /* Read configuration */
285- if (! user_mode)
286+ if (! user_mode) {
287+ char *conf_dir;
288+ int len = 0;
289+
290+ nih_assert (conf_dirs[0]);
291+
292+ /* Count entries */
293+ for (char **d = conf_dirs; d && *d; d++, len++)
294+ ;
295+
296+ nih_assert (len);
297+
298+ /* Use last value specified */
299+ conf_dir = conf_dirs[len-1];
300+
301 NIH_MUST (conf_source_new (NULL, CONFFILE, CONF_FILE));
302
303- if (conf_dir)
304+ nih_debug ("Using configuration directory %s", conf_dir);
305 NIH_MUST (conf_source_new (NULL, conf_dir, CONF_JOB_DIR));
306+ } else {
307+ nih_local char **dirs = NULL;
308
309- if (user_mode) {
310 dirs = NIH_MUST (get_user_upstart_dirs ());
311- for (char **d = dirs; d && *d; d++)
312+
313+ for (char **d = conf_dirs[0] ? conf_dirs : dirs; d && *d; d++) {
314+ nih_debug ("Using configuration directory %s", *d);
315 NIH_MUST (conf_source_new (NULL, *d, CONF_JOB_DIR));
316- nih_free (dirs);
317+ }
318 }
319
320+ nih_free (conf_dirs);
321+
322 job_class_environment_init ();
323
324 conf_reload ();
325@@ -922,31 +942,26 @@
326 /**
327 * handle_confdir:
328 *
329- * Determine where system configuration files should be loaded from.
330+ * Determine where system configuration files should be loaded from
331+ * if not specified on the command-line.
332 **/
333 static void
334 handle_confdir (void)
335 {
336- char *dir;
337+ char *dir;
338+
339+ nih_assert (conf_dirs);
340
341 /* user has already specified directory on command-line */
342- if (conf_dir)
343- goto out;
344+ if (conf_dirs[0])
345+ return;
346
347 if (user_mode)
348 return;
349
350- conf_dir = CONFDIR;
351-
352 dir = getenv (CONFDIR_ENV);
353- if (! dir)
354- return;
355-
356- conf_dir = dir;
357-
358-out:
359- nih_debug ("Using alternate configuration directory %s",
360- conf_dir);
361+
362+ NIH_MUST (nih_str_array_add (&conf_dirs, NULL, NULL, dir ? dir : CONFDIR));
363 }
364
365 /**
366@@ -1001,3 +1016,20 @@
367
368 return 0;
369 }
370+
371+/**
372+ * NihOption setter function to handle selection of configuration file
373+ * directories.
374+ *
375+ * Returns: 0 on success, -1 on invalid console type.
376+ **/
377+static int
378+conf_dir_setter (NihOption *option, const char *arg)
379+{
380+ nih_assert (conf_dirs);
381+ nih_assert (option);
382+
383+ NIH_MUST (nih_str_array_add (&conf_dirs, NULL, NULL, arg));
384+
385+ return 0;
386+}
387
388=== modified file 'init/man/init.8'
389--- init/man/init.8 2013-02-25 09:42:11 +0000
390+++ init/man/init.8 2013-05-30 13:17:28 +0000
391@@ -65,10 +65,13 @@
392 .\"
393 .TP
394 .B \-\-confdir \fIdirectory\fP
395-Read job configuration files from a directory other than
396-\fI/etc/init\fP.
397-For user session mode, read job configuration files from this
398-directory at highest priority.
399+Read job configuration files from a directory other than the default
400+(\fI/etc/init\fP for process ID 1).
401+
402+When running as process ID 1, the last directory specified will be used.
403+
404+In user session mode, multiple directories will be honoured and job
405+configuration files loaded from the directories in the order specified.
406 .\"
407 .TP
408 .B \-\-default-console \fIvalue\fP
409
410=== modified file 'init/state.c'
411--- init/state.c 2013-02-28 16:40:36 +0000
412+++ init/state.c 2013-05-30 13:17:28 +0000
413@@ -19,6 +19,10 @@
414 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
415 */
416
417+#ifdef HAVE_CONFIG_H
418+# include <config.h>
419+#endif /* HAVE_CONFIG_H */
420+
421 #include <string.h>
422 #include <sys/types.h>
423 #include <signal.h>
424
425=== modified file 'init/tests/test_job_process.c'
426--- init/tests/test_job_process.c 2012-12-19 12:16:59 +0000
427+++ init/tests/test_job_process.c 2013-05-30 13:17:28 +0000
428@@ -166,12 +166,6 @@
429 return 1;
430 }
431
432-static int
433-strcmp_compar (const void *a, const void *b)
434-{
435- return strcmp(*(char * const *)a, *(char * const *)b);
436-}
437-
438 static void
439 child (enum child_tests test,
440 const char *filename)
441
442=== added file 'init/tests/test_main.c'
443--- init/tests/test_main.c 1970-01-01 00:00:00 +0000
444+++ init/tests/test_main.c 2013-05-30 13:17:28 +0000
445@@ -0,0 +1,452 @@
446+/* upstart
447+ *
448+ * test_main.c - test suite for init/main.c
449+ *
450+ * Copyright © 2013 Canonical Ltd.
451+ * Author: James Hunt <james.hunt@canonical.com>
452+ *
453+ * This program is free software; you can redistribute it and/or modify
454+ * it under the terms of the GNU General Public License version 2, as
455+ * published by the Free Software Foundation.
456+ *
457+ * This program is distributed in the hope that it will be useful,
458+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
459+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
460+ * GNU General Public License for more details.
461+ *
462+ * You should have received a copy of the GNU General Public License along
463+ * with this program; if not, write to the Free Software Foundation, Inc.,
464+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
465+ */
466+
467+#include <nih/string.h>
468+#include <nih/main.h>
469+#include <nih/test.h>
470+
471+#include <limits.h>
472+#include <stdlib.h>
473+#include <sys/types.h>
474+#include <sys/stat.h>
475+#include <unistd.h>
476+
477+#include "conf.h"
478+#include "job_class.h"
479+#include "job.h"
480+#include "xdg.h"
481+
482+#include "test_util.h"
483+
484+
485+void
486+test_confdir (void)
487+{
488+ char confdir_a[PATH_MAX];
489+ char confdir_b[PATH_MAX];
490+ char xdg_config_home[PATH_MAX];
491+ char xdg_runtime_dir[PATH_MAX];
492+ char logdir[PATH_MAX];
493+ pid_t upstart_pid = 0;
494+ pid_t dbus_pid = 0;
495+ char **output;
496+ size_t lines;
497+ nih_local char *cmd = NULL;
498+ nih_local char *orig_xdg_config_home = NULL;
499+ nih_local char *orig_xdg_runtime_dir = NULL;
500+ nih_local char *xdg_conf_dir = NULL;
501+ nih_local char *session_file = NULL;
502+ nih_local char *path = NULL;
503+
504+ /* space for 2 sets of confdir options and a terminator */
505+ char *extra[5];
506+
507+ TEST_GROUP ("--confdir command-line option handling");
508+
509+ TEST_FILENAME (confdir_a);
510+ assert0 (mkdir (confdir_a, 0755));
511+
512+ TEST_FILENAME (confdir_b);
513+ assert0 (mkdir (confdir_b, 0755));
514+
515+ TEST_FILENAME (xdg_config_home);
516+ assert0 (mkdir (xdg_config_home, 0755));
517+
518+ TEST_FILENAME (xdg_runtime_dir);
519+ assert0 (mkdir (xdg_runtime_dir, 0755));
520+
521+ xdg_conf_dir = nih_sprintf (NULL, "%s/%s", xdg_config_home, "upstart");
522+ TEST_NE_P (xdg_conf_dir, NULL);
523+ assert0 (mkdir (xdg_conf_dir, 0755));
524+
525+ TEST_FILENAME (logdir);
526+ assert0 (mkdir (logdir, 0755));
527+
528+ /* Take care to avoid disrupting users environment by saving and
529+ * restoring these variable (assuming the tests all pass...).
530+ */
531+ orig_xdg_config_home = getenv ("XDG_CONFIG_HOME");
532+ if (orig_xdg_config_home)
533+ orig_xdg_config_home = NIH_MUST (nih_strdup (NULL, orig_xdg_config_home));
534+
535+ assert0 (setenv ("XDG_CONFIG_HOME", xdg_config_home, 1));
536+
537+ orig_xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
538+ if (orig_xdg_runtime_dir)
539+ orig_xdg_runtime_dir = NIH_MUST (nih_strdup (NULL, orig_xdg_runtime_dir));
540+
541+ assert0 (setenv ("XDG_RUNTIME_DIR", xdg_runtime_dir, 1));
542+
543+ /* disable system default job dir */
544+ assert0 (setenv ("UPSTART_NO_SYSTEM_USERCONFDIR", "1", 1));
545+
546+ TEST_DBUS (dbus_pid);
547+
548+ /************************************************************/
549+ TEST_FEATURE ("Session Init without --confdir");
550+
551+ CREATE_FILE (xdg_conf_dir, "foo.conf", "exec true");
552+ CREATE_FILE (xdg_conf_dir, "bar.conf", "exec true");
553+ CREATE_FILE (xdg_conf_dir, "baz.conf", "exec true");
554+
555+ start_upstart_common (&upstart_pid, TRUE, NULL, logdir, NULL);
556+
557+ /* Should be running */
558+ assert0 (kill (upstart_pid, 0));
559+
560+ session_file = get_session_file (xdg_runtime_dir, upstart_pid);
561+
562+ cmd = nih_sprintf (NULL, "%s list 2>&1", get_initctl ());
563+ TEST_NE_P (cmd, NULL);
564+ RUN_COMMAND (NULL, cmd, &output, &lines);
565+
566+ qsort (output, lines, sizeof (output[0]), strcmp_compar);
567+
568+ TEST_EQ (lines, 3);
569+ TEST_STR_MATCH (output[0], "bar stop/waiting");
570+ TEST_STR_MATCH (output[1], "baz stop/waiting");
571+ TEST_STR_MATCH (output[2], "foo stop/waiting");
572+ nih_free (output);
573+
574+ DELETE_FILE (xdg_conf_dir, "foo.conf");
575+ DELETE_FILE (xdg_conf_dir, "bar.conf");
576+ DELETE_FILE (xdg_conf_dir, "baz.conf");
577+
578+ STOP_UPSTART (upstart_pid);
579+ assert0 (unlink (session_file));
580+
581+ /************************************************************/
582+ TEST_FEATURE ("Session Init with --confdir");
583+
584+ CREATE_FILE (xdg_conf_dir, "xdg_dir_job.conf", "exec true");
585+ CREATE_FILE (confdir_a, "conf_dir_job.conf", "exec true");
586+
587+ start_upstart_common (&upstart_pid, TRUE, confdir_a, logdir, NULL);
588+
589+ /* Should be running */
590+ assert0 (kill (upstart_pid, 0));
591+
592+ session_file = get_session_file (xdg_runtime_dir, upstart_pid);
593+
594+ cmd = nih_sprintf (NULL, "%s list 2>&1", get_initctl ());
595+ TEST_NE_P (cmd, NULL);
596+ RUN_COMMAND (NULL, cmd, &output, &lines);
597+
598+ qsort (output, lines, sizeof (output[0]), strcmp_compar);
599+
600+ /* We expect jobs in xdg_conf_dir to be ignored */
601+ TEST_EQ (lines, 1);
602+ TEST_STR_MATCH (output[0], "conf_dir_job stop/waiting");
603+ nih_free (output);
604+
605+ DELETE_FILE (xdg_conf_dir, "xdg_dir_job.conf");
606+ DELETE_FILE (confdir_a, "conf_dir_job.conf");
607+
608+ STOP_UPSTART (upstart_pid);
609+ assert0 (unlink (session_file));
610+
611+ /************************************************************/
612+ TEST_FEATURE ("Session Init with multiple --confdir");
613+
614+ CREATE_FILE (xdg_conf_dir, "xdg_dir_job.conf", "exec true");
615+ CREATE_FILE (confdir_a, "conf_dir_a_job.conf", "exec true");
616+ CREATE_FILE (confdir_b, "conf_dir_b_job.conf", "exec true");
617+
618+ extra[0] = "--confdir";
619+ extra[1] = confdir_a;
620+ extra[2] = "--confdir";
621+ extra[3] = confdir_b;
622+ extra[4] = NULL;
623+
624+ /* pass 2 confdir directories */
625+ start_upstart_common (&upstart_pid, TRUE, NULL, logdir, extra);
626+
627+ /* Should be running */
628+ assert0 (kill (upstart_pid, 0));
629+
630+ session_file = get_session_file (xdg_runtime_dir, upstart_pid);
631+
632+ cmd = nih_sprintf (NULL, "%s list 2>&1", get_initctl ());
633+ TEST_NE_P (cmd, NULL);
634+ RUN_COMMAND (NULL, cmd, &output, &lines);
635+
636+ qsort (output, lines, sizeof (output[0]), strcmp_compar);
637+
638+ /* We expect jobs in xdg_conf_dir to be ignored */
639+ TEST_EQ (lines, 2);
640+ TEST_STR_MATCH (output[0], "conf_dir_a_job stop/waiting");
641+ TEST_STR_MATCH (output[1], "conf_dir_b_job stop/waiting");
642+ nih_free (output);
643+
644+ DELETE_FILE (xdg_conf_dir, "xdg_dir_job.conf");
645+ DELETE_FILE (confdir_a, "conf_dir_a_job.conf");
646+ DELETE_FILE (confdir_b, "conf_dir_b_job.conf");
647+
648+ STOP_UPSTART (upstart_pid);
649+ assert0 (unlink (session_file));
650+
651+ /************************************************************/
652+ TEST_FEATURE ("Session Init with multiple --confdir and conflicting names");
653+
654+ CREATE_FILE (xdg_conf_dir, "conflict.conf", "emits xdg_conf_dir");
655+ CREATE_FILE (confdir_a, "conflict.conf", "emits confdir_a");
656+ CREATE_FILE (confdir_b, "foo.conf", "exec true");
657+
658+ extra[0] = "--confdir";
659+ extra[1] = confdir_a;
660+ extra[2] = "--confdir";
661+ extra[3] = confdir_b;
662+ extra[4] = NULL;
663+
664+ /* pass 2 confdir directories */
665+ start_upstart_common (&upstart_pid, TRUE, NULL, logdir, extra);
666+
667+ /* Should be running */
668+ assert0 (kill (upstart_pid, 0));
669+
670+ session_file = get_session_file (xdg_runtime_dir, upstart_pid);
671+
672+ cmd = nih_sprintf (NULL, "%s list 2>&1", get_initctl ());
673+ TEST_NE_P (cmd, NULL);
674+ RUN_COMMAND (NULL, cmd, &output, &lines);
675+
676+ qsort (output, lines, sizeof (output[0]), strcmp_compar);
677+
678+ /* We expect jobs in xdg_conf_dir to be ignored */
679+ TEST_EQ (lines, 2);
680+ TEST_STR_MATCH (output[0], "conflict stop/waiting");
681+ TEST_STR_MATCH (output[1], "foo stop/waiting");
682+ nih_free (output);
683+
684+ cmd = nih_sprintf (NULL, "%s show-config %s 2>&1", get_initctl (), "conflict");
685+ TEST_NE_P (cmd, NULL);
686+ RUN_COMMAND (NULL, cmd, &output, &lines);
687+
688+ /* Ensure the correct version of the conflict job is found */
689+ TEST_EQ (lines, 2);
690+ TEST_STR_MATCH (output[0], "conflict");
691+ TEST_STR_MATCH (output[1], " emits confdir_a");
692+ nih_free (output);
693+
694+ DELETE_FILE (xdg_conf_dir, "conflict.conf");
695+ DELETE_FILE (confdir_a, "conflict.conf");
696+ DELETE_FILE (confdir_b, "foo.conf");
697+
698+ STOP_UPSTART (upstart_pid);
699+ assert0 (unlink (session_file));
700+
701+ /************************************************************/
702+ TEST_FEATURE ("System Init without --confdir");
703+
704+ /* Use the "secret" interface */
705+ assert0 (setenv ("UPSTART_CONFDIR", confdir_a, 1));
706+
707+ CREATE_FILE (confdir_a, "foo.conf", "exec true");
708+ CREATE_FILE (confdir_a, "bar.conf", "exec true");
709+ CREATE_FILE (confdir_a, "baz.conf", "exec true");
710+
711+ /* Disable user mode */
712+ test_user_mode = FALSE;
713+
714+ start_upstart_common (&upstart_pid, FALSE, NULL, logdir, NULL);
715+
716+ /* Should be running */
717+ assert0 (kill (upstart_pid, 0));
718+
719+ cmd = nih_sprintf (NULL, "%s list 2>&1", get_initctl ());
720+ TEST_NE_P (cmd, NULL);
721+ RUN_COMMAND (NULL, cmd, &output, &lines);
722+
723+ qsort (output, lines, sizeof (output[0]), strcmp_compar);
724+
725+ TEST_EQ (lines, 3);
726+ TEST_STR_MATCH (output[0], "bar stop/waiting");
727+ TEST_STR_MATCH (output[1], "baz stop/waiting");
728+ TEST_STR_MATCH (output[2], "foo stop/waiting");
729+ nih_free (output);
730+
731+ DELETE_FILE (confdir_a, "foo.conf");
732+ DELETE_FILE (confdir_a, "bar.conf");
733+ DELETE_FILE (confdir_a, "baz.conf");
734+
735+ STOP_UPSTART (upstart_pid);
736+
737+ /************************************************************/
738+ TEST_FEATURE ("System Init with --confdir");
739+
740+ CREATE_FILE (confdir_a, "foo.conf", "exec true");
741+ CREATE_FILE (confdir_a, "bar.conf", "exec true");
742+ CREATE_FILE (confdir_b, "baz.conf", "exec true");
743+
744+ start_upstart_common (&upstart_pid, FALSE, confdir_b, logdir, NULL);
745+
746+ /* Should be running */
747+ assert0 (kill (upstart_pid, 0));
748+
749+ cmd = nih_sprintf (NULL, "%s list 2>&1", get_initctl ());
750+ TEST_NE_P (cmd, NULL);
751+ RUN_COMMAND (NULL, cmd, &output, &lines);
752+
753+ qsort (output, lines, sizeof (output[0]), strcmp_compar);
754+
755+ TEST_EQ (lines, 1);
756+ TEST_STR_MATCH (output[0], "baz stop/waiting");
757+ nih_free (output);
758+
759+ DELETE_FILE (confdir_a, "foo.conf");
760+ DELETE_FILE (confdir_a, "bar.conf");
761+ DELETE_FILE (confdir_b, "baz.conf");
762+
763+ STOP_UPSTART (upstart_pid);
764+
765+ /************************************************************/
766+ TEST_FEATURE ("System Init with multiple --confdir");
767+
768+ assert0 (setenv ("UPSTART_CONFDIR", xdg_conf_dir, 1));
769+
770+ CREATE_FILE (xdg_conf_dir, "foo.conf", "exec true");
771+ CREATE_FILE (confdir_a, "bar.conf", "exec true");
772+ CREATE_FILE (confdir_b, "baz.conf", "exec true");
773+ CREATE_FILE (confdir_b, "qux.conf", "exec true");
774+
775+ extra[0] = "--confdir";
776+ extra[1] = confdir_a;
777+ extra[2] = "--confdir";
778+ extra[3] = confdir_b;
779+ extra[4] = NULL;
780+
781+ start_upstart_common (&upstart_pid, FALSE, NULL, logdir, extra);
782+
783+ /* Should be running */
784+ assert0 (kill (upstart_pid, 0));
785+
786+ cmd = nih_sprintf (NULL, "%s list 2>&1", get_initctl ());
787+ TEST_NE_P (cmd, NULL);
788+ RUN_COMMAND (NULL, cmd, &output, &lines);
789+
790+ qsort (output, lines, sizeof (output[0]), strcmp_compar);
791+
792+ TEST_EQ (lines, 2);
793+ /* XXX: Only the last instance of --confdir should be honoured.
794+ *
795+ * This behaviour deviates from running as a Session Init where *all*
796+ * --confdir's specified are used.
797+ */
798+ TEST_STR_MATCH (output[0], "baz stop/waiting");
799+ TEST_STR_MATCH (output[1], "qux stop/waiting");
800+ nih_free (output);
801+
802+ DELETE_FILE (xdg_conf_dir, "foo.conf");
803+ DELETE_FILE (confdir_a, "bar.conf");
804+ DELETE_FILE (confdir_b, "baz.conf");
805+ DELETE_FILE (confdir_b, "qux.conf");
806+
807+ STOP_UPSTART (upstart_pid);
808+
809+ /************************************************************/
810+ TEST_FEATURE ("System Init with multiple --confdir and conflicting names");
811+
812+ assert0 (setenv ("UPSTART_CONFDIR", xdg_conf_dir, 1));
813+
814+ CREATE_FILE (xdg_conf_dir, "conflict.conf", "emits xdg_conf_dir");
815+ CREATE_FILE (confdir_a, "conflict.conf", "emits confdir_a");
816+ CREATE_FILE (confdir_b, "conflict.conf", "emits confdir_b");
817+
818+ extra[0] = "--confdir";
819+ extra[1] = confdir_a;
820+ extra[2] = "--confdir";
821+ extra[3] = confdir_b;
822+ extra[4] = NULL;
823+
824+ start_upstart_common (&upstart_pid, FALSE, NULL, logdir, extra);
825+
826+ /* Should be running */
827+ assert0 (kill (upstart_pid, 0));
828+
829+ cmd = nih_sprintf (NULL, "%s list 2>&1", get_initctl ());
830+ TEST_NE_P (cmd, NULL);
831+ RUN_COMMAND (NULL, cmd, &output, &lines);
832+
833+ qsort (output, lines, sizeof (output[0]), strcmp_compar);
834+
835+ TEST_EQ (lines, 1);
836+ /* only the last instance of --confdir should be honoured */
837+ TEST_STR_MATCH (output[0], "conflict stop/waiting");
838+ nih_free (output);
839+
840+ cmd = nih_sprintf (NULL, "%s show-config %s 2>&1", get_initctl (), "conflict");
841+ TEST_NE_P (cmd, NULL);
842+ RUN_COMMAND (NULL, cmd, &output, &lines);
843+
844+ /* Ensure the correct version of the conflict job is found */
845+ TEST_EQ (lines, 2);
846+ TEST_STR_MATCH (output[0], "conflict");
847+ TEST_STR_MATCH (output[1], " emits confdir_b");
848+ nih_free (output);
849+
850+ DELETE_FILE (xdg_conf_dir, "conflict.conf");
851+ DELETE_FILE (confdir_a, "conflict.conf");
852+ DELETE_FILE (confdir_b, "conflict.conf");
853+
854+ STOP_UPSTART (upstart_pid);
855+
856+ /************************************************************/
857+
858+ TEST_DBUS_END (dbus_pid);
859+
860+ if (orig_xdg_config_home) {
861+ /* restore */
862+ setenv ("XDG_CONFIG_HOME", orig_xdg_config_home, 1);
863+ } else {
864+ assert0 (unsetenv ("XDG_CONFIG_HOME"));
865+ }
866+
867+ if (orig_xdg_runtime_dir) {
868+ /* restore */
869+ setenv ("XDG_RUNTIME_DIR", orig_xdg_runtime_dir, 1);
870+ } else {
871+ assert0 (unsetenv ("XDG_RUNTIME_DIR"));
872+ }
873+
874+ assert0 (rmdir (confdir_a));
875+ assert0 (rmdir (confdir_b));
876+ assert0 (rmdir (xdg_conf_dir));
877+ assert0 (rmdir (xdg_config_home));
878+
879+ /* Remove the directory tree the first Session Init created */
880+ path = NIH_MUST (nih_sprintf (NULL, "%s/upstart/sessions", xdg_runtime_dir));
881+ TEST_EQ (rmdir (path), 0);
882+ path = NIH_MUST (nih_sprintf (NULL, "%s/upstart", xdg_runtime_dir));
883+ TEST_EQ (rmdir (path), 0);
884+ assert0 (rmdir (xdg_runtime_dir));
885+
886+ assert0 (rmdir (logdir));
887+ assert0 (unsetenv ("UPSTART_CONFDIR"));
888+}
889+
890+int
891+main (int argc,
892+ char *argv[])
893+{
894+ test_confdir ();
895+
896+ return 0;
897+}
898
899=== removed file 'init/tests/test_util.c'
900--- init/tests/test_util.c 2012-11-14 14:47:19 +0000
901+++ init/tests/test_util.c 1970-01-01 00:00:00 +0000
902@@ -1,27 +0,0 @@
903-#include "test_util.h"
904-
905-/**
906- * string_check:
907- *
908- * @a: first string,
909- * @b: second string.
910- *
911- * Compare @a and @b either or both of which may be NULL.
912- *
913- * Returns 0 if strings are identical or both NULL, else 1.
914- **/
915-int
916-string_check (const char *a, const char *b)
917-{
918- if (!a && !b)
919- return 0;
920-
921- if (!a || !b)
922- return 1;
923-
924- if (strcmp (a, b))
925- return 1;
926-
927- return 0;
928-}
929-
930
931=== removed file 'init/tests/test_util.h'
932--- init/tests/test_util.h 2012-11-07 10:40:40 +0000
933+++ init/tests/test_util.h 1970-01-01 00:00:00 +0000
934@@ -1,304 +0,0 @@
935-#ifndef TEST_UTIL_H
936-#define TEST_UTIL_H
937-
938-#include <string.h>
939-
940-/* TEST_ENSURE_CLEAN_ENV:
941- *
942- * Ensure the environment is as pristine as possible (to avoid follow-on
943- * errors caused by not freeing objects in a previous test, say)
944- */
945-#define TEST_ENSURE_CLEAN_ENV() \
946-{ \
947- setvbuf(stdout, NULL, _IONBF, 0); \
948- \
949- if (job_classes) { \
950- TEST_HASH_EMPTY (job_classes); \
951- } \
952- \
953- if (conf_sources) { \
954- TEST_LIST_EMPTY (conf_sources); \
955- } \
956- \
957- if (nih_io_watches) { \
958- TEST_LIST_EMPTY (nih_io_watches); \
959- } \
960- \
961- if (nih_timers) { \
962- TEST_LIST_EMPTY (nih_timers); \
963- } \
964- \
965- if (events) { \
966- TEST_LIST_EMPTY (events); \
967- } \
968-}
969-
970-/**
971- * _TEST_WATCH_UPDATE:
972- * @force: if TRUE, force an update,
973- * @timeout: struct timeval pointer, or NULL if no timeout required.
974- *
975- * Request NIH look for a file event relating to any NihIo objects,
976- * with an optional timeout. Behaviour can be forced via @force.
977- **/
978-#define _TEST_WATCH_UPDATE(force, timeout) \
979-{ \
980- int nfds = 0; \
981- int ret = 0; \
982- fd_set readfds, writefds, exceptfds; \
983- \
984- FD_ZERO (&readfds); \
985- FD_ZERO (&writefds); \
986- FD_ZERO (&exceptfds); \
987- \
988- nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); \
989- if (! force) { \
990- ret = select (nfds, &readfds, &writefds, \
991- &exceptfds, timeout); \
992- } \
993- if (force || ret > 0) \
994- nih_io_handle_fds (&readfds, &writefds, &exceptfds); \
995-}
996-
997-/**
998- * TEST_WATCH_UPDATE:
999- *
1000- * Request NIH look for a file event relating to any NihIo objects,
1001- * */
1002-#define TEST_WATCH_UPDATE() \
1003- _TEST_WATCH_UPDATE (0, NULL)
1004-
1005-/**
1006- * TEST_WATCH_UPDATE_TIMEOUT:
1007- * @timeout: struct timeval pointer.
1008- *
1009- * Request NIH look for a file event relating to any NihIo objects
1010- * within time period @timeout.
1011- **/
1012-#define TEST_WATCH_UPDATE_TIMEOUT(timeout) \
1013- _TEST_WATCH_UPDATE (0, timeout)
1014-
1015-/**
1016- * TEST_WATCH_UPDATE_TIMEOUT_SECS:
1017- * @secs: seconds to wait before timeout.
1018- *
1019- * Request NIH look for a file event relating to any NihIo objects
1020- * within @secs timeout.
1021- **/
1022-#define TEST_WATCH_UPDATE_TIMEOUT_SECS(secs) \
1023-{ \
1024- struct timeval _t; \
1025- _t.tv_sec = secs; \
1026- _t.tv_usec = 0; \
1027- _TEST_WATCH_UPDATE (0, &_t); \
1028-}
1029-
1030-/**
1031- * TEST_FORCE_WATCH_UPDATE:
1032- *
1033- * Force NIH to look for a file event relating to any NihIo objects.
1034- **/
1035-#define TEST_FORCE_WATCH_UPDATE() \
1036- _TEST_WATCH_UPDATE (1, NULL)
1037-
1038-/**
1039- * TEST_FORCE_WATCH_UPDATE_TIMEOUT:
1040- * @timeout: struct timeval pointer.
1041- *
1042- * Force NIH to look for a file event relating to any NihIo objects
1043- * within time period @timeout.
1044- **/
1045-#define TEST_FORCE_WATCH_UPDATE_TIMEOUT(timeout) \
1046- _TEST_WATCH_UPDATE (1, timeout)
1047-
1048-/**
1049- * TEST_FORCE_WATCH_UPDATE_TIMEOUT_SECS:
1050- * @timeout: struct timeval pointer.
1051- *
1052- * Force NIH to look for a file event relating to any NihIo objects
1053- * within time period @timeout.
1054- **/
1055-#define TEST_FORCE_WATCH_UPDATE_TIMEOUT_SECS(secs) \
1056-{ \
1057- struct timeval _t; \
1058- _t.tv_sec = secs; \
1059- _t.tv_usec = 0; \
1060- _TEST_WATCH_UPDATE (1, &_t); \
1061-}
1062-
1063-/**
1064- * ENSURE_DIRECTORY_EMPTY:
1065- * @path: Full path to a directory.
1066- *
1067- * Ensure specified directory is empty.
1068- **/
1069-#define ENSURE_DIRECTORY_EMPTY(path) \
1070-{ \
1071- DIR *dp = NULL; \
1072- struct dirent *file = NULL; \
1073- int count = 0; \
1074- \
1075- dp = opendir (path); \
1076- TEST_NE_P (dp, NULL); \
1077- \
1078- while((file = readdir (dp))) { \
1079- if (!strcmp (".", file->d_name) || \
1080- !strcmp ("..", file->d_name)) \
1081- continue; \
1082- count++; \
1083- } \
1084- \
1085- closedir (dp); \
1086- \
1087- TEST_EQ (count, 0); \
1088-}
1089-
1090-/**
1091- * obj_string_check:
1092- *
1093- * @a: first object,
1094- * @b: second object,
1095- * @name: name of string element.
1096- *
1097- * Compare string element @name in objects @a and @b.
1098- *
1099- * Returns: 0 if strings are identical
1100- * (or both NULL), else 1.
1101- **/
1102-#define obj_string_check(a, b, name) \
1103- string_check ((a)->name, (b)->name)
1104-
1105-/**
1106- * obj_num_check:
1107- *
1108- * @a: first object,
1109- * @b: second object.
1110- * @name: name of numeric element.
1111- *
1112- * Compare numeric element @name in objects @a and @b.
1113- *
1114- * Returns: 0 if @a and @b are identical, else 1.
1115- **/
1116-#define obj_num_check(a, b, name) \
1117- (a->name != b->name)
1118-
1119-/**
1120- * TEST_CMP_INT_ARRAYS:
1121- * @a: first array,
1122- * @b: second array,
1123- * @sizea: size of @a,
1124- * @sizeb: size of @b.
1125- *
1126- * Compare integer arrays @a and @b for equivalence.
1127- *
1128- * Returns: 0 if arrays are identical, else -1.
1129- **/
1130-#define TEST_CMP_INT_ARRAYS(a, b, sizea, sizeb) \
1131-({int ret = 0; \
1132- size_t __i; \
1133- if (sizea == sizeb) { \
1134- for (__i = 0; \
1135- __i < sizea; \
1136- __i++) { \
1137- if ((a)[__i] != (b)[__i]) { \
1138- ret = -1; \
1139- break; \
1140- } \
1141- } \
1142- } else \
1143- ret = -1; \
1144- ret;})
1145-
1146-/**
1147- * TEST_CMP_STR_ARRAYS:
1148- * @a: first string array,
1149- * @b: second string array,
1150- * @sizea: length of @a,
1151- * @sizeb: length of @b.
1152- *
1153- * Compare string arrays @a and @b for equivalence.
1154- *
1155- * Returns: 0 if arrays are identical, else -1.
1156- **/
1157-#define TEST_CMP_STR_ARRAYS(a, b, sizea, sizeb) \
1158-({ int ret = 0; \
1159- if (sizea == sizeb) { \
1160- for (size_t __i = 0; \
1161- __i < sizea; \
1162- __i++) { \
1163- if (strcmp (a[__i], b[__i])) { \
1164- ret = -1; \
1165- break; \
1166- } \
1167- } \
1168- } else \
1169- ret = -1; \
1170- ret;})
1171-
1172-/**
1173- * TEST_TWO_LISTS_FOREACH:
1174- * @list1: entry in the first list to iterate,
1175- * @list2: entry in the second list to iterate,
1176- * @iter1: name of iterator variable for @list1,
1177- * @iter2: name of iterator variable for @list2.
1178- *
1179- * Dual version of NIH_LIST_FOREACH() which iterates
1180- * two lists in tandem.
1181- **/
1182-#define TEST_TWO_LISTS_FOREACH(list1, list2, iter1, iter2) \
1183- for (NihList *iter1 = (list1)->next, \
1184- *iter2 = (list2)->next; \
1185- iter1 != (list1) && iter2 != (list2); \
1186- iter1 = iter1->next, \
1187- iter2 = iter2->next)
1188-
1189-/**
1190- * TEST_TWO_HASHES_FOREACH:
1191- * @hash1: entry in the first hash to iterate,
1192- * @hash2: entry in the second hash to iterate,
1193- * @iter1: name of iterator variable for @hash1,
1194- * @iter2: name of iterator variable for @hash2.
1195- *
1196- * Dual version of NIH_HASH_FOREACH() which iterates
1197- * two hashes in tandem.
1198- **/
1199-#define TEST_TWO_HASHES_FOREACH(hash1, hash2, iter1, iter2) \
1200- for (size_t _##iter##_i = 0; _##iter##_i < (hash1)->size; \
1201- _##iter##_i++) \
1202- TEST_TWO_LISTS_FOREACH (&(hash1)->bins[_##iter##_i], \
1203- &(hash2)->bins[_##iter##_i], \
1204- iter1, iter2)
1205-
1206-/**
1207- * TEST_TWO_TREES_FOREACH:
1208- * @tree1: root of the first tree to iterate,
1209- * @tree2: root of the second tree to iterate,
1210- * @iter1: name of iterator variable for @tree1,
1211- * @iter2: name of iterator variable for @tree2.
1212- *
1213- * Dual version of NIH_TREE_FOREACH() which walks
1214- * two trees in tandem.
1215- **/
1216-#define TEST_TWO_TREES_FOREACH(tree1, tree2, iter1, iter2) \
1217- for (NihTree *iter1 = nih_tree_next (tree1, NULL), \
1218- *iter2 = nih_tree_next (tree2, NULL); \
1219- iter1 != NULL && iter2 != NULL; \
1220- iter1 = nih_tree_next (tree1, iter1), \
1221- iter2 = nih_tree_next (tree2, iter2))
1222-
1223-
1224-/**
1225- * TEST_ARRAY_SIZE:
1226- * @array: array.
1227- *
1228- * Determine size of specified array.
1229- *
1230- * Returns: array size.
1231- **/
1232-#define TEST_ARRAY_SIZE(array) \
1233- (sizeof (array) / sizeof (array[0]))
1234-
1235-/* Prototypes */
1236-extern int string_check (const char *a, const char *b);
1237-
1238-#endif /* TEST_UTIL_H */
1239
1240=== modified file 'init/xdg.c'
1241--- init/xdg.c 2013-01-30 16:39:11 +0000
1242+++ init/xdg.c 2013-05-30 13:17:28 +0000
1243@@ -319,8 +319,10 @@
1244 dirs = NULL;
1245
1246 /* System's read-only location */
1247- if (! nih_str_array_add (&all_dirs, NULL, NULL, SYSTEM_USERCONFDIR))
1248- goto error;
1249+ if (! getenv ("UPSTART_NO_SYSTEM_USERCONFDIR")) {
1250+ if (! nih_str_array_add (&all_dirs, NULL, NULL, SYSTEM_USERCONFDIR))
1251+ goto error;
1252+ }
1253
1254 return all_dirs;
1255
1256
1257=== added directory 'test'
1258=== added file 'test/Makefile.am'
1259--- test/Makefile.am 1970-01-01 00:00:00 +0000
1260+++ test/Makefile.am 2013-05-30 13:17:28 +0000
1261@@ -0,0 +1,21 @@
1262+## Process this file with automake to produce Makefile.in
1263+
1264+INITCTL_BINARY = $(abs_top_builddir)/util/initctl
1265+UPSTART_BINARY = $(abs_top_builddir)/init/init
1266+
1267+AM_CFLAGS = \
1268+ $(NIH_CFLAGS) \
1269+ $(NIH_DBUS_CFLAGS) \
1270+ $(DBUS_CFLAGS)
1271+
1272+AM_CPPFLAGS = \
1273+ -DLOCALEDIR="\"$(localedir)\"" \
1274+ -DINITCTL_BUILD \
1275+ -DSBINDIR="\"$(sbindir)\"" \
1276+ -DUPSTART_BINARY="\"$(UPSTART_BINARY)\"" \
1277+ -DINITCTL_BINARY="\"$(INITCTL_BINARY)\"" \
1278+ -I$(top_builddir) -I$(top_srcdir) -iquote$(builddir) -iquote$(srcdir) \
1279+ -I$(top_srcdir)/intl
1280+
1281+check_LIBRARIES = libtest_util.a
1282+libtest_util_a_SOURCES = test_util.c test_util.h
1283
1284=== added file 'test/test_util.c'
1285--- test/test_util.c 1970-01-01 00:00:00 +0000
1286+++ test/test_util.c 2013-05-30 13:17:28 +0000
1287@@ -0,0 +1,628 @@
1288+/* upstart
1289+ *
1290+ * test_util.c - test utilities
1291+ * associated serialisation and deserialisation routines.
1292+ *
1293+ * Copyright © 2012 Canonical Ltd.
1294+ * Author: James Hunt <james.hunt@canonical.com>
1295+ *
1296+ * This program is free software; you can redistribute it and/or modify
1297+ * it under the terms of the GNU General Public License version 2, as
1298+ * published by the Free Software Foundation.
1299+ *
1300+ * This program is distributed in the hope that it will be useful,
1301+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1302+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1303+ * GNU General Public License for more details.
1304+ *
1305+ * You should have received a copy of the GNU General Public License along
1306+ * with this program; if not, write to the Free Software Foundation, Inc.,
1307+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1308+ */
1309+
1310+#ifdef HAVE_CONFIG_H
1311+# include <config.h>
1312+#endif /* HAVE_CONFIG_H */
1313+
1314+#include <nih/test.h>
1315+#include <nih/file.h>
1316+#include <nih/string.h>
1317+#include <nih/signal.h>
1318+#include <nih/logging.h>
1319+#include <nih-dbus/test_dbus.h>
1320+
1321+#include <dbus/dbus.h>
1322+
1323+#include <stdio.h>
1324+#include <signal.h>
1325+#include <unistd.h>
1326+#include <regex.h>
1327+#include <sys/types.h>
1328+#include <sys/stat.h>
1329+#include <ctype.h>
1330+
1331+#include <nih-dbus/dbus_error.h>
1332+#include <nih-dbus/dbus_connection.h>
1333+#include <nih-dbus/dbus_proxy.h>
1334+#include <nih-dbus/errors.h>
1335+
1336+#include "dbus/upstart.h"
1337+
1338+#include "test_util.h"
1339+
1340+#ifndef UPSTART_BINARY
1341+#error unable to find init binary as UPSTART_BINARY not defined
1342+#endif /* UPSTART_BINARY */
1343+
1344+#ifndef INITCTL_BINARY
1345+#error unable to find initctl binary as INITCTL_BINARY not defined
1346+#endif /* INITCTL_BINARY */
1347+
1348+static void selfpipe_write (int n);
1349+static void selfpipe_setup (void);
1350+
1351+/**
1352+ * wait_for_upstart:
1353+ *
1354+ * @user: TRUE if waiting for a Session Init (which uses a private bus
1355+ * rather than the session bus), else FALSE.
1356+ *
1357+ * Wait for Upstart to appear on D-Bus denoting its completion of
1358+ * initialisation. Wait time is somewhat arbitrary (but more
1359+ * than adequate!).
1360+ **/
1361+void
1362+wait_for_upstart (int user)
1363+{
1364+ nih_local NihDBusProxy *upstart = NULL;
1365+ DBusConnection *connection;
1366+ char *address;
1367+ NihError *err;
1368+ int running = FALSE;
1369+
1370+ /* XXX: arbitrary value */
1371+ int attempts = 10;
1372+
1373+ if (user) {
1374+ TEST_TRUE (set_upstart_session ());
1375+ address = getenv ("UPSTART_SESSION");
1376+ } else {
1377+ address = getenv ("DBUS_SESSION_BUS_ADDRESS");
1378+ }
1379+
1380+ TEST_TRUE (address);
1381+
1382+ while (attempts) {
1383+ attempts--;
1384+ sleep (1);
1385+ connection = nih_dbus_connect (address, NULL);
1386+
1387+ if (! connection) {
1388+ err = nih_error_get ();
1389+ nih_free (err);
1390+ continue;
1391+ }
1392+
1393+ upstart = nih_dbus_proxy_new (NULL, connection,
1394+ NULL,
1395+ DBUS_PATH_UPSTART,
1396+ NULL, NULL);
1397+
1398+ if (! upstart) {
1399+ err = nih_error_get ();
1400+ nih_free (err);
1401+ dbus_connection_unref (connection);
1402+ } else {
1403+ running = TRUE;
1404+ break;
1405+ }
1406+ }
1407+ TEST_EQ (running, TRUE);
1408+}
1409+
1410+/* TRUE to denote that Upstart is running in user session mode
1411+ * (FALSE to denote it's using the users D-Bus session bus).
1412+ */
1413+int test_user_mode = FALSE;
1414+
1415+/**
1416+ * set_upstart_session:
1417+ *
1418+ * Attempt to "enter" an Upstart session by setting UPSTART_SESSION to
1419+ * the value of the currently running session.
1420+ *
1421+ * It is only legitimate to call this function if you have previously
1422+ * started a Session Init process.
1423+ *
1424+ * Limitations: Assumes that at most 1 session is running.
1425+ *
1426+ * Returns: TRUE if it was possible to enter the currently running
1427+ * Upstart session, else FALSE.
1428+ **/
1429+int
1430+set_upstart_session (void)
1431+{
1432+ char *value;
1433+ nih_local char *cmd = NULL;
1434+ nih_local char **output = NULL;
1435+ size_t lines = 0;
1436+ int got = FALSE;
1437+ int i;
1438+
1439+ /* XXX: arbitrary value */
1440+ int loops = 5;
1441+
1442+ /* list-sessions relies on this */
1443+ if (! getenv ("XDG_RUNTIME_DIR"))
1444+ return FALSE;
1445+
1446+ cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
1447+ TEST_NE_P (cmd, NULL);
1448+
1449+ /* We expect the list-sessions command to return a valid session
1450+ * within a reasonable period of time.
1451+ */
1452+ for (i = 0; i < loops; i++) {
1453+ sleep (1);
1454+
1455+ RUN_COMMAND (NULL, cmd, &output, &lines);
1456+ if (lines != 1)
1457+ continue;
1458+
1459+ /* No pid in output */
1460+ if (! isdigit(output[0][0]))
1461+ continue;
1462+
1463+ /* look for separator between pid and value of
1464+ * UPSTART_SESSION.
1465+ */
1466+ value = strstr (output[0], " ");
1467+ if (! value)
1468+ continue;
1469+
1470+ /* jump over space */
1471+ value += 1;
1472+ if (! value)
1473+ continue;
1474+
1475+ /* No socket address */
1476+ if (strstr (value, "unix:abstract") == value) {
1477+ got = TRUE;
1478+ break;
1479+ }
1480+ }
1481+
1482+ if (got != TRUE)
1483+ return FALSE;
1484+
1485+ assert0 (setenv ("UPSTART_SESSION", value, 1));
1486+
1487+ return TRUE;
1488+}
1489+
1490+/**
1491+ * selfpipe:
1492+ *
1493+ * Used to allow a timed process wait.
1494+ **/
1495+static int selfpipe[2] = { -1, -1 };
1496+
1497+static void
1498+selfpipe_write (int n)
1499+{
1500+ assert (selfpipe[1] != -1);
1501+
1502+ TEST_EQ (write (selfpipe[1], "", 1), 1);
1503+}
1504+
1505+/**
1506+ * selfpipe_setup:
1507+ *
1508+ * Arrange for SIGCHLD to write to selfpipe such that we can select(2)
1509+ * on child process status changes.
1510+ **/
1511+static void
1512+selfpipe_setup (void)
1513+{
1514+ static struct sigaction act;
1515+ int read_flags;
1516+ int write_flags;
1517+
1518+ assert (selfpipe[0] == -1);
1519+
1520+ assert (! pipe (selfpipe));
1521+
1522+ /* Set non-blocking */
1523+ read_flags = fcntl (selfpipe[0], F_GETFL);
1524+ write_flags = fcntl (selfpipe[1], F_GETFL);
1525+
1526+ read_flags |= O_NONBLOCK;
1527+ write_flags |= O_NONBLOCK;
1528+
1529+ assert (fcntl (selfpipe[0], F_SETFL, read_flags) == 0);
1530+ assert (fcntl (selfpipe[1], F_SETFL, write_flags) == 0);
1531+
1532+ /* Don't leak */
1533+ assert (fcntl (selfpipe[0], F_SETFD, FD_CLOEXEC) == 0);
1534+ assert (fcntl (selfpipe[1], F_SETFD, FD_CLOEXEC) == 0);
1535+
1536+ memset (&act, 0, sizeof (act));
1537+
1538+ /* register SIGCHLD handler which will cause pipe write when child
1539+ * changes state.
1540+ */
1541+ act.sa_handler = selfpipe_write;
1542+
1543+ sigaction (SIGCHLD, &act, NULL);
1544+}
1545+
1546+/**
1547+ * timed_waitpid:
1548+ *
1549+ * @pid: pid to wait for,
1550+ * @timeout: seconds to wait for @pid to change state.
1551+ *
1552+ * Simplified waitpid(2) with timeout using a pipe to allow select(2)
1553+ * with timeout to be used to wait for process state change.
1554+ **/
1555+pid_t
1556+timed_waitpid (pid_t pid, time_t timeout)
1557+{
1558+ static char buffer[1];
1559+ fd_set read_fds;
1560+ struct timeval tv;
1561+ int status;
1562+ int nfds;
1563+ int ret;
1564+ pid_t ret2;
1565+
1566+ assert (pid);
1567+ assert (timeout);
1568+
1569+ if (selfpipe[0] == -1)
1570+ selfpipe_setup ();
1571+
1572+ FD_ZERO (&read_fds);
1573+ FD_SET (selfpipe[0], &read_fds);
1574+
1575+ nfds = 1 + selfpipe[0];
1576+
1577+ tv.tv_sec = timeout;
1578+ tv.tv_usec = 0;
1579+
1580+ /* wait for some activity */
1581+ ret = select (nfds, &read_fds, NULL, NULL, &tv);
1582+
1583+ if (! ret)
1584+ /* timed out */
1585+ return 0;
1586+
1587+ /* discard any data written to pipe */
1588+ while (read (selfpipe[0], buffer, sizeof (buffer)) > 0)
1589+ ;
1590+
1591+ while (TRUE) {
1592+ /* wait for status change or error */
1593+ ret2 = waitpid (pid, &status, WNOHANG);
1594+
1595+ if (ret2 < 0)
1596+ return -1;
1597+
1598+ if (! ret2)
1599+ /* give child a chance to change state */
1600+ sleep (1);
1601+
1602+ if (ret2) {
1603+ if (WIFEXITED (status))
1604+ return ret2;
1605+
1606+ /* unexpected status change */
1607+ return -1;
1608+ }
1609+ }
1610+}
1611+
1612+
1613+/**
1614+ * get_initctl():
1615+ *
1616+ * Determine a suitable initctl command-line for testing purposes.
1617+ *
1618+ * Returns: Static string representing full path to initctl binary with
1619+ * default option to allow communication with an Upstart started using
1620+ * START_UPSTART().
1621+ **/
1622+char *
1623+get_initctl (void)
1624+{
1625+ static char path[PATH_MAX + 1024] = { 0 };
1626+ int ret;
1627+
1628+ ret = sprintf (path, "%s %s",
1629+ INITCTL_BINARY,
1630+ test_user_mode
1631+ ? "--user"
1632+ : "--session");
1633+
1634+ assert (ret > 0);
1635+
1636+ return path;
1637+}
1638+
1639+/*
1640+ * _start_upstart:
1641+ *
1642+ * @pid: PID of running instance,
1643+ * @user: TRUE if upstart will run in User Session mode (FALSE to
1644+ * use the users D-Bus session bus),
1645+ * @args: optional list of arguments to specify.
1646+ *
1647+ * Start an instance of Upstart.
1648+ *
1649+ * If the instance fails to start, abort(3) is called.
1650+ **/
1651+void
1652+_start_upstart (pid_t *pid, int user, char * const *args)
1653+{
1654+ nih_local char **argv = NULL;
1655+ sigset_t child_set, orig_set;
1656+
1657+ assert (pid);
1658+
1659+ argv = NIH_MUST (nih_str_array_new (NULL));
1660+
1661+ NIH_MUST (nih_str_array_add (&argv, NULL, NULL,
1662+ UPSTART_BINARY));
1663+
1664+ if (args)
1665+ NIH_MUST (nih_str_array_append (&argv, NULL, NULL, args));
1666+
1667+ sigfillset (&child_set);
1668+ sigprocmask (SIG_BLOCK, &child_set, &orig_set);
1669+
1670+ TEST_NE (*pid = fork (), -1);
1671+
1672+ if (! *pid) {
1673+ int fd;
1674+ nih_signal_reset ();
1675+ sigprocmask (SIG_SETMASK, &orig_set, NULL);
1676+
1677+ if (! getenv ("UPSTART_TEST_VERBOSE")) {
1678+ fd = open ("/dev/null", O_RDWR);
1679+ assert (fd >= 0);
1680+ assert (dup2 (fd, STDIN_FILENO) != -1);
1681+ assert (dup2 (fd, STDOUT_FILENO) != -1);
1682+ assert (dup2 (fd, STDERR_FILENO) != -1);
1683+ }
1684+
1685+ assert (execv (argv[0], argv) != -1);
1686+ }
1687+
1688+ sigprocmask (SIG_SETMASK, &orig_set, NULL);
1689+ wait_for_upstart (user);
1690+}
1691+
1692+/**
1693+ * start_upstart_common:
1694+ *
1695+ * @pid: PID of running instance,
1696+ * @user: TRUE if upstart should run in User Session mode (FALSE to
1697+ * use the users D-Bus session bus),
1698+ * @confdir: full path to configuration directory,
1699+ * @logdir: full path to log directory,
1700+ * @extra: optional extra arguments.
1701+ *
1702+ * Wrapper round _start_upstart() which specifies common options.
1703+ **/
1704+void
1705+start_upstart_common (pid_t *pid, int user, const char *confdir,
1706+ const char *logdir, char * const *extra)
1707+{
1708+ nih_local char **args = NULL;
1709+
1710+ assert (pid);
1711+
1712+ args = NIH_MUST (nih_str_array_new (NULL));
1713+
1714+ if (user) {
1715+ NIH_MUST (nih_str_array_add (&args, NULL, NULL,
1716+ "--user"));
1717+ test_user_mode = TRUE;
1718+ } else {
1719+ TEST_TRUE (getenv ("DBUS_SESSION_BUS_ADDRESS"));
1720+ NIH_MUST (nih_str_array_add (&args, NULL, NULL,
1721+ "--session"));
1722+ }
1723+
1724+ NIH_MUST (nih_str_array_add (&args, NULL, NULL,
1725+ "--no-startup-event"));
1726+
1727+ NIH_MUST (nih_str_array_add (&args, NULL, NULL,
1728+ "--no-sessions"));
1729+
1730+ NIH_MUST (nih_str_array_add (&args, NULL, NULL,
1731+ "--no-inherit-env"));
1732+
1733+ if (confdir) {
1734+ NIH_MUST (nih_str_array_add (&args, NULL, NULL,
1735+ "--confdir"));
1736+ NIH_MUST (nih_str_array_add (&args, NULL, NULL,
1737+ confdir));
1738+ }
1739+
1740+ if (logdir) {
1741+ NIH_MUST (nih_str_array_add (&args, NULL, NULL,
1742+ "--logdir"));
1743+ NIH_MUST (nih_str_array_add (&args, NULL, NULL,
1744+ logdir));
1745+ }
1746+
1747+ if (extra)
1748+ NIH_MUST (nih_str_array_append (&args, NULL, NULL, extra));
1749+
1750+ _start_upstart (pid, user, args);
1751+}
1752+
1753+/**
1754+ * start_upstart:
1755+ *
1756+ * @pid: PID of running instance.
1757+ *
1758+ * Wrapper round _start_upstart() which just runs an instance with no
1759+ * options.
1760+ **/
1761+void
1762+start_upstart (pid_t *pid)
1763+{
1764+ start_upstart_common (pid, FALSE, NULL, NULL, NULL);
1765+}
1766+
1767+/**
1768+ * job_to_pid:
1769+ *
1770+ * @job: job name.
1771+ *
1772+ * Determine pid of running job.
1773+ *
1774+ * WARNING: it is the callers responsibility to ensure that
1775+ * @job is still running when this function is called!!
1776+ *
1777+ * Returns: pid of job, or -1 if not found.
1778+ **/
1779+pid_t
1780+job_to_pid (const char *job)
1781+{
1782+ pid_t pid;
1783+ regex_t regex;
1784+ regmatch_t regmatch[2];
1785+ int ret;
1786+ nih_local char *cmd = NULL;
1787+ nih_local char *pattern = NULL;
1788+ size_t lines;
1789+ char **status;
1790+ nih_local char *str_pid = NULL;
1791+
1792+ assert (job);
1793+
1794+ pattern = NIH_MUST (nih_sprintf
1795+ (NULL, "^\\b%s\\b .*, process ([0-9]+)", job));
1796+
1797+ cmd = NIH_MUST (nih_sprintf (NULL, "%s status %s 2>&1",
1798+ get_initctl (), job));
1799+ RUN_COMMAND (NULL, cmd, &status, &lines);
1800+ TEST_EQ (lines, 1);
1801+
1802+ ret = regcomp (&regex, pattern, REG_EXTENDED);
1803+ assert0 (ret);
1804+
1805+ ret = regexec (&regex, status[0], 2, regmatch, 0);
1806+ if (ret == REG_NOMATCH) {
1807+ ret = -1;
1808+ goto out;
1809+ }
1810+ assert0 (ret);
1811+
1812+ if (regmatch[1].rm_so == -1 || regmatch[1].rm_eo == -1) {
1813+ ret = -1;
1814+ goto out;
1815+ }
1816+
1817+ /* extract the pid */
1818+ NIH_MUST (nih_strncat (&str_pid, NULL,
1819+ &status[0][regmatch[1].rm_so],
1820+ regmatch[1].rm_eo - regmatch[1].rm_so));
1821+
1822+ nih_free (status);
1823+
1824+ pid = (pid_t)atol (str_pid);
1825+
1826+ /* check it's running */
1827+ ret = kill (pid, 0);
1828+ if (! ret)
1829+ ret = pid;
1830+
1831+out:
1832+ regfree (&regex);
1833+ return ret;
1834+}
1835+
1836+const char *
1837+get_upstart_binary (void)
1838+{
1839+ return UPSTART_BINARY;
1840+}
1841+
1842+const char *
1843+get_initctl_binary (void)
1844+{
1845+ return INITCTL_BINARY;
1846+}
1847+
1848+/**
1849+ * string_check:
1850+ *
1851+ * @a: first string,
1852+ * @b: second string.
1853+ *
1854+ * Compare @a and @b either or both of which may be NULL.
1855+ *
1856+ * Returns 0 if strings are identical or both NULL, else 1.
1857+ **/
1858+int
1859+string_check (const char *a, const char *b)
1860+{
1861+ if (!a && !b)
1862+ return 0;
1863+
1864+ if (!a || !b)
1865+ return 1;
1866+
1867+ if (strcmp (a, b))
1868+ return 1;
1869+
1870+ return 0;
1871+}
1872+
1873+/**
1874+ * strcmp_compar:
1875+ *
1876+ * @a: first string,
1877+ * @b: second string.
1878+ *
1879+ * String comparison function suitable for passing to qsort(3).
1880+ * See the qsort(3) man page for further details.
1881+ **/
1882+int
1883+strcmp_compar (const void *a, const void *b)
1884+{
1885+ return strcmp(*(char * const *)a, *(char * const *)b);
1886+}
1887+
1888+/**
1889+ * get_session_file:
1890+ *
1891+ * @xdg_runtime_dir: Directory to treat as XDG_RUNTIME_DIR,
1892+ * @pid: pid of running Session Init instance.
1893+ *
1894+ * Determine full path to a Session Inits session file.
1895+ *
1896+ * Note: No check on the existence of the session file is performed.
1897+ *
1898+ * Returns: Newly-allocated string representing full path to Session
1899+ * Inits session file.
1900+ **/
1901+char *
1902+get_session_file (const char *xdg_runtime_dir, pid_t pid)
1903+{
1904+ char *session_file;
1905+
1906+ nih_assert (xdg_runtime_dir);
1907+ nih_assert (pid);
1908+
1909+ session_file = nih_sprintf (NULL, "%s/upstart/sessions/%d.session",
1910+ xdg_runtime_dir, (int)pid);
1911+
1912+ nih_assert (session_file);
1913+
1914+ return session_file;
1915+}
1916
1917=== added file 'test/test_util.h'
1918--- test/test_util.h 1970-01-01 00:00:00 +0000
1919+++ test/test_util.h 2013-05-30 13:17:28 +0000
1920@@ -0,0 +1,725 @@
1921+#ifndef TEST_UTIL_H
1922+#define TEST_UTIL_H
1923+
1924+#include <string.h>
1925+#include <sys/types.h>
1926+#include <sys/wait.h>
1927+
1928+#include <nih-dbus/test_dbus.h>
1929+
1930+#define BUFFER_SIZE 1024
1931+
1932+/**
1933+ * TEST_QUIESCE_WAIT_PHASE:
1934+ *
1935+ * Maximum time we expect upstart to wait in the QUIESCE_PHASE_WAIT
1936+ * phase.
1937+ **/
1938+#define TEST_EXIT_TIME 5
1939+
1940+/**
1941+ * TEST_QUIESCE_KILL_PHASE:
1942+ *
1943+ * Maximum time we expect upstart to wait in the QUIESCE_PHASE_KILL
1944+ * phase.
1945+ **/
1946+#define TEST_QUIESCE_KILL_PHASE 5
1947+
1948+#define TEST_QUIESCE_TOTAL_WAIT_TIME (TEST_EXIT_TIME + TEST_QUIESCE_KILL_PHASE)
1949+
1950+/* A 'reasonable' path, but which also contains a marker at the end so
1951+ * we know when we're looking at a PATH these tests have set.
1952+ */
1953+#define TEST_INITCTL_DEFAULT_PATH "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/wibble"
1954+
1955+/* Default value for TERM if not already set */
1956+#define TEST_INITCTL_DEFAULT_TERM "linux"
1957+
1958+
1959+/* TEST_ENSURE_CLEAN_ENV:
1960+ *
1961+ * Ensure the environment is as pristine as possible (to avoid follow-on
1962+ * errors caused by not freeing objects in a previous test, say)
1963+ */
1964+#define TEST_ENSURE_CLEAN_ENV() \
1965+{ \
1966+ setvbuf(stdout, NULL, _IONBF, 0); \
1967+ \
1968+ if (job_classes) { \
1969+ TEST_HASH_EMPTY (job_classes); \
1970+ } \
1971+ \
1972+ if (conf_sources) { \
1973+ TEST_LIST_EMPTY (conf_sources); \
1974+ } \
1975+ \
1976+ if (nih_io_watches) { \
1977+ TEST_LIST_EMPTY (nih_io_watches); \
1978+ } \
1979+ \
1980+ if (nih_timers) { \
1981+ TEST_LIST_EMPTY (nih_timers); \
1982+ } \
1983+ \
1984+ if (events) { \
1985+ TEST_LIST_EMPTY (events); \
1986+ } \
1987+}
1988+
1989+/**
1990+ * _TEST_WATCH_UPDATE:
1991+ * @force: if TRUE, force an update,
1992+ * @timeout: struct timeval pointer, or NULL if no timeout required.
1993+ *
1994+ * Request NIH look for a file event relating to any NihIo objects,
1995+ * with an optional timeout. Behaviour can be forced via @force.
1996+ **/
1997+#define _TEST_WATCH_UPDATE(force, timeout) \
1998+{ \
1999+ int nfds = 0; \
2000+ int ret = 0; \
2001+ fd_set readfds, writefds, exceptfds; \
2002+ \
2003+ FD_ZERO (&readfds); \
2004+ FD_ZERO (&writefds); \
2005+ FD_ZERO (&exceptfds); \
2006+ \
2007+ nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); \
2008+ if (! force) { \
2009+ ret = select (nfds, &readfds, &writefds, \
2010+ &exceptfds, timeout); \
2011+ } \
2012+ if (force || ret > 0) \
2013+ nih_io_handle_fds (&readfds, &writefds, &exceptfds); \
2014+}
2015+
2016+/**
2017+ * TEST_WATCH_UPDATE:
2018+ *
2019+ * Request NIH look for a file event relating to any NihIo objects,
2020+ * */
2021+#define TEST_WATCH_UPDATE() \
2022+ _TEST_WATCH_UPDATE (0, NULL)
2023+
2024+/**
2025+ * TEST_WATCH_UPDATE_TIMEOUT:
2026+ * @timeout: struct timeval pointer.
2027+ *
2028+ * Request NIH look for a file event relating to any NihIo objects
2029+ * within time period @timeout.
2030+ **/
2031+#define TEST_WATCH_UPDATE_TIMEOUT(timeout) \
2032+ _TEST_WATCH_UPDATE (0, timeout)
2033+
2034+/**
2035+ * TEST_WATCH_UPDATE_TIMEOUT_SECS:
2036+ * @secs: seconds to wait before timeout.
2037+ *
2038+ * Request NIH look for a file event relating to any NihIo objects
2039+ * within @secs timeout.
2040+ **/
2041+#define TEST_WATCH_UPDATE_TIMEOUT_SECS(secs) \
2042+{ \
2043+ struct timeval _t; \
2044+ _t.tv_sec = secs; \
2045+ _t.tv_usec = 0; \
2046+ _TEST_WATCH_UPDATE (0, &_t); \
2047+}
2048+
2049+/**
2050+ * TEST_FORCE_WATCH_UPDATE:
2051+ *
2052+ * Force NIH to look for a file event relating to any NihIo objects.
2053+ **/
2054+#define TEST_FORCE_WATCH_UPDATE() \
2055+ _TEST_WATCH_UPDATE (1, NULL)
2056+
2057+/**
2058+ * TEST_FORCE_WATCH_UPDATE_TIMEOUT:
2059+ * @timeout: struct timeval pointer.
2060+ *
2061+ * Force NIH to look for a file event relating to any NihIo objects
2062+ * within time period @timeout.
2063+ **/
2064+#define TEST_FORCE_WATCH_UPDATE_TIMEOUT(timeout) \
2065+ _TEST_WATCH_UPDATE (1, timeout)
2066+
2067+/**
2068+ * TEST_FORCE_WATCH_UPDATE_TIMEOUT_SECS:
2069+ * @timeout: struct timeval pointer.
2070+ *
2071+ * Force NIH to look for a file event relating to any NihIo objects
2072+ * within time period @timeout.
2073+ **/
2074+#define TEST_FORCE_WATCH_UPDATE_TIMEOUT_SECS(secs) \
2075+{ \
2076+ struct timeval _t; \
2077+ _t.tv_sec = secs; \
2078+ _t.tv_usec = 0; \
2079+ _TEST_WATCH_UPDATE (1, &_t); \
2080+}
2081+
2082+/**
2083+ * ENSURE_DIRECTORY_EMPTY:
2084+ * @path: Full path to a directory.
2085+ *
2086+ * Ensure specified directory is empty.
2087+ **/
2088+#define ENSURE_DIRECTORY_EMPTY(path) \
2089+{ \
2090+ DIR *dp = NULL; \
2091+ struct dirent *file = NULL; \
2092+ int count = 0; \
2093+ \
2094+ dp = opendir (path); \
2095+ TEST_NE_P (dp, NULL); \
2096+ \
2097+ while((file = readdir (dp))) { \
2098+ if (!strcmp (".", file->d_name) || \
2099+ !strcmp ("..", file->d_name)) \
2100+ continue; \
2101+ count++; \
2102+ } \
2103+ \
2104+ closedir (dp); \
2105+ \
2106+ TEST_EQ (count, 0); \
2107+}
2108+
2109+/**
2110+ * obj_string_check:
2111+ *
2112+ * @a: first object,
2113+ * @b: second object,
2114+ * @name: name of string element.
2115+ *
2116+ * Compare string element @name in objects @a and @b.
2117+ *
2118+ * Returns: 0 if strings are identical
2119+ * (or both NULL), else 1.
2120+ **/
2121+#define obj_string_check(a, b, name) \
2122+ string_check ((a)->name, (b)->name)
2123+
2124+/**
2125+ * obj_num_check:
2126+ *
2127+ * @a: first object,
2128+ * @b: second object.
2129+ * @name: name of numeric element.
2130+ *
2131+ * Compare numeric element @name in objects @a and @b.
2132+ *
2133+ * Returns: 0 if @a and @b are identical, else 1.
2134+ **/
2135+#define obj_num_check(a, b, name) \
2136+ (a->name != b->name)
2137+
2138+/**
2139+ * TEST_CMP_INT_ARRAYS:
2140+ * @a: first array,
2141+ * @b: second array,
2142+ * @sizea: size of @a,
2143+ * @sizeb: size of @b.
2144+ *
2145+ * Compare integer arrays @a and @b for equivalence.
2146+ *
2147+ * Returns: 0 if arrays are identical, else -1.
2148+ **/
2149+#define TEST_CMP_INT_ARRAYS(a, b, sizea, sizeb) \
2150+({int ret = 0; \
2151+ size_t __i; \
2152+ if (sizea == sizeb) { \
2153+ for (__i = 0; \
2154+ __i < sizea; \
2155+ __i++) { \
2156+ if ((a)[__i] != (b)[__i]) { \
2157+ ret = -1; \
2158+ break; \
2159+ } \
2160+ } \
2161+ } else \
2162+ ret = -1; \
2163+ ret;})
2164+
2165+/**
2166+ * TEST_CMP_STR_ARRAYS:
2167+ * @a: first string array,
2168+ * @b: second string array,
2169+ * @sizea: length of @a,
2170+ * @sizeb: length of @b.
2171+ *
2172+ * Compare string arrays @a and @b for equivalence.
2173+ *
2174+ * Returns: 0 if arrays are identical, else -1.
2175+ **/
2176+#define TEST_CMP_STR_ARRAYS(a, b, sizea, sizeb) \
2177+({ int ret = 0; \
2178+ if (sizea == sizeb) { \
2179+ for (size_t __i = 0; \
2180+ __i < sizea; \
2181+ __i++) { \
2182+ if (strcmp (a[__i], b[__i])) { \
2183+ ret = -1; \
2184+ break; \
2185+ } \
2186+ } \
2187+ } else \
2188+ ret = -1; \
2189+ ret;})
2190+
2191+/**
2192+ * TEST_TWO_LISTS_FOREACH:
2193+ * @list1: entry in the first list to iterate,
2194+ * @list2: entry in the second list to iterate,
2195+ * @iter1: name of iterator variable for @list1,
2196+ * @iter2: name of iterator variable for @list2.
2197+ *
2198+ * Dual version of NIH_LIST_FOREACH() which iterates
2199+ * two lists in tandem.
2200+ **/
2201+#define TEST_TWO_LISTS_FOREACH(list1, list2, iter1, iter2) \
2202+ for (NihList *iter1 = (list1)->next, \
2203+ *iter2 = (list2)->next; \
2204+ iter1 != (list1) && iter2 != (list2); \
2205+ iter1 = iter1->next, \
2206+ iter2 = iter2->next)
2207+
2208+/**
2209+ * TEST_TWO_HASHES_FOREACH:
2210+ * @hash1: entry in the first hash to iterate,
2211+ * @hash2: entry in the second hash to iterate,
2212+ * @iter1: name of iterator variable for @hash1,
2213+ * @iter2: name of iterator variable for @hash2.
2214+ *
2215+ * Dual version of NIH_HASH_FOREACH() which iterates
2216+ * two hashes in tandem.
2217+ **/
2218+#define TEST_TWO_HASHES_FOREACH(hash1, hash2, iter1, iter2) \
2219+ for (size_t _##iter##_i = 0; _##iter##_i < (hash1)->size; \
2220+ _##iter##_i++) \
2221+ TEST_TWO_LISTS_FOREACH (&(hash1)->bins[_##iter##_i], \
2222+ &(hash2)->bins[_##iter##_i], \
2223+ iter1, iter2)
2224+
2225+/**
2226+ * TEST_TWO_TREES_FOREACH:
2227+ * @tree1: root of the first tree to iterate,
2228+ * @tree2: root of the second tree to iterate,
2229+ * @iter1: name of iterator variable for @tree1,
2230+ * @iter2: name of iterator variable for @tree2.
2231+ *
2232+ * Dual version of NIH_TREE_FOREACH() which walks
2233+ * two trees in tandem.
2234+ **/
2235+#define TEST_TWO_TREES_FOREACH(tree1, tree2, iter1, iter2) \
2236+ for (NihTree *iter1 = nih_tree_next (tree1, NULL), \
2237+ *iter2 = nih_tree_next (tree2, NULL); \
2238+ iter1 != NULL && iter2 != NULL; \
2239+ iter1 = nih_tree_next (tree1, iter1), \
2240+ iter2 = nih_tree_next (tree2, iter2))
2241+
2242+
2243+/**
2244+ * TEST_ARRAY_SIZE:
2245+ * @array: array.
2246+ *
2247+ * Determine size of specified array.
2248+ *
2249+ * Returns: array size.
2250+ **/
2251+#define TEST_ARRAY_SIZE(array) \
2252+ (sizeof (array) / sizeof (array[0]))
2253+
2254+/**
2255+ * START_UPSTART:
2256+ *
2257+ * @pid: pid_t that will contain pid of running instance on success,
2258+ * @user_mode: TRUE for Session Init (or FALSE to use D-Bus
2259+ * session bus).
2260+ *
2261+ * Start an instance of Upstart and return PID in @pid.
2262+ **/
2263+#define START_UPSTART(pid, user_mode) \
2264+ start_upstart_common (&(pid), user_mode, NULL, NULL, NULL)
2265+
2266+/**
2267+ * KILL_UPSTART:
2268+ *
2269+ * @pid: pid of upstart to kill,
2270+ * @signo: signal number to send to @pid,
2271+ * @wait: TRUE to wait for @pid to die.
2272+ *
2273+ * Send specified signal to upstart process @pid.
2274+ **/
2275+#define KILL_UPSTART(pid, signo, wait) \
2276+{ \
2277+ int status; \
2278+ assert (pid); \
2279+ assert (signo); \
2280+ \
2281+ assert0 (kill (pid, signo)); \
2282+ if (wait) { \
2283+ TEST_EQ (waitpid (pid, &status, 0), pid); \
2284+ TEST_TRUE (WIFSIGNALED (status)); \
2285+ TEST_TRUE (WTERMSIG (status) == signo); \
2286+ } \
2287+ /* reset since a subsequent start could specify a different \
2288+ * user_mode value. \
2289+ */ \
2290+ test_user_mode = FALSE; \
2291+}
2292+
2293+/**
2294+ * STOP_UPSTART:
2295+ *
2296+ * @pid: pid of upstart to kill.
2297+ *
2298+ * Stop upstart process @pid.
2299+ **/
2300+#define STOP_UPSTART(pid) \
2301+ KILL_UPSTART (pid, SIGKILL, TRUE)
2302+
2303+/**
2304+ * REEXEC_UPSTART:
2305+ *
2306+ * @pid: pid of upstart.
2307+ *
2308+ * Force upstart to perform a re-exec.
2309+ **/
2310+#define REEXEC_UPSTART(pid) \
2311+ KILL_UPSTART (pid, SIGTERM, FALSE); \
2312+ wait_for_upstart (FALSE)
2313+
2314+/**
2315+ * RUN_COMMAND:
2316+ *
2317+ * @parent: pointer to parent object,
2318+ * @cmd: string representing command to run,
2319+ * @result: "char ***" pointer which will contain an array of string
2320+ * values corresponding to lines of standard output generated by @cmd,
2321+ * @len: size_t pointer which will be set to length of @result.
2322+ *
2323+ * Run a command and return its standard output. It is the callers
2324+ * responsibility to free @result. Errors from running @cmd are fatal.
2325+ *
2326+ * Note: trailing '\n' characters are removed in returned command
2327+ * output.
2328+ **/
2329+#define RUN_COMMAND(parent, cmd, result, len) \
2330+{ \
2331+ FILE *f; \
2332+ char buffer[BUFFER_SIZE]; \
2333+ char **ret; \
2334+ \
2335+ assert (cmd[0]); \
2336+ \
2337+ *(result) = nih_str_array_new (parent); \
2338+ TEST_NE_P (*result, NULL); \
2339+ *(len) = 0; \
2340+ \
2341+ f = popen (cmd, "r"); \
2342+ TEST_NE_P (f, NULL); \
2343+ \
2344+ while (fgets (buffer, BUFFER_SIZE, f)) { \
2345+ size_t l = strlen (buffer)-1; \
2346+ \
2347+ if ( buffer[l] == '\n') \
2348+ buffer[l] = '\0'; \
2349+ ret = nih_str_array_add (result, parent, len, \
2350+ buffer); \
2351+ TEST_NE_P (ret, NULL); \
2352+ } \
2353+ \
2354+ TEST_NE (pclose (f), -1); \
2355+}
2356+
2357+/**
2358+ * CREATE_FILE:
2359+ *
2360+ * @dirname: directory name (assumed to already exist),
2361+ * @name: name of file to create (no leading slash),
2362+ * @contents: string contents of @name.
2363+ *
2364+ * Create a file in the specified directory with the specified
2365+ * contents.
2366+ *
2367+ * Notes: A newline character is added in the case where @contents does
2368+ * not end with one.
2369+ **/
2370+#define CREATE_FILE(dirname, name, contents) \
2371+{ \
2372+ FILE *f; \
2373+ char filename[PATH_MAX]; \
2374+ \
2375+ assert (dirname[0]); \
2376+ assert (name[0]); \
2377+ \
2378+ strcpy (filename, dirname); \
2379+ if ( name[0] != '/' ) \
2380+ strcat (filename, "/"); \
2381+ strcat (filename, name); \
2382+ f = fopen (filename, "w"); \
2383+ TEST_NE_P (f, NULL); \
2384+ fprintf (f, "%s", contents); \
2385+ if ( contents[strlen(contents)-1] != '\n') \
2386+ fprintf (f, "\n"); \
2387+ fclose (f); \
2388+}
2389+
2390+/**
2391+ * DELETE_FILE:
2392+ *
2393+ * @dirname: directory in which file to delete exists,
2394+ * @name: name of file in @dirname to delete.
2395+ *
2396+ * Delete specified file.
2397+ *
2398+ **/
2399+#define DELETE_FILE(dirname, name) \
2400+{ \
2401+ char filename[PATH_MAX]; \
2402+ \
2403+ assert (dirname[0]); \
2404+ assert (name[0]); \
2405+ \
2406+ strcpy (filename, dirname); \
2407+ if ( name[0] != '/' ) \
2408+ strcat (filename, "/"); \
2409+ strcat (filename, name); \
2410+ \
2411+ TEST_EQ (unlink (filename), 0); \
2412+}
2413+
2414+/**
2415+ * _WAIT_FOR_FILE():
2416+ *
2417+ * @path: full path to file to look for,
2418+ * @sleep_secs: number of seconds to sleep per loop,
2419+ * @loops: number of times to check for file.
2420+ *
2421+ * Wait for a reasonable period of time for @path to be created.
2422+ *
2423+ * Abort if file does not appear within (sleep_secs * loops) seconds.
2424+ *
2425+ * XXX:WARNING: this is intrinsically racy since although the file has
2426+ * been _created_, it has not necessarily been fully written at the
2427+ * point this macro signifies success. For that we need inotify or
2428+ * similar.
2429+ **/
2430+#define _WAIT_FOR_FILE(path, sleep_secs, loops) \
2431+{ \
2432+ int ok; \
2433+ struct stat statbuf; \
2434+ \
2435+ assert (path[0]); \
2436+ \
2437+ /* Wait for log to be created */ \
2438+ ok = FALSE; \
2439+ for (int i = 0; i < loops; i++) { \
2440+ sleep (sleep_secs); \
2441+ if (! stat (logfile, &statbuf)) { \
2442+ ok = TRUE; \
2443+ break; \
2444+ } \
2445+ } \
2446+ TEST_EQ (ok, TRUE); \
2447+}
2448+
2449+/**
2450+ * WAIT_FOR_FILE():
2451+ *
2452+ * @path: full path to file to look for.
2453+ *
2454+ * Wait for a "reasonable period of time" for @path to be created.
2455+ *
2456+ * Abort if file does not appear within.
2457+ **/
2458+#define WAIT_FOR_FILE(path) \
2459+ _WAIT_FOR_FILE (path, 1, 5)
2460+
2461+/**
2462+ * TEST_STR_MATCH:
2463+ * @_string: string to check,
2464+ * @_pattern: pattern to expect.
2465+ *
2466+ * Check that @_string matches the glob pattern @_pattern, which
2467+ * should include the terminating newline if one is expected.
2468+ *
2469+ * Notes: Analagous to TEST_FILE_MATCH().
2470+ **/
2471+#define TEST_STR_MATCH(_string, _pattern) \
2472+ do { \
2473+ if (fnmatch ((_pattern), _string, 0)) \
2474+ TEST_FAILED ("wrong string value, " \
2475+ "expected '%s' got '%s'", \
2476+ (_pattern), _string); \
2477+ } while (0)
2478+
2479+/**
2480+ * _TEST_STR_ARRAY_CONTAINS:
2481+ *
2482+ * @_array: string array,
2483+ * @_pattern: pattern to expect,
2484+ * @_invert: invert meaning.
2485+ *
2486+ * Check that atleast 1 element in @_array matches @_pattern.
2487+ *
2488+ * If @_invert is TRUE, ensure @_pattern is _NOT_ found in @_array.
2489+ **/
2490+#define _TEST_STR_ARRAY_CONTAINS(_array, _pattern, _invert) \
2491+ do { \
2492+ char **p; \
2493+ int got = FALSE; \
2494+ \
2495+ for (p = _array; p && *p; p++) { \
2496+ \
2497+ if (! fnmatch ((_pattern), *p, 0)) { \
2498+ got = TRUE; \
2499+ break; \
2500+ } \
2501+ } \
2502+ \
2503+ if (_invert) { \
2504+ if (got) { \
2505+ TEST_FAILED ("wrong content in array " \
2506+ "%p (%s), '%s' found unexpectedly", \
2507+ (_array), #_array, (_pattern)); \
2508+ } \
2509+ } else { \
2510+ if (! got) { \
2511+ TEST_FAILED ("wrong content in array " \
2512+ "%p (%s), '%s' not found", \
2513+ (_array), #_array, (_pattern)); \
2514+ } \
2515+ } \
2516+ } while (0)
2517+
2518+/**
2519+ * _TEST_FILE_CONTAINS:
2520+ * @_file: FILE to read from,
2521+ * @_pattern: pattern to expect,
2522+ * @_invert: invert meaning.
2523+ *
2524+ * Check that any subsequent line in file @_file matches the glob pattern
2525+ * @_pattern, which should include the terminating newline if one is expected.
2526+ *
2527+ * If @_invert is TRUE, ensure @_pattern is _NOT_ found in @_file.
2528+ **/
2529+#define _TEST_FILE_CONTAINS(_file, _pattern, _invert) \
2530+ do { \
2531+ char buffer[1024]; \
2532+ int got = FALSE; \
2533+ int ret; \
2534+ while (fgets (buffer, sizeof (buffer), _file)) { \
2535+ \
2536+ ret = fnmatch ((_pattern), buffer, 0); \
2537+ \
2538+ if (! ret) { \
2539+ got = TRUE; \
2540+ break; \
2541+ } \
2542+ } \
2543+ \
2544+ if (_invert) { \
2545+ if (got) { \
2546+ TEST_FAILED ("wrong content in file " \
2547+ "%p (%s), '%s' found unexpectedly", \
2548+ (_file), #_file, (_pattern)); \
2549+ } \
2550+ } else { \
2551+ if (! got) { \
2552+ TEST_FAILED ("wrong content in file " \
2553+ "%p (%s), '%s' not found", \
2554+ (_file), #_file, (_pattern)); \
2555+ } \
2556+ } \
2557+ } while (0)
2558+
2559+
2560+/**
2561+ * TEST_FILE_CONTAINS:
2562+ * @_file: FILE to read from,
2563+ * @_pattern: pattern to expect.
2564+ *
2565+ * Check that any subsequent line in file @_file matches the glob pattern
2566+ * @_pattern, which should include the terminating newline if one is expected.
2567+ *
2568+ **/
2569+#define TEST_FILE_CONTAINS(_file, _pattern) \
2570+ _TEST_FILE_CONTAINS(_file, _pattern, FALSE)
2571+
2572+/**
2573+ * TEST_FILE_NOT_CONTAINS:
2574+ * @_file: FILE to read from,
2575+ * @_pattern: pattern NOT to expect.
2576+ *
2577+ * Check that no subsequent line in file @_file does NOT match the glob pattern
2578+ * @_pattern, which should include the terminating newline if one is expected.
2579+ *
2580+ **/
2581+#define TEST_FILE_NOT_CONTAINS(_file, _pattern) \
2582+ _TEST_FILE_CONTAINS(_file, _pattern, TRUE)
2583+
2584+/**
2585+ * TEST_STR_ARRAY_CONTAINS:
2586+ *
2587+ * @_array: string array,
2588+ * @_pattern: pattern to expect.
2589+ *
2590+ * Check that atleast 1 element in @_array matches @_pattern.
2591+ **/
2592+#define TEST_STR_ARRAY_CONTAINS(_array, _pattern) \
2593+ _TEST_STR_ARRAY_CONTAINS (_array, _pattern, FALSE)
2594+
2595+/**
2596+ * TEST_STR_ARRAY_NOT_CONTAINS:
2597+ *
2598+ * @_array: string array,
2599+ * @_pattern: pattern to expect.
2600+ *
2601+ * Check that no element in @_array matches @_pattern.
2602+ **/
2603+#define TEST_STR_ARRAY_NOT_CONTAINS(_array, _pattern) \
2604+ _TEST_STR_ARRAY_CONTAINS (_array, _pattern, TRUE)
2605+
2606+extern int test_user_mode;
2607+
2608+/* Prototypes */
2609+int set_upstart_session (void)
2610+ __attribute__ ((warn_unused_result));
2611+
2612+void wait_for_upstart (int user);
2613+
2614+pid_t timed_waitpid (pid_t pid, time_t timeout)
2615+ __attribute__ ((warn_unused_result));
2616+
2617+char * get_initctl (void)
2618+ __attribute__ ((warn_unused_result));
2619+
2620+void _start_upstart (pid_t *pid, int user, char * const *args);
2621+
2622+void start_upstart_common (pid_t *pid, int user, const char *confdir,
2623+ const char *logdir, char * const *extra);
2624+
2625+void start_upstart (pid_t *pid);
2626+
2627+pid_t job_to_pid (const char *job)
2628+ __attribute__ ((warn_unused_result));
2629+
2630+int string_check (const char *a, const char *b)
2631+ __attribute__ ((warn_unused_result));
2632+
2633+const char * get_upstart_binary (void)
2634+ __attribute__ ((warn_unused_result));
2635+
2636+const char * get_initctl_binary (void)
2637+ __attribute__ ((warn_unused_result));
2638+
2639+int strcmp_compar (const void *a, const void *b)
2640+ __attribute__ ((warn_unused_result));
2641+
2642+char *get_session_file (const char *xdg_runtime_dir, pid_t pid)
2643+ __attribute__ ((warn_unused_result));
2644+
2645+#endif /* TEST_UTIL_H */
2646
2647=== modified file 'util/Makefile.am'
2648--- util/Makefile.am 2013-03-04 11:44:49 +0000
2649+++ util/Makefile.am 2013-05-30 13:17:28 +0000
2650@@ -10,14 +10,8 @@
2651 -DINITCTL_BUILD \
2652 -DSBINDIR="\"$(sbindir)\"" \
2653 -I$(top_builddir) -I$(top_srcdir) -iquote$(builddir) -iquote$(srcdir) \
2654- -I$(top_srcdir)/intl
2655-
2656-INITCTL_BINARY = $(abs_builddir)/initctl
2657-UPSTART_BINARY = $(abs_builddir)/../init/init
2658-
2659-AM_CPPFLAGS += \
2660- -DUPSTART_BINARY="\"$(UPSTART_BINARY)\"" \
2661- -DINITCTL_BINARY="\"$(INITCTL_BINARY)\""
2662+ -I$(top_srcdir)/intl \
2663+ -I$(top_srcdir)/test -iquote $(top_srcdir)/test
2664
2665 dist_man_MANS = \
2666 man/initctl.8 \
2667@@ -27,7 +21,6 @@
2668 man/telinit.8 \
2669 man/runlevel.7
2670
2671-
2672 sbin_PROGRAMS = \
2673 initctl \
2674 reboot \
2675@@ -189,7 +182,6 @@
2676 rm -f "$(DESTDIR)$(sbindir)/$$file"; \
2677 done
2678
2679-
2680 TESTS = \
2681 test_initctl \
2682 test_utmp \
2683@@ -206,6 +198,7 @@
2684 test_initctl_LDADD = \
2685 com.ubuntu.Upstart.o \
2686 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
2687+ $(top_builddir)/test/libtest_util.a \
2688 $(NIH_LIBS) \
2689 $(NIH_DBUS_LIBS) \
2690 $(DBUS_LIBS)
2691
2692=== modified file 'util/tests/test_initctl.c'
2693--- util/tests/test_initctl.c 2013-02-27 11:46:04 +0000
2694+++ util/tests/test_initctl.c 2013-05-30 13:17:28 +0000
2695@@ -53,882 +53,7 @@
2696
2697 #include "com.ubuntu.Upstart.h"
2698
2699-
2700-#ifndef UPSTART_BINARY
2701-#error unable to find init binary as UPSTART_BINARY not defined
2702-#endif /* UPSTART_BINARY */
2703-
2704-#ifndef INITCTL_BINARY
2705-#error unable to find initctl binary as INITCTL_BINARY not defined
2706-#endif /* INITCTL_BINARY */
2707-
2708-#define BUFFER_SIZE 1024
2709-
2710-/**
2711- * TEST_QUIESCE_WAIT_PHASE:
2712- *
2713- * Maximum time we expect upstart to wait in the QUIESCE_PHASE_WAIT
2714- * phase.
2715- **/
2716-#define TEST_EXIT_TIME 5
2717-
2718-/**
2719- * TEST_QUIESCE_KILL_PHASE:
2720- *
2721- * Maximum time we expect upstart to wait in the QUIESCE_PHASE_KILL
2722- * phase.
2723- **/
2724-#define TEST_QUIESCE_KILL_PHASE 5
2725-
2726-#define TEST_QUIESCE_TOTAL_WAIT_TIME (TEST_EXIT_TIME + TEST_QUIESCE_KILL_PHASE)
2727-
2728-/* A 'reasonable' path, but which also contains a marker at the end so
2729- * we know when we're looking at a PATH these tests have set.
2730- */
2731-#define TEST_INITCTL_DEFAULT_PATH "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/wibble"
2732-
2733-/* Default value for TERM if not already set */
2734-#define TEST_INITCTL_DEFAULT_TERM "linux"
2735-
2736-int
2737-set_upstart_session (void);
2738-
2739-/**
2740- * wait_for_upstart:
2741- *
2742- * @user: TRUE if waiting for a Session Init (which uses a private bus
2743- * rather than the session bus), else FALSE.
2744- *
2745- * Wait for Upstart to appear on D-Bus denoting its completion of
2746- * initialisation. Wait time is somewhat arbitrary (but more
2747- * than adequate!).
2748- **/
2749-void
2750-wait_for_upstart (int user)
2751-{
2752- nih_local NihDBusProxy *upstart = NULL;
2753- DBusConnection *connection;
2754- char *address;
2755- NihError *err;
2756- int running = FALSE;
2757-
2758- /* XXX: arbitrary value */
2759- int attempts = 10;
2760-
2761- if (user) {
2762- set_upstart_session ();
2763- address = getenv ("UPSTART_SESSION");
2764- } else {
2765- address = getenv ("DBUS_SESSION_BUS_ADDRESS");
2766- }
2767-
2768- TEST_TRUE (address);
2769-
2770- while (attempts) {
2771- attempts--;
2772- sleep (1);
2773- connection = nih_dbus_connect (address, NULL);
2774-
2775- if (! connection) {
2776- err = nih_error_get ();
2777- nih_free (err);
2778- continue;
2779- }
2780-
2781- upstart = nih_dbus_proxy_new (NULL, connection,
2782- NULL,
2783- DBUS_PATH_UPSTART,
2784- NULL, NULL);
2785-
2786- if (! upstart) {
2787- err = nih_error_get ();
2788- nih_free (err);
2789- dbus_connection_unref (connection);
2790- } else {
2791- running = TRUE;
2792- break;
2793- }
2794- }
2795- TEST_EQ (running, TRUE);
2796-}
2797-
2798-/**
2799- * START_UPSTART:
2800- *
2801- * @pid: pid_t that will contain pid of running instance on success,
2802- * @user_mode: TRUE for Session Init (or FALSE to use D-Bus
2803- * session bus).
2804- *
2805- * Start an instance of Upstart and return PID in @pid.
2806- **/
2807-#define START_UPSTART(pid, user_mode) \
2808- start_upstart_common (&(pid), user_mode, NULL, NULL, NULL)
2809-
2810-/**
2811- * KILL_UPSTART:
2812- *
2813- * @pid: pid of upstart to kill,
2814- * @signo: signal number to send to @pid,
2815- * @wait: TRUE to wait for @pid to die.
2816- *
2817- * Send specified signal to upstart process @pid.
2818- **/
2819-#define KILL_UPSTART(pid, signo, wait) \
2820-{ \
2821- int status; \
2822- assert (pid); \
2823- assert (signo); \
2824- \
2825- assert0 (kill (pid, signo)); \
2826- if (wait) { \
2827- TEST_EQ (waitpid (pid, &status, 0), pid); \
2828- TEST_TRUE (WIFSIGNALED (status)); \
2829- TEST_TRUE (WTERMSIG (status) == signo); \
2830- } \
2831- /* reset since a subsequent start could specify a different \
2832- * user_mode value. \
2833- */ \
2834- test_user_mode = FALSE; \
2835-}
2836-
2837-/**
2838- * STOP_UPSTART:
2839- *
2840- * @pid: pid of upstart to kill.
2841- *
2842- * Stop upstart process @pid.
2843- **/
2844-#define STOP_UPSTART(pid) \
2845- KILL_UPSTART (pid, SIGKILL, TRUE)
2846-
2847-/**
2848- * REEXEC_UPSTART:
2849- *
2850- * @pid: pid of upstart.
2851- *
2852- * Force upstart to perform a re-exec.
2853- **/
2854-#define REEXEC_UPSTART(pid) \
2855- KILL_UPSTART (pid, SIGTERM, FALSE); \
2856- wait_for_upstart (FALSE)
2857-
2858-/**
2859- * RUN_COMMAND:
2860- *
2861- * @parent: pointer to parent object,
2862- * @cmd: string representing command to run,
2863- * @result: "char ***" pointer which will contain an array of string
2864- * values corresponding to lines of standard output generated by @cmd,
2865- * @len: size_t pointer which will be set to length of @result.
2866- *
2867- * Run a command and return its standard output. It is the callers
2868- * responsibility to free @result. Errors from running @cmd are fatal.
2869- *
2870- * Note: trailing '\n' characters are removed in returned command
2871- * output.
2872- **/
2873-#define RUN_COMMAND(parent, cmd, result, len) \
2874-{ \
2875- FILE *f; \
2876- char buffer[BUFFER_SIZE]; \
2877- char **ret; \
2878- \
2879- assert (cmd[0]); \
2880- \
2881- *(result) = nih_str_array_new (parent); \
2882- TEST_NE_P (*result, NULL); \
2883- *(len) = 0; \
2884- \
2885- f = popen (cmd, "r"); \
2886- TEST_NE_P (f, NULL); \
2887- \
2888- while (fgets (buffer, BUFFER_SIZE, f)) { \
2889- size_t l = strlen (buffer)-1; \
2890- \
2891- if ( buffer[l] == '\n') \
2892- buffer[l] = '\0'; \
2893- ret = nih_str_array_add (result, parent, len, \
2894- buffer); \
2895- TEST_NE_P (ret, NULL); \
2896- } \
2897- \
2898- TEST_NE (pclose (f), -1); \
2899-}
2900-
2901-/**
2902- * CREATE_FILE:
2903- *
2904- * @dirname: directory name (assumed to already exist),
2905- * @name: name of file to create (no leading slash),
2906- * @contents: string contents of @name.
2907- *
2908- * Create a file in the specified directory with the specified
2909- * contents.
2910- *
2911- * Notes: A newline character is added in the case where @contents does
2912- * not end with one.
2913- **/
2914-#define CREATE_FILE(dirname, name, contents) \
2915-{ \
2916- FILE *f; \
2917- char filename[PATH_MAX]; \
2918- \
2919- assert (dirname[0]); \
2920- assert (name[0]); \
2921- \
2922- strcpy (filename, dirname); \
2923- if ( name[0] != '/' ) \
2924- strcat (filename, "/"); \
2925- strcat (filename, name); \
2926- f = fopen (filename, "w"); \
2927- TEST_NE_P (f, NULL); \
2928- fprintf (f, "%s", contents); \
2929- if ( contents[strlen(contents)-1] != '\n') \
2930- fprintf (f, "\n"); \
2931- fclose (f); \
2932-}
2933-
2934-/**
2935- * DELETE_FILE:
2936- *
2937- * @dirname: directory in which file to delete exists,
2938- * @name: name of file in @dirname to delete.
2939- *
2940- * Delete specified file.
2941- *
2942- **/
2943-#define DELETE_FILE(dirname, name) \
2944-{ \
2945- char filename[PATH_MAX]; \
2946- \
2947- assert (dirname[0]); \
2948- assert (name[0]); \
2949- \
2950- strcpy (filename, dirname); \
2951- if ( name[0] != '/' ) \
2952- strcat (filename, "/"); \
2953- strcat (filename, name); \
2954- \
2955- TEST_EQ (unlink (filename), 0); \
2956-}
2957-
2958-/**
2959- * _WAIT_FOR_FILE():
2960- *
2961- * @path: full path to file to look for,
2962- * @sleep_secs: number of seconds to sleep per loop,
2963- * @loops: number of times to check for file.
2964- *
2965- * Wait for a reasonable period of time for @path to be created.
2966- *
2967- * Abort if file does not appear within (sleep_secs * loops) seconds.
2968- *
2969- * XXX:WARNING: this is intrinsically racy since although the file has
2970- * been _created_, it has not necessarily been fully written at the
2971- * point this macro signifies success. For that we need inotify or
2972- * similar.
2973- **/
2974-#define _WAIT_FOR_FILE(path, sleep_secs, loops) \
2975-{ \
2976- int ok; \
2977- struct stat statbuf; \
2978- \
2979- assert (path[0]); \
2980- \
2981- /* Wait for log to be created */ \
2982- ok = FALSE; \
2983- for (int i = 0; i < loops; i++) { \
2984- sleep (sleep_secs); \
2985- if (! stat (logfile, &statbuf)) { \
2986- ok = TRUE; \
2987- break; \
2988- } \
2989- } \
2990- TEST_EQ (ok, TRUE); \
2991-}
2992-
2993-/**
2994- * WAIT_FOR_FILE():
2995- *
2996- * @path: full path to file to look for.
2997- *
2998- * Wait for a "reasonable period of time" for @path to be created.
2999- *
3000- * Abort if file does not appear within.
3001- **/
3002-#define WAIT_FOR_FILE(path) \
3003- _WAIT_FOR_FILE (path, 1, 5)
3004-
3005-/**
3006- * TEST_STR_MATCH:
3007- * @_string: string to check,
3008- * @_pattern: pattern to expect.
3009- *
3010- * Check that @_string matches the glob pattern @_pattern, which
3011- * should include the terminating newline if one is expected.
3012- *
3013- * Notes: Analagous to TEST_FILE_MATCH().
3014- **/
3015-#define TEST_STR_MATCH(_string, _pattern) \
3016- do { \
3017- if (fnmatch ((_pattern), _string, 0)) \
3018- TEST_FAILED ("wrong string value, " \
3019- "expected '%s' got '%s'", \
3020- (_pattern), _string); \
3021- } while (0)
3022-
3023-/**
3024- * _TEST_STR_ARRAY_CONTAINS:
3025- *
3026- * @_array: string array,
3027- * @_pattern: pattern to expect,
3028- * @_invert: invert meaning.
3029- *
3030- * Check that atleast 1 element in @_array matches @_pattern.
3031- *
3032- * If @_invert is TRUE, ensure @_pattern is _NOT_ found in @_array.
3033- **/
3034-#define _TEST_STR_ARRAY_CONTAINS(_array, _pattern, _invert) \
3035- do { \
3036- char **p; \
3037- int got = FALSE; \
3038- \
3039- for (p = _array; p && *p; p++) { \
3040- \
3041- if (! fnmatch ((_pattern), *p, 0)) { \
3042- got = TRUE; \
3043- break; \
3044- } \
3045- } \
3046- \
3047- if (_invert) { \
3048- if (got) { \
3049- TEST_FAILED ("wrong content in array " \
3050- "%p (%s), '%s' found unexpectedly", \
3051- (_array), #_array, (_pattern)); \
3052- } \
3053- } else { \
3054- if (! got) { \
3055- TEST_FAILED ("wrong content in array " \
3056- "%p (%s), '%s' not found", \
3057- (_array), #_array, (_pattern)); \
3058- } \
3059- } \
3060- } while (0)
3061-
3062-/**
3063- * _TEST_FILE_CONTAINS:
3064- * @_file: FILE to read from,
3065- * @_pattern: pattern to expect,
3066- * @_invert: invert meaning.
3067- *
3068- * Check that any subsequent line in file @_file matches the glob pattern
3069- * @_pattern, which should include the terminating newline if one is expected.
3070- *
3071- * If @_invert is TRUE, ensure @_pattern is _NOT_ found in @_file.
3072- **/
3073-#define _TEST_FILE_CONTAINS(_file, _pattern, _invert) \
3074- do { \
3075- char buffer[1024]; \
3076- int got = FALSE; \
3077- int ret; \
3078- while (fgets (buffer, sizeof (buffer), _file)) { \
3079- \
3080- ret = fnmatch ((_pattern), buffer, 0); \
3081- \
3082- if (! ret) { \
3083- got = TRUE; \
3084- break; \
3085- } \
3086- } \
3087- \
3088- if (_invert) { \
3089- if (got) { \
3090- TEST_FAILED ("wrong content in file " \
3091- "%p (%s), '%s' found unexpectedly", \
3092- (_file), #_file, (_pattern)); \
3093- } \
3094- } else { \
3095- if (! got) { \
3096- TEST_FAILED ("wrong content in file " \
3097- "%p (%s), '%s' not found", \
3098- (_file), #_file, (_pattern)); \
3099- } \
3100- } \
3101- } while (0)
3102-
3103-
3104-/**
3105- * TEST_FILE_CONTAINS:
3106- * @_file: FILE to read from,
3107- * @_pattern: pattern to expect.
3108- *
3109- * Check that any subsequent line in file @_file matches the glob pattern
3110- * @_pattern, which should include the terminating newline if one is expected.
3111- *
3112- **/
3113-#define TEST_FILE_CONTAINS(_file, _pattern) \
3114- _TEST_FILE_CONTAINS(_file, _pattern, FALSE)
3115-
3116-/**
3117- * TEST_FILE_NOT_CONTAINS:
3118- * @_file: FILE to read from,
3119- * @_pattern: pattern NOT to expect.
3120- *
3121- * Check that no subsequent line in file @_file does NOT match the glob pattern
3122- * @_pattern, which should include the terminating newline if one is expected.
3123- *
3124- **/
3125-#define TEST_FILE_NOT_CONTAINS(_file, _pattern) \
3126- _TEST_FILE_CONTAINS(_file, _pattern, TRUE)
3127-
3128-/**
3129- * TEST_STR_ARRAY_CONTAINS:
3130- *
3131- * @_array: string array,
3132- * @_pattern: pattern to expect.
3133- *
3134- * Check that atleast 1 element in @_array matches @_pattern.
3135- **/
3136-#define TEST_STR_ARRAY_CONTAINS(_array, _pattern) \
3137- _TEST_STR_ARRAY_CONTAINS (_array, _pattern, FALSE)
3138-
3139-/**
3140- * TEST_STR_ARRAY_NOT_CONTAINS:
3141- *
3142- * @_array: string array,
3143- * @_pattern: pattern to expect.
3144- *
3145- * Check that no element in @_array matches @_pattern.
3146- **/
3147-#define TEST_STR_ARRAY_NOT_CONTAINS(_array, _pattern) \
3148- _TEST_STR_ARRAY_CONTAINS (_array, _pattern, TRUE)
3149-
3150-/* TRUE to denote that Upstart is running in user session mode
3151- * (FALSE to denote it's using the users D-Bus session bus).
3152- */
3153-int test_user_mode = FALSE;
3154-
3155-/**
3156- * set_upstart_session:
3157- *
3158- * Attempt to "enter" an Upstart session by setting UPSTART_SESSION to
3159- * the value of the currently running session.
3160- *
3161- * It is only legitimate to call this function if you have previously
3162- * started a Session Init process.
3163- *
3164- * Limitations: Assumes that at most 1 session is running.
3165- *
3166- * Returns: TRUE if it was possible to enter the currently running
3167- * Upstart session, else FALSE.
3168- **/
3169-int
3170-set_upstart_session (void)
3171-{
3172- char *value;
3173- nih_local char *cmd = NULL;
3174- nih_local char **output = NULL;
3175- size_t lines = 0;
3176- int got = FALSE;
3177- int i;
3178-
3179- /* XXX: arbitrary value */
3180- int loops = 5;
3181-
3182- /* list-sessions relies on this */
3183- if (! getenv ("XDG_RUNTIME_DIR"))
3184- return FALSE;
3185-
3186- cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
3187- TEST_NE_P (cmd, NULL);
3188-
3189- /* We expect the list-sessions command to return a valid session
3190- * within a reasonable period of time.
3191- */
3192- for (i = 0; i < loops; i++) {
3193- sleep (1);
3194-
3195- RUN_COMMAND (NULL, cmd, &output, &lines);
3196- if (lines != 1)
3197- continue;
3198-
3199- /* No pid in output */
3200- if (! isdigit(output[0][0]))
3201- continue;
3202-
3203- /* look for separator between pid and value of
3204- * UPSTART_SESSION.
3205- */
3206- value = strstr (output[0], " ");
3207- if (! value)
3208- continue;
3209-
3210- /* jump over space */
3211- value += 1;
3212- if (! value)
3213- continue;
3214-
3215- /* No socket address */
3216- if (strstr (value, "unix:abstract") == value) {
3217- got = TRUE;
3218- break;
3219- }
3220- }
3221-
3222- if (got != TRUE)
3223- return FALSE;
3224-
3225- assert0 (setenv ("UPSTART_SESSION", value, 1));
3226-
3227- return TRUE;
3228-}
3229-
3230-/**
3231- * selfpipe:
3232- *
3233- * Used to allow a timed process wait.
3234- **/
3235-int selfpipe[2] = { -1, -1 };
3236-
3237-void
3238-selfpipe_write (int n)
3239-{
3240- assert (selfpipe[1] != -1);
3241-
3242- (void)write (selfpipe[1], "", 1);
3243-}
3244-
3245-/**
3246- * selfpipe_setup:
3247- *
3248- * Arrange for SIGCHLD to write to selfpipe such that we can select(2)
3249- * on child process status changes.
3250- **/
3251-void
3252-selfpipe_setup (void)
3253-{
3254- static struct sigaction act;
3255- int read_flags;
3256- int write_flags;
3257-
3258- assert (selfpipe[0] == -1);
3259-
3260- assert (! pipe (selfpipe));
3261-
3262- /* Set non-blocking */
3263- read_flags = fcntl (selfpipe[0], F_GETFL);
3264- write_flags = fcntl (selfpipe[1], F_GETFL);
3265-
3266- read_flags |= O_NONBLOCK;
3267- write_flags |= O_NONBLOCK;
3268-
3269- assert (fcntl (selfpipe[0], F_SETFL, read_flags) == 0);
3270- assert (fcntl (selfpipe[1], F_SETFL, write_flags) == 0);
3271-
3272- /* Don't leak */
3273- assert (fcntl (selfpipe[0], F_SETFD, FD_CLOEXEC) == 0);
3274- assert (fcntl (selfpipe[1], F_SETFD, FD_CLOEXEC) == 0);
3275-
3276- memset (&act, 0, sizeof (act));
3277-
3278- /* register SIGCHLD handler which will cause pipe write when child
3279- * changes state.
3280- */
3281- act.sa_handler = selfpipe_write;
3282-
3283- sigaction (SIGCHLD, &act, NULL);
3284-}
3285-
3286-/**
3287- * timed_waitpid:
3288- *
3289- * @pid: pid to wait for,
3290- * @timeout: seconds to wait for @pid to change state.
3291- *
3292- * Simplified waitpid(2) with timeout using a pipe to allow select(2)
3293- * with timeout to be used to wait for process state change.
3294- **/
3295-pid_t
3296-timed_waitpid (pid_t pid, time_t timeout)
3297-{
3298- static char buffer[1];
3299- fd_set read_fds;
3300- struct timeval tv;
3301- int status;
3302- int nfds;
3303- int ret;
3304- pid_t ret2;
3305-
3306- assert (pid);
3307- assert (timeout);
3308-
3309- if (selfpipe[0] == -1)
3310- selfpipe_setup ();
3311-
3312- FD_ZERO (&read_fds);
3313- FD_SET (selfpipe[0], &read_fds);
3314-
3315- nfds = 1 + selfpipe[0];
3316-
3317- tv.tv_sec = timeout;
3318- tv.tv_usec = 0;
3319-
3320- /* wait for some activity */
3321- ret = select (nfds, &read_fds, NULL, NULL, &tv);
3322-
3323- if (! ret)
3324- /* timed out */
3325- return 0;
3326-
3327- /* discard any data written to pipe */
3328- while (read (selfpipe[0], buffer, sizeof (buffer)) > 0)
3329- ;
3330-
3331- while (TRUE) {
3332- /* wait for status change or error */
3333- ret2 = waitpid (pid, &status, WNOHANG);
3334-
3335- if (ret2 < 0)
3336- return -1;
3337-
3338- if (! ret2)
3339- /* give child a chance to change state */
3340- sleep (1);
3341-
3342- if (ret2) {
3343- if (WIFEXITED (status))
3344- return ret2;
3345-
3346- /* unexpected status change */
3347- return -1;
3348- }
3349- }
3350-}
3351-
3352-
3353-/**
3354- * get_initctl():
3355- *
3356- * Determine a suitable initctl command-line for testing purposes.
3357- *
3358- * Returns: Static string representing full path to initctl binary with
3359- * default option to allow communication with an Upstart started using
3360- * START_UPSTART().
3361- **/
3362-char *
3363-get_initctl (void)
3364-{
3365- static char path[PATH_MAX + 1024] = { 0 };
3366- int ret;
3367-
3368- ret = sprintf (path, "%s %s",
3369- INITCTL_BINARY,
3370- test_user_mode
3371- ? "--user"
3372- : "--session");
3373-
3374- assert (ret > 0);
3375-
3376- return path;
3377-}
3378-
3379-/*
3380- * _start_upstart:
3381- *
3382- * @pid: PID of running instance,
3383- * @user: TRUE if upstart will run in User Session mode (FALSE to
3384- * use the users D-Bus session bus),
3385- * @args: optional list of arguments to specify.
3386- *
3387- * Start an instance of Upstart.
3388- *
3389- * If the instance fails to start, abort(3) is called.
3390- **/
3391-void
3392-_start_upstart (pid_t *pid, int user, char * const *args)
3393-{
3394- nih_local char **argv = NULL;
3395- sigset_t child_set, orig_set;
3396-
3397- assert (pid);
3398-
3399- argv = NIH_MUST (nih_str_array_new (NULL));
3400-
3401- NIH_MUST (nih_str_array_add (&argv, NULL, NULL,
3402- UPSTART_BINARY));
3403-
3404- if (args)
3405- NIH_MUST (nih_str_array_append (&argv, NULL, NULL, args));
3406-
3407- sigfillset (&child_set);
3408- sigprocmask (SIG_BLOCK, &child_set, &orig_set);
3409-
3410- TEST_NE (*pid = fork (), -1);
3411-
3412- if (! *pid) {
3413- int fd;
3414- nih_signal_reset ();
3415- sigprocmask (SIG_SETMASK, &orig_set, NULL);
3416-
3417- if (! getenv ("UPSTART_TEST_VERBOSE")) {
3418- fd = open ("/dev/null", O_RDWR);
3419- assert (fd >= 0);
3420- assert (dup2 (fd, STDIN_FILENO) != -1);
3421- assert (dup2 (fd, STDOUT_FILENO) != -1);
3422- assert (dup2 (fd, STDERR_FILENO) != -1);
3423- }
3424-
3425- assert (execv (argv[0], argv) != -1);
3426- }
3427-
3428- sigprocmask (SIG_SETMASK, &orig_set, NULL);
3429- wait_for_upstart (user);
3430-}
3431-
3432-/**
3433- * start_upstart_common:
3434- *
3435- * @pid: PID of running instance,
3436- * @user: TRUE if upstart should run in User Session mode (FALSE to
3437- * use the users D-Bus session bus),
3438- * @confdir: full path to configuration directory,
3439- * @logdir: full path to log directory,
3440- * @extra: optional extra arguments.
3441- *
3442- * Wrapper round _start_upstart() which specifies common options.
3443- **/
3444-void
3445-start_upstart_common (pid_t *pid, int user, const char *confdir,
3446- const char *logdir, char * const *extra)
3447-{
3448- nih_local char **args = NULL;
3449-
3450- assert (pid);
3451-
3452- args = NIH_MUST (nih_str_array_new (NULL));
3453-
3454- if (user) {
3455- NIH_MUST (nih_str_array_add (&args, NULL, NULL,
3456- "--user"));
3457- test_user_mode = TRUE;
3458- } else {
3459- TEST_TRUE (getenv ("DBUS_SESSION_BUS_ADDRESS"));
3460- NIH_MUST (nih_str_array_add (&args, NULL, NULL,
3461- "--session"));
3462- }
3463-
3464- NIH_MUST (nih_str_array_add (&args, NULL, NULL,
3465- "--no-startup-event"));
3466-
3467- NIH_MUST (nih_str_array_add (&args, NULL, NULL,
3468- "--no-sessions"));
3469-
3470- NIH_MUST (nih_str_array_add (&args, NULL, NULL,
3471- "--no-inherit-env"));
3472-
3473- if (confdir) {
3474- NIH_MUST (nih_str_array_add (&args, NULL, NULL,
3475- "--confdir"));
3476- NIH_MUST (nih_str_array_add (&args, NULL, NULL,
3477- confdir));
3478- }
3479-
3480- if (logdir) {
3481- NIH_MUST (nih_str_array_add (&args, NULL, NULL,
3482- "--logdir"));
3483- NIH_MUST (nih_str_array_add (&args, NULL, NULL,
3484- logdir));
3485- }
3486-
3487- if (extra)
3488- NIH_MUST (nih_str_array_append (&args, NULL, NULL, extra));
3489-
3490- _start_upstart (pid, user, args);
3491-}
3492-
3493-/**
3494- * start_upstart:
3495- *
3496- * @pid: PID of running instance.
3497- *
3498- * Wrapper round _start_upstart() which just runs an instance with no
3499- * options.
3500- **/
3501-void
3502-start_upstart (pid_t *pid)
3503-{
3504- start_upstart_common (pid, FALSE, NULL, NULL, NULL);
3505-}
3506-
3507-/**
3508- * job_to_pid:
3509- *
3510- * @job: job name.
3511- *
3512- * Determine pid of running job.
3513- *
3514- * WARNING: it is the callers responsibility to ensure that
3515- * @job is still running when this function is called!!
3516- *
3517- * Returns: pid of job, or -1 if not found.
3518- **/
3519-pid_t
3520-job_to_pid (const char *job)
3521-{
3522- pid_t pid;
3523- regex_t regex;
3524- regmatch_t regmatch[2];
3525- int ret;
3526- nih_local char *cmd = NULL;
3527- nih_local char *pattern = NULL;
3528- size_t lines;
3529- char **status;
3530- nih_local char *str_pid = NULL;
3531-
3532- assert (job);
3533-
3534- pattern = NIH_MUST (nih_sprintf
3535- (NULL, "^\\b%s\\b .*, process ([0-9]+)", job));
3536-
3537- cmd = NIH_MUST (nih_sprintf (NULL, "%s status %s 2>&1",
3538- get_initctl (), job));
3539- RUN_COMMAND (NULL, cmd, &status, &lines);
3540- TEST_EQ (lines, 1);
3541-
3542- ret = regcomp (&regex, pattern, REG_EXTENDED);
3543- assert0 (ret);
3544-
3545- ret = regexec (&regex, status[0], 2, regmatch, 0);
3546- if (ret == REG_NOMATCH) {
3547- ret = -1;
3548- goto out;
3549- }
3550- assert0 (ret);
3551-
3552- if (regmatch[1].rm_so == -1 || regmatch[1].rm_eo == -1) {
3553- ret = -1;
3554- goto out;
3555- }
3556-
3557- /* extract the pid */
3558- NIH_MUST (nih_strncat (&str_pid, NULL,
3559- &status[0][regmatch[1].rm_so],
3560- regmatch[1].rm_eo - regmatch[1].rm_so));
3561-
3562- nih_free (status);
3563-
3564- pid = (pid_t)atol (str_pid);
3565-
3566- /* check it's running */
3567- ret = kill (pid, 0);
3568- if (! ret)
3569- ret = pid;
3570-
3571-out:
3572- regfree (&regex);
3573- return ret;
3574-}
3575+#include "test_util.h"
3576
3577 extern int use_dbus;
3578 extern int user_mode;
3579@@ -11744,12 +10869,6 @@
3580 dbus_shutdown ();
3581 }
3582
3583-int
3584-strcmp_compar (const void *a, const void *b)
3585-{
3586- return strcmp(*(char * const *)a, *(char * const *)b);
3587-}
3588-
3589 void
3590 test_list (void)
3591 {
3592@@ -12071,7 +11190,7 @@
3593 TEST_FEATURE ("with no instances and XDG_RUNTIME_DIR unset");
3594
3595 assert0 (unsetenv ("XDG_RUNTIME_DIR"));
3596- cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
3597+ cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", get_initctl_binary ());
3598 TEST_NE_P (cmd, NULL);
3599 RUN_COMMAND (NULL, cmd, &output, &lines);
3600 TEST_EQ (lines, 1);
3601@@ -12083,7 +11202,7 @@
3602
3603 TEST_EQ (setenv ("XDG_RUNTIME_DIR", dirname, 1), 0);
3604
3605- cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
3606+ cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", get_initctl_binary ());
3607 TEST_NE_P (cmd, NULL);
3608 RUN_COMMAND (NULL, cmd, &output, &lines);
3609 TEST_EQ (lines, 0);
3610@@ -12100,8 +11219,7 @@
3611
3612 start_upstart_common (&upstart_pid, TRUE, NULL, NULL, NULL);
3613
3614- session_file = nih_sprintf (NULL, "%s/upstart/sessions/%d.session",
3615- dirname, (int)upstart_pid);
3616+ session_file = get_session_file (dirname, upstart_pid);
3617
3618 /* session file should now have been created by Upstart */
3619 TEST_EQ (stat (session_file, &statbuf), 0);
3620@@ -12123,7 +11241,7 @@
3621
3622 expected = nih_sprintf (NULL, "%d %s", (int)upstart_pid, value);
3623
3624- cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
3625+ cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", get_initctl_binary ());
3626 TEST_NE_P (cmd, NULL);
3627 RUN_COMMAND (NULL, cmd, &output, &lines);
3628 TEST_EQ (lines, 1);
3629@@ -17379,7 +16497,7 @@
3630 TEST_DBUS (dbus_pid);
3631 start_upstart_common (&upstart_pid, TRUE, confdir, logdir, NULL);
3632
3633- cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
3634+ cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", get_initctl_binary ());
3635 TEST_NE_P (cmd, NULL);
3636 RUN_COMMAND (NULL, cmd, &output, &lines);
3637 TEST_EQ (lines, 1);

Subscribers

People subscribed via source and target branches