54
54
#include "com.ubuntu.Upstart.h"
57
#ifndef UPSTART_BINARY
58
#error unable to find init binary as UPSTART_BINARY not defined
59
#endif /* UPSTART_BINARY */
61
#ifndef INITCTL_BINARY
62
#error unable to find initctl binary as INITCTL_BINARY not defined
63
#endif /* INITCTL_BINARY */
65
#define BUFFER_SIZE 1024
68
* TEST_QUIESCE_WAIT_PHASE:
70
* Maximum time we expect upstart to wait in the QUIESCE_PHASE_WAIT
73
#define TEST_EXIT_TIME 5
76
* TEST_QUIESCE_KILL_PHASE:
78
* Maximum time we expect upstart to wait in the QUIESCE_PHASE_KILL
81
#define TEST_QUIESCE_KILL_PHASE 5
83
#define TEST_QUIESCE_TOTAL_WAIT_TIME (TEST_EXIT_TIME + TEST_QUIESCE_KILL_PHASE)
85
/* A 'reasonable' path, but which also contains a marker at the end so
86
* we know when we're looking at a PATH these tests have set.
88
#define TEST_INITCTL_DEFAULT_PATH "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/wibble"
90
/* Default value for TERM if not already set */
91
#define TEST_INITCTL_DEFAULT_TERM "linux"
94
set_upstart_session (void);
99
* @user: TRUE if waiting for a Session Init (which uses a private bus
100
* rather than the session bus), else FALSE.
102
* Wait for Upstart to appear on D-Bus denoting its completion of
103
* initialisation. Wait time is somewhat arbitrary (but more
107
wait_for_upstart (int user)
109
nih_local NihDBusProxy *upstart = NULL;
110
DBusConnection *connection;
115
/* XXX: arbitrary value */
119
set_upstart_session ();
120
address = getenv ("UPSTART_SESSION");
122
address = getenv ("DBUS_SESSION_BUS_ADDRESS");
130
connection = nih_dbus_connect (address, NULL);
133
err = nih_error_get ();
138
upstart = nih_dbus_proxy_new (NULL, connection,
144
err = nih_error_get ();
146
dbus_connection_unref (connection);
152
TEST_EQ (running, TRUE);
158
* @pid: pid_t that will contain pid of running instance on success,
159
* @user_mode: TRUE for Session Init (or FALSE to use D-Bus
162
* Start an instance of Upstart and return PID in @pid.
164
#define START_UPSTART(pid, user_mode) \
165
start_upstart_common (&(pid), user_mode, NULL, NULL, NULL)
170
* @pid: pid of upstart to kill,
171
* @signo: signal number to send to @pid,
172
* @wait: TRUE to wait for @pid to die.
174
* Send specified signal to upstart process @pid.
176
#define KILL_UPSTART(pid, signo, wait) \
182
assert0 (kill (pid, signo)); \
184
TEST_EQ (waitpid (pid, &status, 0), pid); \
185
TEST_TRUE (WIFSIGNALED (status)); \
186
TEST_TRUE (WTERMSIG (status) == signo); \
188
/* reset since a subsequent start could specify a different \
191
test_user_mode = FALSE; \
197
* @pid: pid of upstart to kill.
199
* Stop upstart process @pid.
201
#define STOP_UPSTART(pid) \
202
KILL_UPSTART (pid, SIGKILL, TRUE)
207
* @pid: pid of upstart.
209
* Force upstart to perform a re-exec.
211
#define REEXEC_UPSTART(pid) \
212
KILL_UPSTART (pid, SIGTERM, FALSE); \
213
wait_for_upstart (FALSE)
218
* @parent: pointer to parent object,
219
* @cmd: string representing command to run,
220
* @result: "char ***" pointer which will contain an array of string
221
* values corresponding to lines of standard output generated by @cmd,
222
* @len: size_t pointer which will be set to length of @result.
224
* Run a command and return its standard output. It is the callers
225
* responsibility to free @result. Errors from running @cmd are fatal.
227
* Note: trailing '\n' characters are removed in returned command
230
#define RUN_COMMAND(parent, cmd, result, len) \
233
char buffer[BUFFER_SIZE]; \
238
*(result) = nih_str_array_new (parent); \
239
TEST_NE_P (*result, NULL); \
242
f = popen (cmd, "r"); \
243
TEST_NE_P (f, NULL); \
245
while (fgets (buffer, BUFFER_SIZE, f)) { \
246
size_t l = strlen (buffer)-1; \
248
if ( buffer[l] == '\n') \
250
ret = nih_str_array_add (result, parent, len, \
252
TEST_NE_P (ret, NULL); \
255
TEST_NE (pclose (f), -1); \
261
* @dirname: directory name (assumed to already exist),
262
* @name: name of file to create (no leading slash),
263
* @contents: string contents of @name.
265
* Create a file in the specified directory with the specified
268
* Notes: A newline character is added in the case where @contents does
271
#define CREATE_FILE(dirname, name, contents) \
274
char filename[PATH_MAX]; \
276
assert (dirname[0]); \
279
strcpy (filename, dirname); \
280
if ( name[0] != '/' ) \
281
strcat (filename, "/"); \
282
strcat (filename, name); \
283
f = fopen (filename, "w"); \
284
TEST_NE_P (f, NULL); \
285
fprintf (f, "%s", contents); \
286
if ( contents[strlen(contents)-1] != '\n') \
294
* @dirname: directory in which file to delete exists,
295
* @name: name of file in @dirname to delete.
297
* Delete specified file.
300
#define DELETE_FILE(dirname, name) \
302
char filename[PATH_MAX]; \
304
assert (dirname[0]); \
307
strcpy (filename, dirname); \
308
if ( name[0] != '/' ) \
309
strcat (filename, "/"); \
310
strcat (filename, name); \
312
TEST_EQ (unlink (filename), 0); \
318
* @path: full path to file to look for,
319
* @sleep_secs: number of seconds to sleep per loop,
320
* @loops: number of times to check for file.
322
* Wait for a reasonable period of time for @path to be created.
324
* Abort if file does not appear within (sleep_secs * loops) seconds.
326
* XXX:WARNING: this is intrinsically racy since although the file has
327
* been _created_, it has not necessarily been fully written at the
328
* point this macro signifies success. For that we need inotify or
331
#define _WAIT_FOR_FILE(path, sleep_secs, loops) \
334
struct stat statbuf; \
338
/* Wait for log to be created */ \
340
for (int i = 0; i < loops; i++) { \
341
sleep (sleep_secs); \
342
if (! stat (logfile, &statbuf)) { \
347
TEST_EQ (ok, TRUE); \
353
* @path: full path to file to look for.
355
* Wait for a "reasonable period of time" for @path to be created.
357
* Abort if file does not appear within.
359
#define WAIT_FOR_FILE(path) \
360
_WAIT_FOR_FILE (path, 1, 5)
364
* @_string: string to check,
365
* @_pattern: pattern to expect.
367
* Check that @_string matches the glob pattern @_pattern, which
368
* should include the terminating newline if one is expected.
370
* Notes: Analagous to TEST_FILE_MATCH().
372
#define TEST_STR_MATCH(_string, _pattern) \
374
if (fnmatch ((_pattern), _string, 0)) \
375
TEST_FAILED ("wrong string value, " \
376
"expected '%s' got '%s'", \
377
(_pattern), _string); \
381
* _TEST_STR_ARRAY_CONTAINS:
383
* @_array: string array,
384
* @_pattern: pattern to expect,
385
* @_invert: invert meaning.
387
* Check that atleast 1 element in @_array matches @_pattern.
389
* If @_invert is TRUE, ensure @_pattern is _NOT_ found in @_array.
391
#define _TEST_STR_ARRAY_CONTAINS(_array, _pattern, _invert) \
396
for (p = _array; p && *p; p++) { \
398
if (! fnmatch ((_pattern), *p, 0)) { \
406
TEST_FAILED ("wrong content in array " \
407
"%p (%s), '%s' found unexpectedly", \
408
(_array), #_array, (_pattern)); \
412
TEST_FAILED ("wrong content in array " \
413
"%p (%s), '%s' not found", \
414
(_array), #_array, (_pattern)); \
420
* _TEST_FILE_CONTAINS:
421
* @_file: FILE to read from,
422
* @_pattern: pattern to expect,
423
* @_invert: invert meaning.
425
* Check that any subsequent line in file @_file matches the glob pattern
426
* @_pattern, which should include the terminating newline if one is expected.
428
* If @_invert is TRUE, ensure @_pattern is _NOT_ found in @_file.
430
#define _TEST_FILE_CONTAINS(_file, _pattern, _invert) \
435
while (fgets (buffer, sizeof (buffer), _file)) { \
437
ret = fnmatch ((_pattern), buffer, 0); \
447
TEST_FAILED ("wrong content in file " \
448
"%p (%s), '%s' found unexpectedly", \
449
(_file), #_file, (_pattern)); \
453
TEST_FAILED ("wrong content in file " \
454
"%p (%s), '%s' not found", \
455
(_file), #_file, (_pattern)); \
462
* TEST_FILE_CONTAINS:
463
* @_file: FILE to read from,
464
* @_pattern: pattern to expect.
466
* Check that any subsequent line in file @_file matches the glob pattern
467
* @_pattern, which should include the terminating newline if one is expected.
470
#define TEST_FILE_CONTAINS(_file, _pattern) \
471
_TEST_FILE_CONTAINS(_file, _pattern, FALSE)
474
* TEST_FILE_NOT_CONTAINS:
475
* @_file: FILE to read from,
476
* @_pattern: pattern NOT to expect.
478
* Check that no subsequent line in file @_file does NOT match the glob pattern
479
* @_pattern, which should include the terminating newline if one is expected.
482
#define TEST_FILE_NOT_CONTAINS(_file, _pattern) \
483
_TEST_FILE_CONTAINS(_file, _pattern, TRUE)
486
* TEST_STR_ARRAY_CONTAINS:
488
* @_array: string array,
489
* @_pattern: pattern to expect.
491
* Check that atleast 1 element in @_array matches @_pattern.
493
#define TEST_STR_ARRAY_CONTAINS(_array, _pattern) \
494
_TEST_STR_ARRAY_CONTAINS (_array, _pattern, FALSE)
497
* TEST_STR_ARRAY_NOT_CONTAINS:
499
* @_array: string array,
500
* @_pattern: pattern to expect.
502
* Check that no element in @_array matches @_pattern.
504
#define TEST_STR_ARRAY_NOT_CONTAINS(_array, _pattern) \
505
_TEST_STR_ARRAY_CONTAINS (_array, _pattern, TRUE)
507
/* TRUE to denote that Upstart is running in user session mode
508
* (FALSE to denote it's using the users D-Bus session bus).
510
int test_user_mode = FALSE;
513
* set_upstart_session:
515
* Attempt to "enter" an Upstart session by setting UPSTART_SESSION to
516
* the value of the currently running session.
518
* It is only legitimate to call this function if you have previously
519
* started a Session Init process.
521
* Limitations: Assumes that at most 1 session is running.
523
* Returns: TRUE if it was possible to enter the currently running
524
* Upstart session, else FALSE.
527
set_upstart_session (void)
530
nih_local char *cmd = NULL;
531
nih_local char **output = NULL;
536
/* XXX: arbitrary value */
539
/* list-sessions relies on this */
540
if (! getenv ("XDG_RUNTIME_DIR"))
543
cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
544
TEST_NE_P (cmd, NULL);
546
/* We expect the list-sessions command to return a valid session
547
* within a reasonable period of time.
549
for (i = 0; i < loops; i++) {
552
RUN_COMMAND (NULL, cmd, &output, &lines);
556
/* No pid in output */
557
if (! isdigit(output[0][0]))
560
/* look for separator between pid and value of
563
value = strstr (output[0], " ");
567
/* jump over space */
572
/* No socket address */
573
if (strstr (value, "unix:abstract") == value) {
582
assert0 (setenv ("UPSTART_SESSION", value, 1));
590
* Used to allow a timed process wait.
592
int selfpipe[2] = { -1, -1 };
595
selfpipe_write (int n)
597
assert (selfpipe[1] != -1);
599
(void)write (selfpipe[1], "", 1);
605
* Arrange for SIGCHLD to write to selfpipe such that we can select(2)
606
* on child process status changes.
609
selfpipe_setup (void)
611
static struct sigaction act;
615
assert (selfpipe[0] == -1);
617
assert (! pipe (selfpipe));
619
/* Set non-blocking */
620
read_flags = fcntl (selfpipe[0], F_GETFL);
621
write_flags = fcntl (selfpipe[1], F_GETFL);
623
read_flags |= O_NONBLOCK;
624
write_flags |= O_NONBLOCK;
626
assert (fcntl (selfpipe[0], F_SETFL, read_flags) == 0);
627
assert (fcntl (selfpipe[1], F_SETFL, write_flags) == 0);
630
assert (fcntl (selfpipe[0], F_SETFD, FD_CLOEXEC) == 0);
631
assert (fcntl (selfpipe[1], F_SETFD, FD_CLOEXEC) == 0);
633
memset (&act, 0, sizeof (act));
635
/* register SIGCHLD handler which will cause pipe write when child
638
act.sa_handler = selfpipe_write;
640
sigaction (SIGCHLD, &act, NULL);
646
* @pid: pid to wait for,
647
* @timeout: seconds to wait for @pid to change state.
649
* Simplified waitpid(2) with timeout using a pipe to allow select(2)
650
* with timeout to be used to wait for process state change.
653
timed_waitpid (pid_t pid, time_t timeout)
655
static char buffer[1];
666
if (selfpipe[0] == -1)
670
FD_SET (selfpipe[0], &read_fds);
672
nfds = 1 + selfpipe[0];
677
/* wait for some activity */
678
ret = select (nfds, &read_fds, NULL, NULL, &tv);
684
/* discard any data written to pipe */
685
while (read (selfpipe[0], buffer, sizeof (buffer)) > 0)
689
/* wait for status change or error */
690
ret2 = waitpid (pid, &status, WNOHANG);
696
/* give child a chance to change state */
700
if (WIFEXITED (status))
703
/* unexpected status change */
713
* Determine a suitable initctl command-line for testing purposes.
715
* Returns: Static string representing full path to initctl binary with
716
* default option to allow communication with an Upstart started using
722
static char path[PATH_MAX + 1024] = { 0 };
725
ret = sprintf (path, "%s %s",
739
* @pid: PID of running instance,
740
* @user: TRUE if upstart will run in User Session mode (FALSE to
741
* use the users D-Bus session bus),
742
* @args: optional list of arguments to specify.
744
* Start an instance of Upstart.
746
* If the instance fails to start, abort(3) is called.
749
_start_upstart (pid_t *pid, int user, char * const *args)
751
nih_local char **argv = NULL;
752
sigset_t child_set, orig_set;
756
argv = NIH_MUST (nih_str_array_new (NULL));
758
NIH_MUST (nih_str_array_add (&argv, NULL, NULL,
762
NIH_MUST (nih_str_array_append (&argv, NULL, NULL, args));
764
sigfillset (&child_set);
765
sigprocmask (SIG_BLOCK, &child_set, &orig_set);
767
TEST_NE (*pid = fork (), -1);
772
sigprocmask (SIG_SETMASK, &orig_set, NULL);
774
if (! getenv ("UPSTART_TEST_VERBOSE")) {
775
fd = open ("/dev/null", O_RDWR);
777
assert (dup2 (fd, STDIN_FILENO) != -1);
778
assert (dup2 (fd, STDOUT_FILENO) != -1);
779
assert (dup2 (fd, STDERR_FILENO) != -1);
782
assert (execv (argv[0], argv) != -1);
785
sigprocmask (SIG_SETMASK, &orig_set, NULL);
786
wait_for_upstart (user);
790
* start_upstart_common:
792
* @pid: PID of running instance,
793
* @user: TRUE if upstart should run in User Session mode (FALSE to
794
* use the users D-Bus session bus),
795
* @confdir: full path to configuration directory,
796
* @logdir: full path to log directory,
797
* @extra: optional extra arguments.
799
* Wrapper round _start_upstart() which specifies common options.
802
start_upstart_common (pid_t *pid, int user, const char *confdir,
803
const char *logdir, char * const *extra)
805
nih_local char **args = NULL;
809
args = NIH_MUST (nih_str_array_new (NULL));
812
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
814
test_user_mode = TRUE;
816
TEST_TRUE (getenv ("DBUS_SESSION_BUS_ADDRESS"));
817
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
821
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
822
"--no-startup-event"));
824
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
827
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
828
"--no-inherit-env"));
831
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
833
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
838
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
840
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
845
NIH_MUST (nih_str_array_append (&args, NULL, NULL, extra));
847
_start_upstart (pid, user, args);
853
* @pid: PID of running instance.
855
* Wrapper round _start_upstart() which just runs an instance with no
859
start_upstart (pid_t *pid)
861
start_upstart_common (pid, FALSE, NULL, NULL, NULL);
869
* Determine pid of running job.
871
* WARNING: it is the callers responsibility to ensure that
872
* @job is still running when this function is called!!
874
* Returns: pid of job, or -1 if not found.
877
job_to_pid (const char *job)
881
regmatch_t regmatch[2];
883
nih_local char *cmd = NULL;
884
nih_local char *pattern = NULL;
887
nih_local char *str_pid = NULL;
891
pattern = NIH_MUST (nih_sprintf
892
(NULL, "^\\b%s\\b .*, process ([0-9]+)", job));
894
cmd = NIH_MUST (nih_sprintf (NULL, "%s status %s 2>&1",
895
get_initctl (), job));
896
RUN_COMMAND (NULL, cmd, &status, &lines);
899
ret = regcomp (®ex, pattern, REG_EXTENDED);
902
ret = regexec (®ex, status[0], 2, regmatch, 0);
903
if (ret == REG_NOMATCH) {
909
if (regmatch[1].rm_so == -1 || regmatch[1].rm_eo == -1) {
914
/* extract the pid */
915
NIH_MUST (nih_strncat (&str_pid, NULL,
916
&status[0][regmatch[1].rm_so],
917
regmatch[1].rm_eo - regmatch[1].rm_so));
921
pid = (pid_t)atol (str_pid);
923
/* check it's running */
56
#include "test_util_common.h"
933
58
extern int use_dbus;
934
59
extern int user_mode;