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
, 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
;
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 fd_stdout
= mkostemp(stdout_file
, O_SYNC
);
88 if (fd_stdout
== -1) {
90 snprintf(errbuf
, errbuf_sz
, "Could not mkostemp(): "
91 "%s\n", strerror(errno
));
96 fd_stderr
= mkostemp(stderr_file
, O_SYNC
);
97 if (fd_stderr
== -1) {
99 snprintf(errbuf
, errbuf_sz
, "Could not mkostemp(): "
100 "%s\n", strerror(errno
));
106 if ((pid
= fork()) == -1) {
108 snprintf(errbuf
, errbuf_sz
, "Could not fork to run "
109 "binary %s: %s\n", binary
, strerror(errno
));
112 } else if (pid
> 0) {
115 if (timeout
!= NULL
) {
117 bzero(&sa
, sizeof(sa
));
118 sa
.sa_handler
= sig_handle
;
119 sigaction(SIGALRM
, &sa
, NULL
);
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
);
128 snprintf(errbuf
, errbuf_sz
, "Could not "
129 "set up timer: %s", strerror(errno
));
131 /* Clean up child process! */
136 r_pid
= wait4(pid
, &status
, 0, &tr
->rusage
);
138 if (errno
== EINTR
) {
139 /* Alarm timed out */
140 tr
->result
= RESULT_TIMEOUT
;
142 /* Clean up child process! */
144 } else if (errno
== ECHILD
) {
145 /* Child already exited somehow */
146 tr
->result
= RESULT_UNKNOWN
;
150 snprintf(errbuf
, errbuf_sz
, "Could not "
151 "wait4(): %s", strerror(errno
));
156 if (WIFEXITED(status
)) {
157 tr
->result
= (WEXITSTATUS(status
) == rc
) ?
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;
168 tr
->result
= RESULT_UNKNOWN
;
172 if (timeout
!= NULL
) {
174 itim
.it_value
.tv_sec
= 0;
175 itim
.it_value
.tv_usec
= 0;
176 setitimer(ITIMER_REAL
, &itim
, NULL
);
179 /* pid == 0, so we are the child */
181 /* Redirect stdout and stderr */
182 if (fd_stdout
>= 0) {
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
,
193 /* Set uid if necessary */
197 fprintf(stderr
, "ERR: NOT RUN (setuid): %s",
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",
222 /* We still need argv[0] when argc is 0 */
223 for (int i
= 0; i
<= argc
; i
++) {
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",
235 /* Null terminate the array */
236 argv_copy
[argc
+ 2] = NULL
;
237 r
= execvp(interpreter
, argv_copy
);
240 r
= execvp(binary
, __DECONST(char **, argv
));
244 * If we couldn't exec(), notify parent that we didn't
247 fprintf(stderr
, "ERR: NOT RUN: %s", strerror(errno
));
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';
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';
289 if (fd_stdout
>= 0) {
294 if (fd_stderr
>= 0) {
303 run_simple_cmd(const char *binary
, const char *arg
, char *errbuf
,
304 size_t errbuf_sz
, struct testcase_result
*tr
)
309 s
= strrchr(binary
, '/');
311 argv
[0] = (s
== NULL
) ? __DECONST(char *, binary
) : s
+1;
312 argv
[1] = __DECONST(char *, arg
);
315 return run_userland(binary
, /* executable */
318 NULL
, /* interpreter */
319 0, /* needs_setuid */
323 1, /* unify_output */
325 errbuf_sz
, /* errbuf_size */
326 tr
); /* testcase_result */