3
* test_util_common.c - common test utilities
5
* Copyright © 2012-2013 Canonical Ltd.
6
* Author: James Hunt <james.hunt@canonical.com>
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License version 2, as
10
* published by the Free Software Foundation.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License along
18
* with this program; if not, write to the Free Software Foundation, Inc.,
19
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
#endif /* HAVE_CONFIG_H */
28
#include <nih/string.h>
29
#include <nih/signal.h>
30
#include <nih/logging.h>
31
#include <nih-dbus/test_dbus.h>
33
#include <dbus/dbus.h>
39
#include <sys/types.h>
43
#include <nih-dbus/dbus_error.h>
44
#include <nih-dbus/dbus_connection.h>
45
#include <nih-dbus/dbus_proxy.h>
46
#include <nih-dbus/errors.h>
48
#include "dbus/upstart.h"
50
#include "test_util_common.h"
52
#ifndef UPSTART_BINARY
53
#error unable to find init binary as UPSTART_BINARY not defined
54
#endif /* UPSTART_BINARY */
56
#ifndef INITCTL_BINARY
57
#error unable to find initctl binary as INITCTL_BINARY not defined
58
#endif /* INITCTL_BINARY */
60
static void selfpipe_write (int n);
61
static void selfpipe_setup (void);
66
* @session_init_pid: pid of Session Init (which uses a private bus
67
* rather than the session bus), else 0.
69
* Wait for Upstart to appear on D-Bus denoting its completion of
70
* initialisation. Wait time is somewhat arbitrary (but more
74
wait_for_upstart (int session_init_pid)
76
nih_local NihDBusProxy *upstart = NULL;
77
DBusConnection *connection;
82
/* XXX: arbitrary value */
85
if (session_init_pid) {
86
TEST_TRUE (set_upstart_session (session_init_pid));
87
address = getenv ("UPSTART_SESSION");
89
address = getenv ("DBUS_SESSION_BUS_ADDRESS");
97
connection = nih_dbus_connect (address, NULL);
100
err = nih_error_get ();
105
upstart = nih_dbus_proxy_new (NULL, connection,
111
err = nih_error_get ();
113
dbus_connection_unref (connection);
119
TEST_EQ (running, TRUE);
122
/* TRUE to denote that Upstart is running in user session mode
123
* (FALSE to denote it's using the users D-Bus session bus).
125
int test_user_mode = FALSE;
128
* set_upstart_session:
130
* @session_init_pid: pid of Session Init.
132
* Attempt to "enter" an Upstart session by setting UPSTART_SESSION to
133
* the value of the session running under pid @session_init_pid.
135
* Returns: TRUE if it was possible to enter the currently running
136
* Upstart session, else FALSE.
139
set_upstart_session (pid_t session_init_pid)
142
nih_local char *cmd = NULL;
143
nih_local char **output = NULL;
149
/* XXX: arbitrary value */
152
nih_assert (session_init_pid);
154
/* list-sessions relies on this */
155
if (! getenv ("XDG_RUNTIME_DIR"))
158
cmd = nih_sprintf (NULL, "%s list-sessions 2>&1", INITCTL_BINARY);
159
TEST_NE_P (cmd, NULL);
161
/* We expect the list-sessions command to return a valid session
162
* within a reasonable period of time.
164
for (i = 0; i < loops; i++) {
167
RUN_COMMAND (NULL, cmd, &output, &lines);
172
/* Look for the specific session */
173
for (size_t line = 0; line < lines; lines++) {
175
/* No pid in output */
176
if (! isdigit(output[line][0]))
179
pid = (pid_t)atoi(output[line]);
180
nih_assert (pid > 0);
182
if (pid != session_init_pid)
185
/* look for separator between pid and value of
188
value = strstr (output[0], " ");
192
/* jump over space */
197
/* No socket address */
198
if (strstr (value, "unix:abstract") == value) {
210
assert0 (setenv ("UPSTART_SESSION", value, 1));
218
* Used to allow a timed process wait.
220
static int selfpipe[2] = { -1, -1 };
223
selfpipe_write (int n)
225
assert (selfpipe[1] != -1);
227
TEST_EQ (write (selfpipe[1], "", 1), 1);
233
* Arrange for SIGCHLD to write to selfpipe such that we can select(2)
234
* on child process status changes.
237
selfpipe_setup (void)
239
static struct sigaction act;
243
assert (selfpipe[0] == -1);
245
assert (! pipe (selfpipe));
247
/* Set non-blocking */
248
read_flags = fcntl (selfpipe[0], F_GETFL);
249
write_flags = fcntl (selfpipe[1], F_GETFL);
251
read_flags |= O_NONBLOCK;
252
write_flags |= O_NONBLOCK;
254
assert (fcntl (selfpipe[0], F_SETFL, read_flags) == 0);
255
assert (fcntl (selfpipe[1], F_SETFL, write_flags) == 0);
258
assert (fcntl (selfpipe[0], F_SETFD, FD_CLOEXEC) == 0);
259
assert (fcntl (selfpipe[1], F_SETFD, FD_CLOEXEC) == 0);
261
memset (&act, 0, sizeof (act));
263
/* register SIGCHLD handler which will cause pipe write when child
266
act.sa_handler = selfpipe_write;
268
sigaction (SIGCHLD, &act, NULL);
274
* @pid: pid to wait for,
275
* @timeout: seconds to wait for @pid to change state.
277
* Simplified waitpid(2) with timeout using a pipe to allow select(2)
278
* with timeout to be used to wait for process state change.
281
timed_waitpid (pid_t pid, time_t timeout)
283
static char buffer[1];
294
if (selfpipe[0] == -1)
298
FD_SET (selfpipe[0], &read_fds);
300
nfds = 1 + selfpipe[0];
305
/* wait for some activity */
306
ret = select (nfds, &read_fds, NULL, NULL, &tv);
312
/* discard any data written to pipe */
313
while (read (selfpipe[0], buffer, sizeof (buffer)) > 0)
317
/* wait for status change or error */
318
ret2 = waitpid (pid, &status, WNOHANG);
324
/* give child a chance to change state */
328
if (WIFEXITED (status))
331
/* unexpected status change */
341
* Determine a suitable initctl command-line for testing purposes.
343
* Returns: Static string representing full path to initctl binary with
344
* default option to allow communication with an Upstart started using
350
static char path[PATH_MAX + 1024] = { 0 };
353
ret = sprintf (path, "%s %s",
367
* @pid: PID of running instance,
368
* @user: TRUE if upstart will run in User Session mode (FALSE to
369
* use the users D-Bus session bus),
370
* @args: optional list of arguments to specify.
372
* Start an instance of Upstart.
374
* If the instance fails to start, abort(3) is called.
377
_start_upstart (pid_t *pid, int user, char * const *args)
379
nih_local char **argv = NULL;
380
sigset_t child_set, orig_set;
384
argv = NIH_MUST (nih_str_array_new (NULL));
386
NIH_MUST (nih_str_array_add (&argv, NULL, NULL,
390
NIH_MUST (nih_str_array_append (&argv, NULL, NULL, args));
392
sigfillset (&child_set);
393
sigprocmask (SIG_BLOCK, &child_set, &orig_set);
395
TEST_NE (*pid = fork (), -1);
400
sigprocmask (SIG_SETMASK, &orig_set, NULL);
402
if (! getenv ("UPSTART_TEST_VERBOSE")) {
403
fd = open ("/dev/null", O_RDWR);
405
assert (dup2 (fd, STDIN_FILENO) != -1);
406
assert (dup2 (fd, STDOUT_FILENO) != -1);
407
assert (dup2 (fd, STDERR_FILENO) != -1);
410
assert (execv (argv[0], argv) != -1);
413
sigprocmask (SIG_SETMASK, &orig_set, NULL);
414
wait_for_upstart (user ? *pid : 0);
418
* start_upstart_common:
420
* @pid: PID of running instance,
421
* @user: TRUE if upstart should run in User Session mode (FALSE to
422
* use the users D-Bus session bus),
423
* @confdir: full path to configuration directory,
424
* @logdir: full path to log directory,
425
* @extra: optional extra arguments.
427
* Wrapper round _start_upstart() which specifies common options.
430
start_upstart_common (pid_t *pid, int user, const char *confdir,
431
const char *logdir, char * const *extra)
433
nih_local char **args = NULL;
437
args = NIH_MUST (nih_str_array_new (NULL));
440
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
442
test_user_mode = TRUE;
444
TEST_TRUE (getenv ("DBUS_SESSION_BUS_ADDRESS"));
445
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
449
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
450
"--no-startup-event"));
452
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
455
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
456
"--no-inherit-env"));
459
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
461
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
466
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
468
NIH_MUST (nih_str_array_add (&args, NULL, NULL,
473
NIH_MUST (nih_str_array_append (&args, NULL, NULL, extra));
475
_start_upstart (pid, user, args);
481
* @pid: PID of running instance.
483
* Wrapper round _start_upstart() which just runs an instance with no
487
start_upstart (pid_t *pid)
489
start_upstart_common (pid, FALSE, NULL, NULL, NULL);
497
* Determine pid of running job.
499
* WARNING: it is the callers responsibility to ensure that
500
* @job is still running when this function is called!!
502
* Returns: pid of job, or -1 if not found.
505
job_to_pid (const char *job)
509
regmatch_t regmatch[2];
511
nih_local char *cmd = NULL;
512
nih_local char *pattern = NULL;
515
nih_local char *str_pid = NULL;
519
pattern = NIH_MUST (nih_sprintf
520
(NULL, "^\\b%s\\b .*, process ([0-9]+)", job));
522
cmd = NIH_MUST (nih_sprintf (NULL, "%s status %s 2>&1",
523
get_initctl (), job));
524
RUN_COMMAND (NULL, cmd, &status, &lines);
527
ret = regcomp (®ex, pattern, REG_EXTENDED);
530
ret = regexec (®ex, status[0], 2, regmatch, 0);
531
if (ret == REG_NOMATCH) {
537
if (regmatch[1].rm_so == -1 || regmatch[1].rm_eo == -1) {
542
/* extract the pid */
543
NIH_MUST (nih_strncat (&str_pid, NULL,
544
&status[0][regmatch[1].rm_so],
545
regmatch[1].rm_eo - regmatch[1].rm_so));
549
pid = (pid_t)atol (str_pid);
551
/* check it's running */
562
get_upstart_binary (void)
564
return UPSTART_BINARY;
568
get_initctl_binary (void)
570
return INITCTL_BINARY;
579
* Compare @a and @b either or both of which may be NULL.
581
* Returns 0 if strings are identical or both NULL, else 1.
584
string_check (const char *a, const char *b)
604
* String comparison function suitable for passing to qsort(3).
605
* See the qsort(3) man page for further details.
608
strcmp_compar (const void *a, const void *b)
610
return strcmp(*(char * const *)a, *(char * const *)b);
616
* @xdg_runtime_dir: Directory to treat as XDG_RUNTIME_DIR,
617
* @pid: pid of running Session Init instance.
619
* Determine full path to a Session Inits session file.
621
* Note: No check on the existence of the session file is performed.
623
* Returns: Newly-allocated string representing full path to Session
624
* Inits session file.
627
get_session_file (const char *xdg_runtime_dir, pid_t pid)
631
nih_assert (xdg_runtime_dir);
634
session_file = nih_sprintf (NULL, "%s/upstart/sessions/%d.session",
635
xdg_runtime_dir, (int)pid);
637
nih_assert (session_file);
645
* Determine if running inside a chroot environment.
647
* Failures are fatal.
649
* Returns TRUE if within a chroot, else FALSE.
661
fprintf (stderr, "ERROR: cannot stat '%s'\n", dir);
665
if ( st.st_ino == 2 )
674
* Determine if D-Bus has been configured (with dbus-uuidgen).
676
* Returns TRUE if D-Bus appears to have been configured,
680
dbus_configured (void)
683
char path[] = "/var/lib/dbus/machine-id";
685
return !stat (path, &st);
689
* search_and_replace:
691
* @parent: parent for returned string,
692
* @str: string to operate on,
693
* @from: string to look for,
694
* @to: string to replace @from with.
696
* Replace all occurences of @from in @str with @to.
698
* Returns: Newly-allocated string, or NULL on error or
699
* if @str does not contain any occurences of @from.
702
search_and_replace (void *parent,
719
while (start && *start) {
720
match = strstr (start, from);
723
/* No more matches, so copy the remainder of the original string */
724
if (! nih_strcat (&new, parent, start))
729
/* Copy data from start of segment to the match */
730
if (! nih_strncat (&new, parent , start, match - start))
733
/* Replace the string */
734
if (! nih_strcat (&new, parent, to))
737
/* Make start move to 1 byte beyond the end of the match */