2 * Utility functions for tests that use subprocesses.
4 * Provides utility functions for subprocess manipulation. Specifically,
5 * provides a function, run_setup, which runs a command and bails if it fails,
6 * using its error message as the bail output, and is_function_output, which
7 * runs a function in a subprocess and checks its output and exit status
8 * against expected values.
10 * Requires an Autoconf probe for sys/select.h and a replacement for a missing
13 * The canonical version of this file is maintained in the rra-c-util package,
14 * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
16 * Written by Russ Allbery <eagle@eyrie.org>
17 * Copyright 2002, 2004-2005, 2013, 2016-2017, 2022
18 * Russ Allbery <eagle@eyrie.org>
19 * Copyright 2009-2011, 2013-2014
20 * The Board of Trustees of the Leland Stanford Junior University
22 * Permission is hereby granted, free of charge, to any person obtaining a
23 * copy of this software and associated documentation files (the "Software"),
24 * to deal in the Software without restriction, including without limitation
25 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
26 * and/or sell copies of the Software, and to permit persons to whom the
27 * Software is furnished to do so, subject to the following conditions:
29 * The above copyright notice and this permission notice shall be included in
30 * all copies or substantial portions of the Software.
32 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
35 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
37 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
38 * DEALINGS IN THE SOFTWARE.
40 * SPDX-License-Identifier: MIT
44 #include <portable/system.h>
49 #ifdef HAVE_SYS_SELECT_H
50 # include <sys/select.h>
53 #ifdef HAVE_SYS_TIME_H
54 # include <sys/time.h>
59 #include <tests/tap/basic.h>
60 #include <tests/tap/process.h>
61 #include <tests/tap/string.h>
63 /* May be defined by the build system. */
65 # define PATH_FAKEROOT ""
68 /* How long to wait for the process to start in seconds. */
69 #define PROCESS_WAIT 10
72 * Used to store information about a background process. This contains
73 * everything required to stop the process and clean up after it.
76 pid_t pid; /* PID of child process */
77 char *pidfile; /* PID file to delete on process stop */
78 char *tmpdir; /* Temporary directory for log file */
79 char *logfile; /* Log file of process output */
80 bool is_child; /* Whether we can waitpid for process */
81 struct process *next; /* Next process in global list */
85 * Global list of started processes, which will be cleaned up automatically on
86 * program exit if they haven't been explicitly stopped with process_stop
87 * prior to that point.
89 static struct process *processes = NULL;
93 * Given a function, an expected exit status, and expected output, runs that
94 * function in a subprocess, capturing stdout and stderr via a pipe, and
95 * returns the function output in newly allocated memory. Also captures the
96 * process exit status.
99 run_child_function(test_function_type function, void *data, int *status,
105 ssize_t count, ret, buflen;
108 /* Flush stdout before we start to avoid odd forking issues. */
111 /* Set up the pipe and call the function, collecting its output. */
113 sysbail("can't create pipe");
115 if (child == (pid_t) -1) {
116 sysbail("can't fork");
117 } else if (child == 0) {
118 /* In child. Set up our stdout and stderr. */
120 if (dup2(fds[1], 1) == -1)
122 if (dup2(fds[1], 2) == -1)
125 /* Now, run the function and exit successfully if it returns. */
131 * In the parent; close the extra file descriptor, read the output if
132 * any, and then collect the exit status.
136 buf = bmalloc(buflen);
139 ret = read(fds[0], buf + count, buflen - count - 1);
140 if (SSIZE_MAX - count <= ret)
141 bail("maximum output size exceeded in run_child_function");
144 if (count >= buflen - 1) {
146 buf = brealloc(buf, buflen);
150 if (waitpid(child, &rval, 0) == (pid_t) -1)
151 sysbail("waitpid failed");
155 /* Store the output and return. */
162 * Given a function, data to pass to that function, an expected exit status,
163 * and expected output, runs that function in a subprocess, capturing stdout
164 * and stderr via a pipe, and compare the combination of stdout and stderr
165 * with the expected output and the exit status with the expected status.
166 * Expects the function to always exit (not die from a signal).
169 is_function_output(test_function_type function, void *data, int status,
170 const char *output, const char *format, ...)
176 run_child_function(function, data, &rval, &buf);
178 /* Now, check the results against what we expected. */
179 va_start(args, format);
180 bvasprintf(&msg, format, args);
182 ok(WIFEXITED(rval), "%s (exited)", msg);
183 is_int(status, WEXITSTATUS(rval), "%s (status)", msg);
184 is_string(output, buf, "%s (output)", msg);
191 * A helper function for run_setup. This is a function to run an external
192 * command, suitable for passing into run_child_function. The expected
193 * argument must be an argv array, with argv[0] being the command to run.
196 exec_command(void *data)
198 char *const *argv = data;
200 execvp(argv[0], argv);
205 * Given a command expressed as an argv struct, with argv[0] the name or path
206 * to the command, run that command. If it exits with a non-zero status, use
207 * the part of its output up to the first newline as the error message when
211 run_setup(const char *const argv[])
216 run_child_function(exec_command, (void *) argv, &status, &output);
217 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
218 p = strchr(output, '\n');
221 if (output[0] != '\0')
224 bail("setup command failed with no output");
231 * Free the resources associated with tracking a process, without doing
232 * anything to the process. This is kept separate so that we can free
233 * resources during shutdown in a non-primary process.
236 process_free(struct process *process)
238 struct process **prev;
240 /* Do nothing if called with a NULL argument. */
244 /* Remove the process from the global list. */
246 while (*prev != NULL && *prev != process)
247 prev = &(*prev)->next;
248 if (*prev == process)
249 *prev = process->next;
251 /* Free resources. */
252 free(process->pidfile);
253 free(process->logfile);
254 test_tmpdir_free(process->tmpdir);
260 * Kill a process and wait for it to exit. Returns the status of the process.
261 * Calls bail on a system failure or a failure of the process to exit.
263 * We are quite aggressive with error reporting here because child processes
264 * that don't exit or that don't exist often indicate some form of test
268 process_kill(struct process *process)
273 unsigned long pid = process->pid;
275 /* If the process is not a child, just kill it and hope. */
276 if (!process->is_child) {
277 if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH)
278 sysbail("cannot send SIGTERM to process %lu", pid);
282 /* Check if the process has already exited. */
283 result = waitpid(process->pid, &status, WNOHANG);
285 sysbail("cannot wait for child process %lu", pid);
290 * Kill the process and wait for it to exit. I don't want to go to the
291 * work of setting up a SIGCHLD handler or a full event loop here, so we
292 * effectively poll every tenth of a second for process exit (and
293 * hopefully faster when it does since the SIGCHLD may interrupt our
294 * select, although we're racing with it.
296 if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH)
297 sysbail("cannot send SIGTERM to child process %lu", pid);
298 for (i = 0; i < PROCESS_WAIT * 10; i++) {
301 select(0, NULL, NULL, NULL, &tv);
302 result = waitpid(process->pid, &status, WNOHANG);
304 sysbail("cannot wait for child process %lu", pid);
309 /* The process still hasn't exited. Bail. */
310 bail("child process %lu did not exit on SIGTERM", pid);
312 /* Not reached, but some compilers may get confused. */
318 * Stop a particular process given its process struct. This kills the
319 * process, waits for it to exit if possible (giving it at most five seconds),
320 * and then removes it from the global processes struct so that it isn't
321 * stopped again during global shutdown.
324 process_stop(struct process *process)
327 unsigned long pid = process->pid;
329 /* Stop the process. */
330 status = process_kill(process);
332 /* Call diag to flush logs as well as provide exit status. */
333 if (process->is_child)
334 diag("stopped process %lu (exit status %d)", pid, status);
336 diag("stopped process %lu", pid);
338 /* Remove the log and PID file. */
339 diag_file_remove(process->logfile);
340 unlink(process->pidfile);
341 unlink(process->logfile);
343 /* Free resources. */
344 process_free(process);
349 * Stop all running processes. This is called as a cleanup handler during
350 * process shutdown. The first argument, which says whether the test was
351 * successful, is ignored, since the same actions should be performed
352 * regardless. The second argument says whether this is the primary process,
353 * in which case we do the full shutdown. Otherwise, we only free resources
354 * but don't stop the process.
357 process_stop_all(int success UNUSED, int primary)
359 while (processes != NULL) {
361 process_stop(processes);
363 process_free(processes);
369 * Read the PID of a process from a file. This is necessary when running
370 * under fakeroot to get the actual PID of the remctld process.
373 read_pidfile(const char *path)
379 file = fopen(path, "r");
381 sysbail("cannot open %s", path);
382 if (fgets(buffer, sizeof(buffer), file) == NULL)
383 sysbail("cannot read from %s", path);
385 pid = strtol(buffer, NULL, 10);
387 bail("cannot read PID from %s", path);
393 * Start a process and return its status information. The status information
394 * is also stored in the global processes linked list so that it can be
395 * stopped automatically on program exit.
397 * The boolean argument says whether to start the process under fakeroot. If
398 * true, PATH_FAKEROOT must be defined, generally by Autoconf. If it's not
399 * found, call skip_all.
401 * This is a helper function for process_start and process_start_fakeroot.
403 static struct process *
404 process_start_internal(const char *const argv[], const char *pidfile,
411 struct process *process;
412 const char **fakeroot_argv = NULL;
413 const char *path_fakeroot = PATH_FAKEROOT;
415 /* Check prerequisites. */
417 bail("argv for process_start is NULL");
418 if (fakeroot && path_fakeroot[0] == '\0')
419 skip_all("fakeroot not found");
421 /* Create the process struct and log file. */
422 process = bcalloc(1, sizeof(struct process));
423 process->pidfile = bstrdup(pidfile);
424 process->tmpdir = test_tmpdir();
425 name = strrchr(argv[0], '/');
430 basprintf(&process->logfile, "%s/%s.log.XXXXXX", process->tmpdir, name);
431 log_fd = mkstemp(process->logfile);
433 sysbail("cannot create log file for %s", argv[0]);
435 /* If using fakeroot, rewrite argv accordingly. */
437 for (i = 0; argv[i] != NULL; i++)
439 fakeroot_argv = bcalloc(2 + i + 1, sizeof(const char *));
440 fakeroot_argv[0] = path_fakeroot;
441 fakeroot_argv[1] = "--";
442 for (i = 0; argv[i] != NULL; i++)
443 fakeroot_argv[i + 2] = argv[i];
444 fakeroot_argv[i + 2] = NULL;
445 argv = fakeroot_argv;
449 * Fork off the child process, redirect its standard output and standard
450 * error to the log file, and then exec the program.
452 process->pid = fork();
453 if (process->pid < 0)
454 sysbail("fork failed");
455 else if (process->pid == 0) {
456 if (dup2(log_fd, STDOUT_FILENO) < 0)
457 sysbail("cannot redirect standard output");
458 if (dup2(log_fd, STDERR_FILENO) < 0)
459 sysbail("cannot redirect standard error");
461 if (execv(argv[0], (char *const *) argv) < 0)
462 sysbail("exec of %s failed", argv[0]);
468 * In the parent. Wait for the child to start by watching for the PID
469 * file to appear in 100ms intervals.
471 for (i = 0; i < PROCESS_WAIT * 10 && access(pidfile, F_OK) != 0; i++) {
474 select(0, NULL, NULL, NULL, &tv);
478 * If the PID file still hasn't appeared after ten seconds, attempt to
479 * kill the process and then bail.
481 if (access(pidfile, F_OK) != 0) {
482 kill(process->pid, SIGTERM);
484 waitpid(process->pid, NULL, 0);
486 bail("cannot start %s", argv[0]);
490 * Read the PID back from the PID file. This usually isn't necessary for
491 * non-forking daemons, but always doing this makes this function general,
492 * and it's required when running under fakeroot.
495 process->pid = read_pidfile(pidfile);
496 process->is_child = !fakeroot;
498 /* Register the log file as a source of diag messages. */
499 diag_file_add(process->logfile);
502 * Add the process to our global list and set our cleanup handler if this
503 * is the first process we started.
505 if (processes == NULL)
506 test_cleanup_register(process_stop_all);
507 process->next = processes;
516 * Start a process and return the opaque process struct. The process must
517 * create pidfile with its PID when startup is complete.
520 process_start(const char *const argv[], const char *pidfile)
522 return process_start_internal(argv, pidfile, false);
527 * Start a process under fakeroot and return the opaque process struct. If
528 * fakeroot is not available, calls skip_all. The process must create pidfile
529 * with its PID when startup is complete.
532 process_start_fakeroot(const char *const argv[], const char *pidfile)
534 return process_start_internal(argv, pidfile, true);