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