1 /* 2 * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 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. 15 * 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. 28 */ 29 30 #include <sys/resource.h> 31 #include <sys/time.h> 32 #include <sys/types.h> 33 #include <sys/wait.h> 34 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> 44 45 #include <err.h> 46 47 #include <libprop/proplib.h> 48 49 #include "testcase.h" 50 #include "runlist.h" 51 #include "userland.h" 52 #include <dfregress.h> 53 54 static void 55 clean_child(pid_t pid) 56 { 57 kill(pid, SIGKILL); 58 } 59 60 static void 61 sig_handle(int sig __unused) 62 { 63 return; 64 } 65 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) 70 { 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; 80 81 /* Set sane defaults */ 82 bzero(tr, sizeof(*tr)); 83 tr->result = RESULT_NOTRUN; 84 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; 93 } 94 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; 102 } 103 } 104 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; 111 } 112 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; 120 } 121 } 122 123 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)); 128 129 goto err_out; 130 } else if (pid > 0) { 131 /* parent */ 132 133 if (timeout != NULL) { 134 /* Ignore SIGALRM */ 135 bzero(&sa, sizeof(sa)); 136 sa.sa_handler = sig_handle; 137 sigaction(SIGALRM, &sa, NULL); 138 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)); 148 149 /* Clean up child process! */ 150 goto err_out; 151 } 152 } 153 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; 159 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)); 170 171 goto err_out; 172 } 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; 179 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; 187 } 188 } 189 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); 195 } 196 } else { 197 /* pid == 0, so we are the child */ 198 199 /* Redirect stdout and stderr */ 200 if (fd_stdout >= 0) { 201 dup2(fd_stdout, 1); 202 setvbuf(stdout, NULL, _IONBF, 0); 203 } 204 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); 209 } 210 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); 218 } 219 } 220 221 /* Try to exec() */ 222 r = execvp(binary, __DECONST(char **, argv)); 223 if (r == -1) { 224 /* 225 * If we couldn't exec(), notify parent that we didn't 226 * run. 227 */ 228 fprintf(stderr, "ERR: NOT RUN: %s", strerror(errno)); 229 exit(EXIT_NOTRUN); 230 } 231 } 232 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); 236 237 tr->stdout_buf = malloc(sz_stdout + 1); 238 if (tr->stdout_buf == NULL) 239 err(1, "could not malloc fd buf memory"); 240 241 read(fd_stdout, tr->stdout_buf, sz_stdout); 242 tr->stdout_buf[sz_stdout] = '\0'; 243 244 close(fd_stdout); 245 unlink(stdout_file); 246 247 if (!unify_output) { 248 sz_stderr = (size_t)lseek(fd_stderr, 0, SEEK_END); 249 lseek(fd_stderr, 0, SEEK_SET); 250 251 tr->stderr_buf = malloc(sz_stderr + 1); 252 if (tr->stderr_buf == NULL) 253 err(1, "could not malloc fd buf memory"); 254 255 read(fd_stderr, tr->stderr_buf, sz_stderr); 256 tr->stderr_buf[sz_stderr] = '\0'; 257 258 close(fd_stderr); 259 unlink(stderr_file); 260 } 261 262 263 return 0; 264 /* NOTREACHED */ 265 266 err_out: 267 if (pid != -1) 268 clean_child(pid); 269 270 if (fd_stdout >= 0) { 271 close(fd_stdout); 272 unlink(stdout_file); 273 } 274 275 if (fd_stderr >= 0) { 276 close(fd_stderr); 277 unlink(stderr_file); 278 } 279 280 return -1; 281 } 282 283 int 284 run_simple_cmd(const char *binary, const char *arg, char *errbuf, 285 size_t errbuf_sz, struct testcase_result *tr) 286 { 287 const char *argv[3]; 288 char *s; 289 290 s = strrchr(binary, '/'); 291 292 argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1; 293 argv[1] = __DECONST(char *, arg); 294 argv[2] = NULL; 295 296 return run_userland(binary, argv, 0, 0, NULL, 1, errbuf, errbuf_sz, tr); 297 } 298