2 * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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
30 #include <sys/resource.h>
32 #include <sys/types.h>
47 #include <libprop/proplib.h>
52 #include <dfregress.h>
55 clean_child(pid_t pid)
61 sig_handle(int sig __unused)
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;
73 pid_t pid = -1, r_pid;
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];
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);
90 snprintf(errbuf, errbuf_sz, "Could not mktemp(): "
91 "%s\n", strerror(errno));
96 str = mktemp(stderr_file);
99 snprintf(errbuf, errbuf_sz, "Could not mktemp(): "
100 "%s\n", strerror(errno));
105 fd_stdout = open(stdout_file, O_RDWR | O_CREAT | O_FSYNC);
108 snprintf(errbuf, errbuf_sz, "Could not open() temp file "
109 "for stdout: %s\n", strerror(errno));
114 fd_stderr = open(stderr_file, O_RDWR | O_CREAT | O_FSYNC);
117 snprintf(errbuf, errbuf_sz, "Could not open() "
118 "temp file for stderr: %s\n", strerror(errno));
124 if ((pid = fork()) == -1) {
126 snprintf(errbuf, errbuf_sz, "Could not fork to run "
127 "binary %s: %s\n", binary, strerror(errno));
130 } else if (pid > 0) {
133 if (timeout != NULL) {
135 bzero(&sa, sizeof(sa));
136 sa.sa_handler = sig_handle;
137 sigaction(SIGALRM, &sa, NULL);
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);
146 snprintf(errbuf, errbuf_sz, "Could not "
147 "set up timer: %s", strerror(errno));
149 /* Clean up child process! */
154 r_pid = wait4(pid, &status, 0, &tr->rusage);
156 if (errno == EINTR) {
157 /* Alarm timed out */
158 tr->result = RESULT_TIMEOUT;
160 /* Clean up child process! */
162 } else if (errno == ECHILD) {
163 /* Child already exited somehow */
164 tr->result = RESULT_UNKNOWN;
168 snprintf(errbuf, errbuf_sz, "Could not "
169 "wait4(): %s", strerror(errno));
174 if (WIFEXITED(status)) {
175 tr->result = (WEXITSTATUS(status) == 0) ?
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;
186 tr->result = RESULT_UNKNOWN;
190 if (timeout != NULL) {
192 itim.it_value.tv_sec = 0;
193 itim.it_value.tv_usec = 0;
194 setitimer(ITIMER_REAL, &itim, NULL);
197 /* pid == 0, so we are the child */
199 /* Redirect stdout and stderr */
200 if (fd_stdout >= 0) {
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,
211 /* Set uid if necessary */
215 fprintf(stderr, "ERR: NOT RUN (setuid): %s",
222 r = execvp(binary, __DECONST(char **, argv));
225 * If we couldn't exec(), notify parent that we didn't
228 fprintf(stderr, "ERR: NOT RUN: %s", strerror(errno));
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';
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';
282 run_simple_cmd(const char *binary, const char *arg, char *errbuf,
283 size_t errbuf_sz, struct testcase_result *tr)
288 s = strrchr(binary, '/');
290 argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1;
291 argv[1] = __DECONST(char *, arg);
294 return run_userland(binary, argv, 0, 0, NULL, 1, errbuf, errbuf_sz, tr);