]> eyrie.org Git - kerberos/krb5-strength.git/blob - tests/tap/process.c
Declare fast forward from 3.1-2
[kerberos/krb5-strength.git] / tests / tap / process.c
1 /*
2  * Utility functions for tests that use subprocesses.
3  *
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.
9  *
10  * Requires an Autoconf probe for sys/select.h and a replacement for a missing
11  * mkstemp.
12  *
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/>.
15  *
16  * Written by Russ Allbery <eagle@eyrie.org>
17  * Copyright 2002, 2004-2005, 2013, 2016-2017 Russ Allbery <eagle@eyrie.org>
18  * Copyright 2009-2011, 2013-2014
19  *     The Board of Trustees of the Leland Stanford Junior University
20  *
21  * Permission is hereby granted, free of charge, to any person obtaining a
22  * copy of this software and associated documentation files (the "Software"),
23  * to deal in the Software without restriction, including without limitation
24  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
25  * and/or sell copies of the Software, and to permit persons to whom the
26  * Software is furnished to do so, subject to the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be included in
29  * all copies or substantial portions of the Software.
30  *
31  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
32  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
34  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
36  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
37  * DEALINGS IN THE SOFTWARE.
38  *
39  * SPDX-License-Identifier: MIT
40  */
41
42 #include <config.h>
43 #include <portable/system.h>
44
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <signal.h>
48 #ifdef HAVE_SYS_SELECT_H
49 #    include <sys/select.h>
50 #endif
51 #include <sys/stat.h>
52 #ifdef HAVE_SYS_TIME_H
53 #    include <sys/time.h>
54 #endif
55 #include <sys/wait.h>
56 #include <time.h>
57
58 #include <tests/tap/basic.h>
59 #include <tests/tap/process.h>
60 #include <tests/tap/string.h>
61
62 /* May be defined by the build system. */
63 #ifndef PATH_FAKEROOT
64 #    define PATH_FAKEROOT ""
65 #endif
66
67 /* How long to wait for the process to start in seconds. */
68 #define PROCESS_WAIT 10
69
70 /*
71  * Used to store information about a background process.  This contains
72  * everything required to stop the process and clean up after it.
73  */
74 struct process {
75     pid_t pid;            /* PID of child process */
76     char *pidfile;        /* PID file to delete on process stop */
77     char *tmpdir;         /* Temporary directory for log file */
78     char *logfile;        /* Log file of process output */
79     bool is_child;        /* Whether we can waitpid for process */
80     struct process *next; /* Next process in global list */
81 };
82
83 /*
84  * Global list of started processes, which will be cleaned up automatically on
85  * program exit if they haven't been explicitly stopped with process_stop
86  * prior to that point.
87  */
88 static struct process *processes = NULL;
89
90
91 /*
92  * Given a function, an expected exit status, and expected output, runs that
93  * function in a subprocess, capturing stdout and stderr via a pipe, and
94  * returns the function output in newly allocated memory.  Also captures the
95  * process exit status.
96  */
97 static void
98 run_child_function(test_function_type function, void *data, int *status,
99                    char **output)
100 {
101     int fds[2];
102     pid_t child;
103     char *buf;
104     ssize_t count, ret, buflen;
105     int rval;
106
107     /* Flush stdout before we start to avoid odd forking issues. */
108     fflush(stdout);
109
110     /* Set up the pipe and call the function, collecting its output. */
111     if (pipe(fds) == -1)
112         sysbail("can't create pipe");
113     child = fork();
114     if (child == (pid_t) -1) {
115         sysbail("can't fork");
116     } else if (child == 0) {
117         /* In child.  Set up our stdout and stderr. */
118         close(fds[0]);
119         if (dup2(fds[1], 1) == -1)
120             _exit(255);
121         if (dup2(fds[1], 2) == -1)
122             _exit(255);
123
124         /* Now, run the function and exit successfully if it returns. */
125         (*function)(data);
126         fflush(stdout);
127         _exit(0);
128     } else {
129         /*
130          * In the parent; close the extra file descriptor, read the output if
131          * any, and then collect the exit status.
132          */
133         close(fds[1]);
134         buflen = BUFSIZ;
135         buf = bmalloc(buflen);
136         count = 0;
137         do {
138             ret = read(fds[0], buf + count, buflen - count - 1);
139             if (SSIZE_MAX - count <= ret)
140                 bail("maximum output size exceeded in run_child_function");
141             if (ret > 0)
142                 count += ret;
143             if (count >= buflen - 1) {
144                 buflen += BUFSIZ;
145                 buf = brealloc(buf, buflen);
146             }
147         } while (ret > 0);
148         buf[count] = '\0';
149         if (waitpid(child, &rval, 0) == (pid_t) -1)
150             sysbail("waitpid failed");
151         close(fds[0]);
152     }
153
154     /* Store the output and return. */
155     *status = rval;
156     *output = buf;
157 }
158
159
160 /*
161  * Given a function, data to pass to that function, an expected exit status,
162  * and expected output, runs that function in a subprocess, capturing stdout
163  * and stderr via a pipe, and compare the combination of stdout and stderr
164  * with the expected output and the exit status with the expected status.
165  * Expects the function to always exit (not die from a signal).
166  */
167 void
168 is_function_output(test_function_type function, void *data, int status,
169                    const char *output, const char *format, ...)
170 {
171     char *buf, *msg;
172     int rval;
173     va_list args;
174
175     run_child_function(function, data, &rval, &buf);
176
177     /* Now, check the results against what we expected. */
178     va_start(args, format);
179     bvasprintf(&msg, format, args);
180     va_end(args);
181     ok(WIFEXITED(rval), "%s (exited)", msg);
182     is_int(status, WEXITSTATUS(rval), "%s (status)", msg);
183     is_string(output, buf, "%s (output)", msg);
184     free(buf);
185     free(msg);
186 }
187
188
189 /*
190  * A helper function for run_setup.  This is a function to run an external
191  * command, suitable for passing into run_child_function.  The expected
192  * argument must be an argv array, with argv[0] being the command to run.
193  */
194 static void
195 exec_command(void *data)
196 {
197     char *const *argv = data;
198
199     execvp(argv[0], argv);
200 }
201
202
203 /*
204  * Given a command expressed as an argv struct, with argv[0] the name or path
205  * to the command, run that command.  If it exits with a non-zero status, use
206  * the part of its output up to the first newline as the error message when
207  * calling bail.
208  */
209 void
210 run_setup(const char *const argv[])
211 {
212     char *output, *p;
213     int status;
214
215     run_child_function(exec_command, (void *) argv, &status, &output);
216     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
217         p = strchr(output, '\n');
218         if (p != NULL)
219             *p = '\0';
220         if (output[0] != '\0')
221             bail("%s", output);
222         else
223             bail("setup command failed with no output");
224     }
225     free(output);
226 }
227
228
229 /*
230  * Free the resources associated with tracking a process, without doing
231  * anything to the process.  This is kept separate so that we can free
232  * resources during shutdown in a non-primary process.
233  */
234 static void
235 process_free(struct process *process)
236 {
237     struct process **prev;
238
239     /* Do nothing if called with a NULL argument. */
240     if (process == NULL)
241         return;
242
243     /* Remove the process from the global list. */
244     prev = &processes;
245     while (*prev != NULL && *prev != process)
246         prev = &(*prev)->next;
247     if (*prev == process)
248         *prev = process->next;
249
250     /* Free resources. */
251     free(process->pidfile);
252     free(process->logfile);
253     test_tmpdir_free(process->tmpdir);
254     free(process);
255 }
256
257
258 /*
259  * Kill a process and wait for it to exit.  Returns the status of the process.
260  * Calls bail on a system failure or a failure of the process to exit.
261  *
262  * We are quite aggressive with error reporting here because child processes
263  * that don't exit or that don't exist often indicate some form of test
264  * failure.
265  */
266 static int
267 process_kill(struct process *process)
268 {
269     int result, i;
270     int status = -1;
271     struct timeval tv;
272     unsigned long pid = process->pid;
273
274     /* If the process is not a child, just kill it and hope. */
275     if (!process->is_child) {
276         if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH)
277             sysbail("cannot send SIGTERM to process %lu", pid);
278         return 0;
279     }
280
281     /* Check if the process has already exited. */
282     result = waitpid(process->pid, &status, WNOHANG);
283     if (result < 0)
284         sysbail("cannot wait for child process %lu", pid);
285     else if (result > 0)
286         return status;
287
288     /*
289      * Kill the process and wait for it to exit.  I don't want to go to the
290      * work of setting up a SIGCHLD handler or a full event loop here, so we
291      * effectively poll every tenth of a second for process exit (and
292      * hopefully faster when it does since the SIGCHLD may interrupt our
293      * select, although we're racing with it.
294      */
295     if (kill(process->pid, SIGTERM) < 0 && errno != ESRCH)
296         sysbail("cannot send SIGTERM to child process %lu", pid);
297     for (i = 0; i < PROCESS_WAIT * 10; i++) {
298         tv.tv_sec = 0;
299         tv.tv_usec = 100000;
300         select(0, NULL, NULL, NULL, &tv);
301         result = waitpid(process->pid, &status, WNOHANG);
302         if (result < 0)
303             sysbail("cannot wait for child process %lu", pid);
304         else if (result > 0)
305             return status;
306     }
307
308     /* The process still hasn't exited.  Bail. */
309     bail("child process %lu did not exit on SIGTERM", pid);
310
311     /* Not reached, but some compilers may get confused. */
312     return status;
313 }
314
315
316 /*
317  * Stop a particular process given its process struct.  This kills the
318  * process, waits for it to exit if possible (giving it at most five seconds),
319  * and then removes it from the global processes struct so that it isn't
320  * stopped again during global shutdown.
321  */
322 void
323 process_stop(struct process *process)
324 {
325     int status;
326     unsigned long pid = process->pid;
327
328     /* Stop the process. */
329     status = process_kill(process);
330
331     /* Call diag to flush logs as well as provide exit status. */
332     if (process->is_child)
333         diag("stopped process %lu (exit status %d)", pid, status);
334     else
335         diag("stopped process %lu", pid);
336
337     /* Remove the log and PID file. */
338     diag_file_remove(process->logfile);
339     unlink(process->pidfile);
340     unlink(process->logfile);
341
342     /* Free resources. */
343     process_free(process);
344 }
345
346
347 /*
348  * Stop all running processes.  This is called as a cleanup handler during
349  * process shutdown.  The first argument, which says whether the test was
350  * successful, is ignored, since the same actions should be performed
351  * regardless.  The second argument says whether this is the primary process,
352  * in which case we do the full shutdown.  Otherwise, we only free resources
353  * but don't stop the process.
354  */
355 static void
356 process_stop_all(int success UNUSED, int primary)
357 {
358     while (processes != NULL) {
359         if (primary)
360             process_stop(processes);
361         else
362             process_free(processes);
363     }
364 }
365
366
367 /*
368  * Read the PID of a process from a file.  This is necessary when running
369  * under fakeroot to get the actual PID of the remctld process.
370  */
371 static pid_t
372 read_pidfile(const char *path)
373 {
374     FILE *file;
375     char buffer[BUFSIZ];
376     long pid;
377
378     file = fopen(path, "r");
379     if (file == NULL)
380         sysbail("cannot open %s", path);
381     if (fgets(buffer, sizeof(buffer), file) == NULL)
382         sysbail("cannot read from %s", path);
383     fclose(file);
384     pid = strtol(buffer, NULL, 10);
385     if (pid <= 0)
386         bail("cannot read PID from %s", path);
387     return (pid_t) pid;
388 }
389
390
391 /*
392  * Start a process and return its status information.  The status information
393  * is also stored in the global processes linked list so that it can be
394  * stopped automatically on program exit.
395  *
396  * The boolean argument says whether to start the process under fakeroot.  If
397  * true, PATH_FAKEROOT must be defined, generally by Autoconf.  If it's not
398  * found, call skip_all.
399  *
400  * This is a helper function for process_start and process_start_fakeroot.
401  */
402 static struct process *
403 process_start_internal(const char *const argv[], const char *pidfile,
404                        bool fakeroot)
405 {
406     size_t i;
407     int log_fd;
408     const char *name;
409     struct timeval tv;
410     struct process *process;
411     const char **fakeroot_argv = NULL;
412     const char *path_fakeroot = PATH_FAKEROOT;
413
414     /* Check prerequisites. */
415     if (fakeroot && path_fakeroot[0] == '\0')
416         skip_all("fakeroot not found");
417
418     /* Create the process struct and log file. */
419     process = bcalloc(1, sizeof(struct process));
420     process->pidfile = bstrdup(pidfile);
421     process->tmpdir = test_tmpdir();
422     name = strrchr(argv[0], '/');
423     if (name != NULL)
424         name++;
425     else
426         name = argv[0];
427     basprintf(&process->logfile, "%s/%s.log.XXXXXX", process->tmpdir, name);
428     log_fd = mkstemp(process->logfile);
429     if (log_fd < 0)
430         sysbail("cannot create log file for %s", argv[0]);
431
432     /* If using fakeroot, rewrite argv accordingly. */
433     if (fakeroot) {
434         for (i = 0; argv[i] != NULL; i++)
435             ;
436         fakeroot_argv = bcalloc(2 + i + 1, sizeof(const char *));
437         fakeroot_argv[0] = path_fakeroot;
438         fakeroot_argv[1] = "--";
439         for (i = 0; argv[i] != NULL; i++)
440             fakeroot_argv[i + 2] = argv[i];
441         fakeroot_argv[i + 2] = NULL;
442         argv = fakeroot_argv;
443     }
444
445     /*
446      * Fork off the child process, redirect its standard output and standard
447      * error to the log file, and then exec the program.
448      */
449     process->pid = fork();
450     if (process->pid < 0)
451         sysbail("fork failed");
452     else if (process->pid == 0) {
453         if (dup2(log_fd, STDOUT_FILENO) < 0)
454             sysbail("cannot redirect standard output");
455         if (dup2(log_fd, STDERR_FILENO) < 0)
456             sysbail("cannot redirect standard error");
457         close(log_fd);
458         if (execv(argv[0], (char *const *) argv) < 0)
459             sysbail("exec of %s failed", argv[0]);
460     }
461     close(log_fd);
462     free(fakeroot_argv);
463
464     /*
465      * In the parent.  Wait for the child to start by watching for the PID
466      * file to appear in 100ms intervals.
467      */
468     for (i = 0; i < PROCESS_WAIT * 10 && access(pidfile, F_OK) != 0; i++) {
469         tv.tv_sec = 0;
470         tv.tv_usec = 100000;
471         select(0, NULL, NULL, NULL, &tv);
472     }
473
474     /*
475      * If the PID file still hasn't appeared after ten seconds, attempt to
476      * kill the process and then bail.
477      */
478     if (access(pidfile, F_OK) != 0) {
479         kill(process->pid, SIGTERM);
480         alarm(5);
481         waitpid(process->pid, NULL, 0);
482         alarm(0);
483         bail("cannot start %s", argv[0]);
484     }
485
486     /*
487      * Read the PID back from the PID file.  This usually isn't necessary for
488      * non-forking daemons, but always doing this makes this function general,
489      * and it's required when running under fakeroot.
490      */
491     if (fakeroot)
492         process->pid = read_pidfile(pidfile);
493     process->is_child = !fakeroot;
494
495     /* Register the log file as a source of diag messages. */
496     diag_file_add(process->logfile);
497
498     /*
499      * Add the process to our global list and set our cleanup handler if this
500      * is the first process we started.
501      */
502     if (processes == NULL)
503         test_cleanup_register(process_stop_all);
504     process->next = processes;
505     processes = process;
506
507     /* All done. */
508     return process;
509 }
510
511
512 /*
513  * Start a process and return the opaque process struct.  The process must
514  * create pidfile with its PID when startup is complete.
515  */
516 struct process *
517 process_start(const char *const argv[], const char *pidfile)
518 {
519     return process_start_internal(argv, pidfile, false);
520 }
521
522
523 /*
524  * Start a process under fakeroot and return the opaque process struct.  If
525  * fakeroot is not available, calls skip_all.  The process must create pidfile
526  * with its PID when startup is complete.
527  */
528 struct process *
529 process_start_fakeroot(const char *const argv[], const char *pidfile)
530 {
531     return process_start_internal(argv, pidfile, true);
532 }