setnetpath(3): Fix a double free().
[dragonfly.git] / usr.bin / dfregress / userland.c
blob96ff0cf7f7b2abce3bfb941cbc60d5f5661d3ba1
1 /*
2 * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
30 #include <sys/resource.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdint.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <pwd.h>
45 #include <err.h>
47 #include <libprop/proplib.h>
49 #include "testcase.h"
50 #include "runlist.h"
51 #include "userland.h"
52 #include <dfregress.h>
54 static void
55 clean_child(pid_t pid)
57 kill(pid, SIGKILL);
60 static void
61 sig_handle(int sig __unused)
63 return;
66 int
67 run_userland(const char *binary, const char **argv, int need_setuid, uid_t uid,
68 struct timeval *timeout, int unify_output, char *errbuf, size_t errbuf_sz,
69 struct testcase_result *tr)
71 struct itimerval itim;
72 struct sigaction sa;
73 pid_t pid = -1, r_pid;
74 int r, status;
75 int fd_stdout = -1, fd_stderr = -1;
76 size_t sz_stdout, sz_stderr;
77 char stdout_file[256];
78 char stderr_file[256];
79 char *str;
81 /* Set sane defaults */
82 bzero(tr, sizeof(*tr));
83 tr->result = RESULT_NOTRUN;
85 strcpy(stdout_file, "/tmp/dfregress.XXXXXXXXXXXX");
86 strcpy(stderr_file, "/tmp/dfregress.XXXXXXXXXXXX");
87 str = mktemp(stdout_file);
88 if (str == NULL) {
89 if (errbuf)
90 snprintf(errbuf, errbuf_sz, "Could not mktemp(): "
91 "%s\n", strerror(errno));
92 return -1;
95 if (!unify_output) {
96 str = mktemp(stderr_file);
97 if (str == NULL) {
98 if (errbuf)
99 snprintf(errbuf, errbuf_sz, "Could not mktemp(): "
100 "%s\n", strerror(errno));
101 return -1;
105 fd_stdout = open(stdout_file, O_RDWR | O_CREAT | O_FSYNC);
106 if (fd_stdout < 0) {
107 if (errbuf)
108 snprintf(errbuf, errbuf_sz, "Could not open() temp file "
109 "for stdout: %s\n", strerror(errno));
110 goto err_out;
113 if (!unify_output) {
114 fd_stderr = open(stderr_file, O_RDWR | O_CREAT | O_FSYNC);
115 if (fd_stderr < 0) {
116 if (errbuf)
117 snprintf(errbuf, errbuf_sz, "Could not open() "
118 "temp file for stderr: %s\n", strerror(errno));
119 goto err_out;
124 if ((pid = fork()) == -1) {
125 if (errbuf)
126 snprintf(errbuf, errbuf_sz, "Could not fork to run "
127 "binary %s: %s\n", binary, strerror(errno));
129 goto err_out;
130 } else if (pid > 0) {
131 /* parent */
133 if (timeout != NULL) {
134 /* Ignore SIGALRM */
135 bzero(&sa, sizeof(sa));
136 sa.sa_handler = sig_handle;
137 sigaction(SIGALRM, &sa, NULL);
139 /* Set up timeout */
140 itim.it_interval.tv_sec = 0;
141 itim.it_interval.tv_usec = 0;
142 itim.it_value = *timeout;
143 r = setitimer(ITIMER_REAL, &itim, NULL);
144 if (r == -1) {
145 if (errbuf)
146 snprintf(errbuf, errbuf_sz, "Could not "
147 "set up timer: %s", strerror(errno));
149 /* Clean up child process! */
150 goto err_out;
154 r_pid = wait4(pid, &status, 0, &tr->rusage);
155 if (r_pid == -1) {
156 if (errno == EINTR) {
157 /* Alarm timed out */
158 tr->result = RESULT_TIMEOUT;
160 /* Clean up child process! */
161 clean_child(pid);
162 } else if (errno == ECHILD) {
163 /* Child already exited somehow */
164 tr->result = RESULT_UNKNOWN;
165 } else {
166 /* EFAULT */
167 if (errbuf)
168 snprintf(errbuf, errbuf_sz, "Could not "
169 "wait4(): %s", strerror(errno));
171 goto err_out;
173 } else {
174 if (WIFEXITED(status)) {
175 tr->result = (WEXITSTATUS(status) == 0) ?
176 RESULT_PASS :
177 (WEXITSTATUS(status) == EXIT_NOTRUN) ?
178 RESULT_NOTRUN : RESULT_FAIL;
180 tr->exit_value = WEXITSTATUS(status);
181 } else if (WIFSIGNALED(status)) {
182 tr->result = RESULT_SIGNALLED;
183 tr->signal = WTERMSIG(status);
184 tr->core_dumped = (WCOREDUMP(status)) ? 1 : 0;
185 } else {
186 tr->result = RESULT_UNKNOWN;
190 if (timeout != NULL) {
191 /* Disable timer */
192 itim.it_value.tv_sec = 0;
193 itim.it_value.tv_usec = 0;
194 setitimer(ITIMER_REAL, &itim, NULL);
196 } else {
197 /* pid == 0, so we are the child */
199 /* Redirect stdout and stderr */
200 if (fd_stdout >= 0) {
201 dup2(fd_stdout, 1);
202 setvbuf(stdout, NULL, _IONBF, 0);
205 if ((fd_stderr >= 0) || (unify_output && fd_stdout >= 0)) {
206 dup2((unify_output) ? fd_stdout : fd_stderr, 2);
207 setvbuf((unify_output) ? stdout : stderr,
208 NULL, _IONBF, 0);
211 /* Set uid if necessary */
212 if (need_setuid) {
213 r = setuid(uid);
214 if (r == -1) {
215 fprintf(stderr, "ERR: NOT RUN (setuid): %s",
216 strerror(errno));
217 exit(EXIT_NOTRUN);
221 /* Try to exec() */
222 r = execvp(binary, __DECONST(char **, argv));
223 if (r == -1) {
225 * If we couldn't exec(), notify parent that we didn't
226 * run.
228 fprintf(stderr, "ERR: NOT RUN: %s", strerror(errno));
229 exit(EXIT_NOTRUN);
233 /* Read stdout and stderr redirected file contents into memory */
234 sz_stdout = (size_t)lseek(fd_stdout, 0, SEEK_END);
235 lseek(fd_stdout, 0, SEEK_SET);
237 tr->stdout_buf = malloc(sz_stdout + 1);
238 if (tr->stdout_buf == NULL)
239 err(1, "could not malloc fd buf memory");
241 read(fd_stdout, tr->stdout_buf, sz_stdout);
242 tr->stdout_buf[sz_stdout] = '\0';
244 close(fd_stdout);
245 unlink(stdout_file);
247 if (!unify_output) {
248 sz_stderr = (size_t)lseek(fd_stderr, 0, SEEK_END);
249 lseek(fd_stderr, 0, SEEK_SET);
251 tr->stderr_buf = malloc(sz_stderr + 1);
252 if (tr->stderr_buf == NULL)
253 err(1, "could not malloc fd buf memory");
255 read(fd_stderr, tr->stderr_buf, sz_stderr);
256 tr->stderr_buf[sz_stderr] = '\0';
258 close(fd_stderr);
259 unlink(stderr_file);
263 return 0;
264 /* NOTREACHED */
266 err_out:
267 if (pid != -1)
268 clean_child(pid);
270 if (fd_stdout >= 0) {
271 close(fd_stdout);
272 unlink(stdout_file);
275 if (fd_stderr >= 0) {
276 close(fd_stderr);
277 unlink(stderr_file);
280 return -1;
284 run_simple_cmd(const char *binary, const char *arg, char *errbuf,
285 size_t errbuf_sz, struct testcase_result *tr)
287 const char *argv[3];
288 char *s;
290 s = strrchr(binary, '/');
292 argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1;
293 argv[1] = __DECONST(char *, arg);
294 argv[2] = NULL;
296 return run_userland(binary, argv, 0, 0, NULL, 1, errbuf, errbuf_sz, tr);