]> eyrie.org Git - kerberos/krb5-strength.git/blob - tests/tap/process.c
8ed4cfd0b33de46f2a73a4361f389820a0f3b667
[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  * The canonical version of this file is maintained in the rra-c-util package,
11  * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
12  *
13  * Written by Russ Allbery <rra@stanford.edu>
14  * Copyright 2002, 2004, 2005 Russ Allbery <rra@stanford.edu>
15  * Copyright 2009, 2010, 2011
16  *     The Board of Trustees of the Leland Stanford Junior University
17  *
18  * Permission is hereby granted, free of charge, to any person obtaining a
19  * copy of this software and associated documentation files (the "Software"),
20  * to deal in the Software without restriction, including without limitation
21  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
22  * and/or sell copies of the Software, and to permit persons to whom the
23  * Software is furnished to do so, subject to the following conditions:
24  *
25  * The above copyright notice and this permission notice shall be included in
26  * all copies or substantial portions of the Software.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
31  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
33  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
34  * DEALINGS IN THE SOFTWARE.
35  */
36
37 #include <config.h>
38 #include <portable/system.h>
39
40 #include <sys/wait.h>
41
42 #include <tests/tap/basic.h>
43 #include <tests/tap/process.h>
44 #include <tests/tap/string.h>
45
46
47 /*
48  * Given a function, an expected exit status, and expected output, runs that
49  * function in a subprocess, capturing stdout and stderr via a pipe, and
50  * returns the function output in newly allocated memory.  Also captures the
51  * process exit status.
52  */
53 static void
54 run_child_function(test_function_type function, void *data, int *status,
55                    char **output)
56 {
57     int fds[2];
58     pid_t child;
59     char *buf;
60     ssize_t count, ret, buflen;
61     int rval;
62
63     /* Flush stdout before we start to avoid odd forking issues. */
64     fflush(stdout);
65
66     /* Set up the pipe and call the function, collecting its output. */
67     if (pipe(fds) == -1)
68         sysbail("can't create pipe");
69     child = fork();
70     if (child == (pid_t) -1) {
71         sysbail("can't fork");
72     } else if (child == 0) {
73         /* In child.  Set up our stdout and stderr. */
74         close(fds[0]);
75         if (dup2(fds[1], 1) == -1)
76             _exit(255);
77         if (dup2(fds[1], 2) == -1)
78             _exit(255);
79
80         /* Now, run the function and exit successfully if it returns. */
81         (*function)(data);
82         fflush(stdout);
83         _exit(0);
84     } else {
85         /*
86          * In the parent; close the extra file descriptor, read the output if
87          * any, and then collect the exit status.
88          */
89         close(fds[1]);
90         buflen = BUFSIZ;
91         buf = bmalloc(buflen);
92         count = 0;
93         do {
94             ret = read(fds[0], buf + count, buflen - count - 1);
95             if (ret > 0)
96                 count += ret;
97             if (count >= buflen - 1) {
98                 buflen += BUFSIZ;
99                 buf = brealloc(buf, buflen);
100             }
101         } while (ret > 0);
102         buf[count < 0 ? 0 : count] = '\0';
103         if (waitpid(child, &rval, 0) == (pid_t) -1)
104             sysbail("waitpid failed");
105         close(fds[0]);
106     }
107
108     /* Store the output and return. */
109     *status = rval;
110     *output = buf;
111 }
112
113
114 /*
115  * Given a function, data to pass to that function, an expected exit status,
116  * and expected output, runs that function in a subprocess, capturing stdout
117  * and stderr via a pipe, and compare the combination of stdout and stderr
118  * with the expected output and the exit status with the expected status.
119  * Expects the function to always exit (not die from a signal).
120  */
121 void
122 is_function_output(test_function_type function, void *data, int status,
123                    const char *output, const char *format, ...)
124 {
125     char *buf, *msg;
126     int rval;
127     va_list args;
128
129     run_child_function(function, data, &rval, &buf);
130
131     /* Now, check the results against what we expected. */
132     va_start(args, format);
133     bvasprintf(&msg, format, args);
134     va_end(args);
135     ok(WIFEXITED(rval), "%s (exited)", msg);
136     is_int(status, WEXITSTATUS(rval), "%s (status)", msg);
137     is_string(output, buf, "%s (output)", msg);
138     free(buf);
139     free(msg);
140 }
141
142
143 /*
144  * A helper function for run_setup.  This is a function to run an external
145  * command, suitable for passing into run_child_function.  The expected
146  * argument must be an argv array, with argv[0] being the command to run.
147  */
148 static void
149 exec_command(void *data)
150 {
151     char *const *argv = data;
152
153     execvp(argv[0], argv);
154 }
155
156
157 /*
158  * Given a command expressed as an argv struct, with argv[0] the name or path
159  * to the command, run that command.  If it exits with a non-zero status, use
160  * the part of its output up to the first newline as the error message when
161  * calling bail.
162  */
163 void
164 run_setup(const char *const argv[])
165 {
166     char *output, *p;
167     int status;
168
169     run_child_function(exec_command, (void *) argv, &status, &output);
170     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
171         p = strchr(output, '\n');
172         if (p != NULL)
173             *p = '\0';
174         bail("%s", output);
175     }
176     free(output);
177 }