sys/vfs/hammer2: Remove obsolete comments on hidden inode
[dragonfly.git] / usr.bin / dfregress / userland.c
blob7ebaee4aef6c13c54dd813b2c1b6158d6c5e45f9
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, int argc, const char **argv, const char *interpreter,
68 int need_setuid, uid_t uid, struct timeval *timeout, int rc, int unify_output,
69 char *errbuf, size_t errbuf_sz, 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 **argv_copy;
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 fd_stdout = mkostemp(stdout_file, O_SYNC);
88 if (fd_stdout == -1) {
89 if (errbuf)
90 snprintf(errbuf, errbuf_sz, "Could not mkostemp(): "
91 "%s\n", strerror(errno));
92 return -1;
95 if (!unify_output) {
96 fd_stderr = mkostemp(stderr_file, O_SYNC);
97 if (fd_stderr == -1) {
98 if (errbuf)
99 snprintf(errbuf, errbuf_sz, "Could not mkostemp(): "
100 "%s\n", strerror(errno));
101 return -1;
106 if ((pid = fork()) == -1) {
107 if (errbuf)
108 snprintf(errbuf, errbuf_sz, "Could not fork to run "
109 "binary %s: %s\n", binary, strerror(errno));
111 goto err_out;
112 } else if (pid > 0) {
113 /* parent */
115 if (timeout != NULL) {
116 /* Ignore SIGALRM */
117 bzero(&sa, sizeof(sa));
118 sa.sa_handler = sig_handle;
119 sigaction(SIGALRM, &sa, NULL);
121 /* Set up timeout */
122 itim.it_interval.tv_sec = 0;
123 itim.it_interval.tv_usec = 0;
124 itim.it_value = *timeout;
125 r = setitimer(ITIMER_REAL, &itim, NULL);
126 if (r == -1) {
127 if (errbuf)
128 snprintf(errbuf, errbuf_sz, "Could not "
129 "set up timer: %s", strerror(errno));
131 /* Clean up child process! */
132 goto err_out;
136 r_pid = wait4(pid, &status, 0, &tr->rusage);
137 if (r_pid == -1) {
138 if (errno == EINTR) {
139 /* Alarm timed out */
140 tr->result = RESULT_TIMEOUT;
142 /* Clean up child process! */
143 clean_child(pid);
144 } else if (errno == ECHILD) {
145 /* Child already exited somehow */
146 tr->result = RESULT_UNKNOWN;
147 } else {
148 /* EFAULT */
149 if (errbuf)
150 snprintf(errbuf, errbuf_sz, "Could not "
151 "wait4(): %s", strerror(errno));
153 goto err_out;
155 } else {
156 if (WIFEXITED(status)) {
157 tr->result = (WEXITSTATUS(status) == rc) ?
158 RESULT_PASS :
159 (WEXITSTATUS(status) == EXIT_NOTRUN) ?
160 RESULT_NOTRUN : RESULT_FAIL;
162 tr->exit_value = WEXITSTATUS(status);
163 } else if (WIFSIGNALED(status)) {
164 tr->result = RESULT_SIGNALLED;
165 tr->signal = WTERMSIG(status);
166 tr->core_dumped = (WCOREDUMP(status)) ? 1 : 0;
167 } else {
168 tr->result = RESULT_UNKNOWN;
172 if (timeout != NULL) {
173 /* Disable timer */
174 itim.it_value.tv_sec = 0;
175 itim.it_value.tv_usec = 0;
176 setitimer(ITIMER_REAL, &itim, NULL);
178 } else {
179 /* pid == 0, so we are the child */
181 /* Redirect stdout and stderr */
182 if (fd_stdout >= 0) {
183 dup2(fd_stdout, 1);
184 setvbuf(stdout, NULL, _IONBF, 0);
187 if ((fd_stderr >= 0) || (unify_output && fd_stdout >= 0)) {
188 dup2((unify_output) ? fd_stdout : fd_stderr, 2);
189 setvbuf((unify_output) ? stdout : stderr,
190 NULL, _IONBF, 0);
193 /* Set uid if necessary */
194 if (need_setuid) {
195 r = setuid(uid);
196 if (r == -1) {
197 fprintf(stderr, "ERR: NOT RUN (setuid): %s",
198 strerror(errno));
199 exit(EXIT_NOTRUN);
203 if (interpreter) {
205 * Allocate argc + 3 arguments more as shown below:
206 * argv_copy[0] = interpreter
207 * argv_copy[1] = argv[0]
208 * argv_copy[argc+2] = NULL
210 * execvp requires the array to end with NULL.
212 argv_copy = (char **)calloc(argc + 3, sizeof(char *));
213 if (argv_copy == NULL) {
214 err(1, "could not calloc argv_copy memory");
217 /* Insert the interpreter at pos 0 */
218 argv_copy[0] = malloc(strlen(interpreter) + 1);
219 snprintf(argv_copy[0], strlen(interpreter) + 1, "%s",
220 interpreter);
222 /* We still need argv[0] when argc is 0 */
223 for (int i = 0; i <= argc; i++) {
224 size_t len;
225 len = strlen(argv[i]) + 1; /* NULL-terminated */
227 argv_copy[i + 1] = malloc(len);
228 if (argv_copy[i] == NULL)
229 err(1, "could not malloc memory");
231 snprintf(argv_copy[i + 1], len, "%s",
232 argv[i]);
235 /* Null terminate the array */
236 argv_copy[argc + 2] = NULL;
237 r = execvp(interpreter, argv_copy);
238 } else {
239 /* Try to exec() */
240 r = execvp(binary, __DECONST(char **, argv));
242 if (r == -1) {
244 * If we couldn't exec(), notify parent that we didn't
245 * run.
247 fprintf(stderr, "ERR: NOT RUN: %s", strerror(errno));
248 exit(EXIT_NOTRUN);
252 /* Read stdout and stderr redirected file contents into memory */
253 sz_stdout = (size_t)lseek(fd_stdout, 0, SEEK_END);
254 lseek(fd_stdout, 0, SEEK_SET);
256 tr->stdout_buf = malloc(sz_stdout + 1);
257 if (tr->stdout_buf == NULL)
258 err(1, "could not malloc fd buf memory");
260 read(fd_stdout, tr->stdout_buf, sz_stdout);
261 tr->stdout_buf[sz_stdout] = '\0';
263 close(fd_stdout);
264 unlink(stdout_file);
266 if (!unify_output) {
267 sz_stderr = (size_t)lseek(fd_stderr, 0, SEEK_END);
268 lseek(fd_stderr, 0, SEEK_SET);
270 tr->stderr_buf = malloc(sz_stderr + 1);
271 if (tr->stderr_buf == NULL)
272 err(1, "could not malloc fd buf memory");
274 read(fd_stderr, tr->stderr_buf, sz_stderr);
275 tr->stderr_buf[sz_stderr] = '\0';
277 close(fd_stderr);
278 unlink(stderr_file);
282 return 0;
283 /* NOTREACHED */
285 err_out:
286 if (pid != -1)
287 clean_child(pid);
289 if (fd_stdout >= 0) {
290 close(fd_stdout);
291 unlink(stdout_file);
294 if (fd_stderr >= 0) {
295 close(fd_stderr);
296 unlink(stderr_file);
299 return -1;
303 run_simple_cmd(const char *binary, const char *arg, char *errbuf,
304 size_t errbuf_sz, struct testcase_result *tr)
306 const char *argv[3];
307 char *s;
309 s = strrchr(binary, '/');
311 argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1;
312 argv[1] = __DECONST(char *, arg);
313 argv[2] = NULL;
315 return run_userland(binary, /* executable */
316 1, /* argc */
317 argv, /* argv */
318 NULL, /* interpreter */
319 0, /* needs_setuid */
320 0, /* runas_uid */
321 NULL, /* timeout */
322 0, /* rc */
323 1, /* unify_output */
324 errbuf, /* errbuf */
325 errbuf_sz, /* errbuf_size */
326 tr); /* testcase_result */