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';
270 if (fd_stdout
>= 0) {
275 if (fd_stderr
>= 0) {
284 run_simple_cmd(const char *binary
, const char *arg
, char *errbuf
,
285 size_t errbuf_sz
, struct testcase_result
*tr
)
290 s
= strrchr(binary
, '/');
292 argv
[0] = (s
== NULL
) ? __DECONST(char *, binary
) : s
+1;
293 argv
[1] = __DECONST(char *, arg
);
296 return run_userland(binary
, argv
, 0, 0, NULL
, 1, errbuf
, errbuf_sz
, tr
);