1 /* $OpenBSD: manager.c,v 1.4 2015/10/23 18:50:54 mmcc Exp $ */ 2 /* 3 * Copyright (c) 2015 Sebastien Marie <semarie@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/syslimits.h> 19 #include <sys/wait.h> 20 21 #include <ctype.h> 22 #include <err.h> 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <signal.h> 26 #include <stdarg.h> 27 #include <stdlib.h> 28 #include <stdio.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 extern char *__progname; 33 34 static const char * 35 coredump_name() 36 { 37 static char coredump[PATH_MAX] = ""; 38 39 if (*coredump) 40 return (coredump); 41 42 if (strlcpy(coredump, __progname, sizeof(coredump)) >= sizeof(coredump)) 43 errx(1, "coredump: strlcpy"); 44 45 if (strlcat(coredump, ".core", sizeof(coredump)) >= sizeof(coredump)) 46 errx(1, "coredump: strlcat"); 47 48 return (coredump); 49 } 50 51 52 static int 53 check_coredump() 54 { 55 const char *coredump = coredump_name(); 56 int fd; 57 58 if ((fd = open(coredump, O_RDONLY)) == -1) { 59 if (errno == ENOENT) 60 return (1); /* coredump not found */ 61 else 62 return (-1); /* error */ 63 } 64 65 (void)close(fd); 66 return (0); /* coredump found */ 67 } 68 69 70 static int 71 clear_coredump(int *ret, const char *test_name) 72 { 73 int saved_errno = errno; 74 int u; 75 76 if (((u = unlink(coredump_name())) != 0) && (errno != ENOENT)) { 77 warn("test(%s): clear_coredump", test_name); 78 *ret = EXIT_FAILURE; 79 return (-1); 80 } 81 errno = saved_errno; 82 83 return (0); 84 } 85 86 87 static int 88 grab_syscall(pid_t pid) 89 { 90 int ret = -1; 91 char *search = NULL; 92 int searchlen; 93 FILE *fd; 94 char line[1024]; 95 char *end; 96 97 /* build searched string */ 98 if ((searchlen = asprintf(&search, "%s(%d): syscall ", __progname, pid)) 99 <= 0) 100 goto out; 101 102 /* call dmesg */ 103 if ((fd = popen("/sbin/dmesg", "r")) == NULL) 104 goto out; 105 106 /* search the string */ 107 while (1) { 108 /* read a line */ 109 fgets(line, sizeof(line), fd); 110 111 /* error checking */ 112 if (ferror(fd)) { 113 ret = -1; 114 goto out; 115 } 116 117 /* quit */ 118 if (feof(fd)) 119 break; 120 121 /* strip trailing '\n' */ 122 end = strchr(line, '\n'); 123 if (*end == '\n') 124 *end = '\0'; 125 126 /* check if found */ 127 if (strncmp(search, line, searchlen) == 0) { 128 const char *errstr = NULL; 129 char *c; 130 /* truncate at first no-number */ 131 for (c = line + searchlen; (*c != '\0') && isdigit((unsigned char)*c); 132 c++) 133 ; 134 *c = '\0'; 135 136 /* convert it */ 137 ret = strtonum(line + searchlen, 0, 255, &errstr); 138 if (errstr) { 139 warn("strtonum: line=%s err=%s", line, errstr); 140 return (-1); 141 } 142 } 143 } 144 145 /* cleanup */ 146 if (pclose(fd) == -1) 147 goto out; 148 149 /* not found */ 150 if (ret == -1) 151 ret = 0; 152 153 out: 154 free(search); 155 return (ret); 156 } 157 158 /* mainly stolen from src/bin/cat/cat.c */ 159 static int 160 drainfd(int rfd, int wfd) 161 { 162 char buf[1024]; 163 ssize_t nr, nw, off; 164 165 while ((nr = read(rfd, buf, sizeof(buf))) != -1 && nr != 0) 166 for (off = 0; nr; nr -= nw, off += nw) 167 if ((nw = write(wfd, buf + off, (size_t)nr)) == 0 || 168 nw == -1) 169 return (-1); 170 if (nr < 0) 171 return (-1); 172 173 return (0); 174 } 175 176 void 177 _start_test(int *ret, const char *test_name, const char *request, 178 const char *paths[], void (*test_func)(void)) 179 { 180 int fildes[2]; 181 pid_t pid; 182 int status; 183 int i; 184 185 /* early print testname */ 186 printf("test(%s): pledge=", test_name); 187 if (request) { 188 printf("(\"%s\",", request); 189 if (paths) { 190 printf("{"); 191 for (i = 0; paths[i] != NULL; i++) 192 printf("\"%s\",", paths[i]); 193 printf("NULL})"); 194 } else 195 printf("NULL)"); 196 } else 197 printf("skip"); 198 199 /* unlink previous coredump (if exists) */ 200 if (clear_coredump(ret, test_name) == -1) 201 return; 202 203 /* flush outputs (for STDOUT_FILENO manipulation) */ 204 if (fflush(NULL) != 0) { 205 warn("test(%s) fflush", test_name); 206 *ret = EXIT_FAILURE; 207 return; 208 } 209 210 /* make pipe to grab output */ 211 if (pipe(fildes) != 0) { 212 warn("test(%s) pipe", test_name); 213 *ret = EXIT_FAILURE; 214 return; 215 } 216 217 /* fork and launch the test */ 218 switch (pid = fork()) { 219 case -1: 220 (void)close(fildes[0]); 221 (void)close(fildes[1]); 222 223 warn("test(%s) fork", test_name); 224 *ret = EXIT_FAILURE; 225 return; 226 227 case 0: 228 /* output to pipe */ 229 (void)close(fildes[0]); 230 while (dup2(fildes[1], STDOUT_FILENO) == -1) 231 if (errno != EINTR) 232 err(errno, "dup2"); 233 234 /* create a new session (for kill) */ 235 setsid(); 236 237 /* set pledge policy */ 238 if (request && pledge(request, paths) != 0) 239 err(errno, "pledge"); 240 241 /* reset errno and launch test */ 242 errno = 0; 243 test_func(); 244 245 if (errno != 0) 246 _exit(errno); 247 248 _exit(EXIT_SUCCESS); 249 /* NOTREACHED */ 250 } 251 252 /* copy pipe to output */ 253 (void)close(fildes[1]); 254 if (drainfd(fildes[0], STDOUT_FILENO) != 0) { 255 warn("test(%s): drainfd", test_name); 256 *ret = EXIT_FAILURE; 257 return; 258 } 259 if (close(fildes[0]) != 0) { 260 warn("test(%s): close", test_name); 261 *ret = EXIT_FAILURE; 262 return; 263 } 264 265 /* wait for test to terminate */ 266 while (waitpid(pid, &status, 0) < 0) { 267 if (errno == EAGAIN) 268 continue; 269 warn("test(%s): waitpid", test_name); 270 *ret = EXIT_FAILURE; 271 return; 272 } 273 274 /* show status and details */ 275 printf(" status=%d", status); 276 277 if (WIFCONTINUED(status)) 278 printf(" continued"); 279 280 if (WIFEXITED(status)) { 281 int e = WEXITSTATUS(status); 282 printf(" exit=%d", e); 283 if (e > 0 && e <= ELAST) 284 printf(" (errno: \"%s\")", strerror(e)); 285 } 286 287 if (WIFSIGNALED(status)) { 288 int signal = WTERMSIG(status); 289 printf(" signal=%d", signal); 290 291 /* check if core file is really here ? */ 292 if (WCOREDUMP(status)) { 293 int coredump = check_coredump(); 294 295 switch(coredump) { 296 case -1: /* error */ 297 warn("test(%s): check_coredump", test_name); 298 *ret = EXIT_FAILURE; 299 return; 300 301 case 0: /* found */ 302 printf(" coredump=present"); 303 break; 304 305 case 1: /* not found */ 306 printf(" coredump=absent"); 307 break; 308 309 default: 310 warnx("test(%s): unknown coredump code %d", 311 test_name, coredump); 312 *ret = EXIT_FAILURE; 313 return; 314 } 315 316 } 317 318 /* grab pledged syscall from dmesg */ 319 if ((signal == SIGKILL) || (signal = SIGABRT)) { 320 int syscall = grab_syscall(pid); 321 switch (syscall) { 322 case -1: /* error */ 323 warn("test(%s): grab_syscall pid=%d", test_name, 324 pid); 325 *ret = EXIT_FAILURE; 326 return; 327 328 case 0: /* not found */ 329 printf(" pledged_syscall=not_found"); 330 break; 331 332 default: 333 printf(" pledged_syscall=%d", syscall); 334 } 335 } 336 } 337 338 if (WIFSTOPPED(status)) 339 printf(" stop=%d", WSTOPSIG(status)); 340 341 printf("\n"); 342 } 343