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 80 /* Set sane defaults */ 81 bzero(tr, sizeof(*tr)); 82 tr->result = RESULT_NOTRUN; 83 84 strcpy(stdout_file, "/tmp/dfregress.XXXXXXXXXXXX"); 85 strcpy(stderr_file, "/tmp/dfregress.XXXXXXXXXXXX"); 86 fd_stdout = mkostemp(stdout_file, O_SYNC); 87 if (fd_stdout == -1) { 88 if (errbuf) 89 snprintf(errbuf, errbuf_sz, "Could not mkostemp(): " 90 "%s\n", strerror(errno)); 91 return -1; 92 } 93 94 if (!unify_output) { 95 fd_stderr = mkostemp(stderr_file, O_SYNC); 96 if (fd_stderr == -1) { 97 if (errbuf) 98 snprintf(errbuf, errbuf_sz, "Could not mkostemp(): " 99 "%s\n", strerror(errno)); 100 return -1; 101 } 102 } 103 104 105 if ((pid = fork()) == -1) { 106 if (errbuf) 107 snprintf(errbuf, errbuf_sz, "Could not fork to run " 108 "binary %s: %s\n", binary, strerror(errno)); 109 110 goto err_out; 111 } else if (pid > 0) { 112 /* parent */ 113 114 if (timeout != NULL) { 115 /* Ignore SIGALRM */ 116 bzero(&sa, sizeof(sa)); 117 sa.sa_handler = sig_handle; 118 sigaction(SIGALRM, &sa, NULL); 119 120 /* Set up timeout */ 121 itim.it_interval.tv_sec = 0; 122 itim.it_interval.tv_usec = 0; 123 itim.it_value = *timeout; 124 r = setitimer(ITIMER_REAL, &itim, NULL); 125 if (r == -1) { 126 if (errbuf) 127 snprintf(errbuf, errbuf_sz, "Could not " 128 "set up timer: %s", strerror(errno)); 129 130 /* Clean up child process! */ 131 goto err_out; 132 } 133 } 134 135 r_pid = wait4(pid, &status, 0, &tr->rusage); 136 if (r_pid == -1) { 137 if (errno == EINTR) { 138 /* Alarm timed out */ 139 tr->result = RESULT_TIMEOUT; 140 141 /* Clean up child process! */ 142 clean_child(pid); 143 } else if (errno == ECHILD) { 144 /* Child already exited somehow */ 145 tr->result = RESULT_UNKNOWN; 146 } else { 147 /* EFAULT */ 148 if (errbuf) 149 snprintf(errbuf, errbuf_sz, "Could not " 150 "wait4(): %s", strerror(errno)); 151 152 goto err_out; 153 } 154 } else { 155 if (WIFEXITED(status)) { 156 tr->result = (WEXITSTATUS(status) == 0) ? 157 RESULT_PASS : 158 (WEXITSTATUS(status) == EXIT_NOTRUN) ? 159 RESULT_NOTRUN : RESULT_FAIL; 160 161 tr->exit_value = WEXITSTATUS(status); 162 } else if (WIFSIGNALED(status)) { 163 tr->result = RESULT_SIGNALLED; 164 tr->signal = WTERMSIG(status); 165 tr->core_dumped = (WCOREDUMP(status)) ? 1 : 0; 166 } else { 167 tr->result = RESULT_UNKNOWN; 168 } 169 } 170 171 if (timeout != NULL) { 172 /* Disable timer */ 173 itim.it_value.tv_sec = 0; 174 itim.it_value.tv_usec = 0; 175 setitimer(ITIMER_REAL, &itim, NULL); 176 } 177 } else { 178 /* pid == 0, so we are the child */ 179 180 /* Redirect stdout and stderr */ 181 if (fd_stdout >= 0) { 182 dup2(fd_stdout, 1); 183 setvbuf(stdout, NULL, _IONBF, 0); 184 } 185 186 if ((fd_stderr >= 0) || (unify_output && fd_stdout >= 0)) { 187 dup2((unify_output) ? fd_stdout : fd_stderr, 2); 188 setvbuf((unify_output) ? stdout : stderr, 189 NULL, _IONBF, 0); 190 } 191 192 /* Set uid if necessary */ 193 if (need_setuid) { 194 r = setuid(uid); 195 if (r == -1) { 196 fprintf(stderr, "ERR: NOT RUN (setuid): %s", 197 strerror(errno)); 198 exit(EXIT_NOTRUN); 199 } 200 } 201 202 /* Try to exec() */ 203 r = execvp(binary, __DECONST(char **, argv)); 204 if (r == -1) { 205 /* 206 * If we couldn't exec(), notify parent that we didn't 207 * run. 208 */ 209 fprintf(stderr, "ERR: NOT RUN: %s", strerror(errno)); 210 exit(EXIT_NOTRUN); 211 } 212 } 213 214 /* Read stdout and stderr redirected file contents into memory */ 215 sz_stdout = (size_t)lseek(fd_stdout, 0, SEEK_END); 216 lseek(fd_stdout, 0, SEEK_SET); 217 218 tr->stdout_buf = malloc(sz_stdout + 1); 219 if (tr->stdout_buf == NULL) 220 err(1, "could not malloc fd buf memory"); 221 222 read(fd_stdout, tr->stdout_buf, sz_stdout); 223 tr->stdout_buf[sz_stdout] = '\0'; 224 225 close(fd_stdout); 226 unlink(stdout_file); 227 228 if (!unify_output) { 229 sz_stderr = (size_t)lseek(fd_stderr, 0, SEEK_END); 230 lseek(fd_stderr, 0, SEEK_SET); 231 232 tr->stderr_buf = malloc(sz_stderr + 1); 233 if (tr->stderr_buf == NULL) 234 err(1, "could not malloc fd buf memory"); 235 236 read(fd_stderr, tr->stderr_buf, sz_stderr); 237 tr->stderr_buf[sz_stderr] = '\0'; 238 239 close(fd_stderr); 240 unlink(stderr_file); 241 } 242 243 244 return 0; 245 /* NOTREACHED */ 246 247 err_out: 248 if (pid != -1) 249 clean_child(pid); 250 251 if (fd_stdout >= 0) { 252 close(fd_stdout); 253 unlink(stdout_file); 254 } 255 256 if (fd_stderr >= 0) { 257 close(fd_stderr); 258 unlink(stderr_file); 259 } 260 261 return -1; 262 } 263 264 int 265 run_simple_cmd(const char *binary, const char *arg, char *errbuf, 266 size_t errbuf_sz, struct testcase_result *tr) 267 { 268 const char *argv[3]; 269 char *s; 270 271 s = strrchr(binary, '/'); 272 273 argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1; 274 argv[1] = __DECONST(char *, arg); 275 argv[2] = NULL; 276 277 return run_userland(binary, argv, 0, 0, NULL, 1, errbuf, errbuf_sz, tr); 278 } 279