1 /* $OpenBSD: manager.c,v 1.7 2017/12/15 14:45:51 bluhm 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/types.h> 20 #include <sys/wait.h> 21 22 #include <ctype.h> 23 #include <err.h> 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <regex.h> 27 #include <signal.h> 28 #include <stdarg.h> 29 #include <stdlib.h> 30 #include <stdio.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "manager.h" 35 36 extern char *__progname; 37 38 static const char * 39 coredump_name() 40 { 41 static char coredump[PATH_MAX] = ""; 42 43 if (*coredump) 44 return (coredump); 45 46 if (strlcpy(coredump, __progname, sizeof(coredump)) >= sizeof(coredump)) 47 errx(1, "coredump: strlcpy"); 48 49 if (strlcat(coredump, ".core", sizeof(coredump)) >= sizeof(coredump)) 50 errx(1, "coredump: strlcat"); 51 52 return (coredump); 53 } 54 55 56 static int 57 check_coredump() 58 { 59 const char *coredump = coredump_name(); 60 int fd; 61 62 if ((fd = open(coredump, O_RDONLY)) == -1) { 63 if (errno == ENOENT) 64 return (1); /* coredump not found */ 65 else 66 return (-1); /* error */ 67 } 68 69 (void)close(fd); 70 return (0); /* coredump found */ 71 } 72 73 74 static int 75 clear_coredump(int *ret, const char *test_name) 76 { 77 int saved_errno = errno; 78 int u; 79 80 if (((u = unlink(coredump_name())) != 0) && (errno != ENOENT)) { 81 warn("test(%s): clear_coredump", test_name); 82 *ret = EXIT_FAILURE; 83 return (-1); 84 } 85 errno = saved_errno; 86 87 return (0); 88 } 89 90 91 static int 92 grab_syscall(pid_t pid) 93 { 94 int ret = -1; 95 char *pattern; 96 regex_t regex; 97 regmatch_t matches[2]; 98 FILE *fd; 99 char line[1024]; 100 int error; 101 const char *errstr; 102 103 /* build searched string */ 104 error = asprintf(&pattern, 105 "^%s\\[%d\\]: pledge \"[a-z]+\", syscall ([0-9]+)\n?$", 106 __progname, pid); 107 if (error <= 0) { 108 warn("asprintf pattern"); 109 return (-1); 110 } 111 error = regcomp(®ex, pattern, REG_EXTENDED); 112 if (error) { 113 warnx("regcomp pattern=%s error=%d", pattern, error); 114 free(pattern); 115 return (-1); 116 } 117 if (regex.re_nsub != 1) { 118 warnx("regcomp pattern=%s nsub=%zu", pattern, regex.re_nsub); 119 goto out; 120 } 121 122 /* call dmesg */ 123 if ((fd = popen("/sbin/dmesg", "r")) == NULL) { 124 warn("popen /sbin/dmesg"); 125 goto out; 126 } 127 128 /* search the string */ 129 while (1) { 130 /* read a line */ 131 fgets(line, sizeof(line), fd); 132 133 /* error checking */ 134 if (ferror(fd)) { 135 ret = -1; 136 goto out; 137 } 138 139 /* quit */ 140 if (feof(fd)) 141 break; 142 143 /* check if found */ 144 error = regexec(®ex, line, 2, matches, 0); 145 if (error == REG_NOMATCH) 146 continue; 147 if (error) { 148 warnx("regexec pattern=%s line=%s error=%d", 149 pattern, line, error); 150 ret = -1; 151 goto out; 152 } 153 154 /* convert it */ 155 line[matches[1].rm_eo] = '\0'; 156 ret = strtonum(&line[matches[1].rm_so], 0, 255, &errstr); 157 if (errstr) { 158 warnx("strtonum: number=%s error=%s", 159 &line[matches[1].rm_so], errstr); 160 ret = -1; 161 goto out; 162 } 163 } 164 165 /* cleanup */ 166 if (pclose(fd) == -1) 167 goto out; 168 169 /* not found */ 170 if (ret == -1) 171 ret = 0; 172 173 out: 174 free(pattern); 175 regfree(®ex); 176 return (ret); 177 } 178 179 /* mainly stolen from src/bin/cat/cat.c */ 180 static int 181 drainfd(int rfd, int wfd) 182 { 183 char buf[1024]; 184 ssize_t nr, nw, off; 185 186 while ((nr = read(rfd, buf, sizeof(buf))) != -1 && nr != 0) 187 for (off = 0; nr; nr -= nw, off += nw) 188 if ((nw = write(wfd, buf + off, (size_t)nr)) == 0 || 189 nw == -1) 190 return (-1); 191 if (nr < 0) 192 return (-1); 193 194 return (0); 195 } 196 197 void 198 _start_test(int *ret, const char *test_name, const char *request, 199 void (*test_func)(void)) 200 { 201 int fildes[2]; 202 pid_t pid; 203 int status; 204 205 /* early print testname */ 206 printf("test(%s): pledge=", test_name); 207 if (request) { 208 printf("(\"%s\",", request); 209 printf("NULL)"); 210 } else 211 printf("skip"); 212 213 /* unlink previous coredump (if exists) */ 214 if (clear_coredump(ret, test_name) == -1) 215 return; 216 217 /* flush outputs (for STDOUT_FILENO manipulation) */ 218 if (fflush(NULL) != 0) { 219 warn("test(%s) fflush", test_name); 220 *ret = EXIT_FAILURE; 221 return; 222 } 223 224 /* make pipe to grab output */ 225 if (pipe(fildes) != 0) { 226 warn("test(%s) pipe", test_name); 227 *ret = EXIT_FAILURE; 228 return; 229 } 230 231 /* fork and launch the test */ 232 switch (pid = fork()) { 233 case -1: 234 (void)close(fildes[0]); 235 (void)close(fildes[1]); 236 237 warn("test(%s) fork", test_name); 238 *ret = EXIT_FAILURE; 239 return; 240 241 case 0: 242 /* output to pipe */ 243 (void)close(fildes[0]); 244 while (dup2(fildes[1], STDOUT_FILENO) == -1) 245 if (errno != EINTR) 246 err(errno, "dup2"); 247 248 /* create a new session (for kill) */ 249 setsid(); 250 251 /* set pledge policy */ 252 if (request && pledge(request, NULL) != 0) 253 err(errno, "pledge"); 254 255 /* reset errno and launch test */ 256 errno = 0; 257 test_func(); 258 259 if (errno != 0) 260 _exit(errno); 261 262 _exit(EXIT_SUCCESS); 263 /* NOTREACHED */ 264 } 265 266 /* copy pipe to output */ 267 (void)close(fildes[1]); 268 if (drainfd(fildes[0], STDOUT_FILENO) != 0) { 269 warn("test(%s): drainfd", test_name); 270 *ret = EXIT_FAILURE; 271 return; 272 } 273 if (close(fildes[0]) != 0) { 274 warn("test(%s): close", test_name); 275 *ret = EXIT_FAILURE; 276 return; 277 } 278 279 /* wait for test to terminate */ 280 while (waitpid(pid, &status, 0) < 0) { 281 if (errno == EAGAIN) 282 continue; 283 warn("test(%s): waitpid", test_name); 284 *ret = EXIT_FAILURE; 285 return; 286 } 287 288 /* show status and details */ 289 printf(" status=%d", status); 290 291 if (WIFCONTINUED(status)) 292 printf(" continued"); 293 294 if (WIFEXITED(status)) { 295 int e = WEXITSTATUS(status); 296 printf(" exit=%d", e); 297 if (e > 0 && e <= ELAST) 298 printf(" (errno: \"%s\")", strerror(e)); 299 } 300 301 if (WIFSIGNALED(status)) { 302 int signal = WTERMSIG(status); 303 printf(" signal=%d", signal); 304 305 /* check if core file is really here ? */ 306 if (WCOREDUMP(status)) { 307 int coredump = check_coredump(); 308 309 switch(coredump) { 310 case -1: /* error */ 311 warn("test(%s): check_coredump", test_name); 312 *ret = EXIT_FAILURE; 313 return; 314 315 case 0: /* found */ 316 printf(" coredump=present"); 317 break; 318 319 case 1: /* not found */ 320 printf(" coredump=absent"); 321 break; 322 323 default: 324 warnx("test(%s): unknown coredump code %d", 325 test_name, coredump); 326 *ret = EXIT_FAILURE; 327 return; 328 } 329 330 } 331 332 /* grab pledged syscall from dmesg */ 333 if ((signal == SIGKILL) || (signal = SIGABRT)) { 334 int syscall = grab_syscall(pid); 335 switch (syscall) { 336 case -1: /* error */ 337 warn("test(%s): grab_syscall pid=%d", test_name, 338 pid); 339 *ret = EXIT_FAILURE; 340 return; 341 342 case 0: /* not found */ 343 printf(" pledged_syscall=not_found"); 344 break; 345 346 default: 347 printf(" pledged_syscall=%d", syscall); 348 } 349 } 350 } 351 352 if (WIFSTOPPED(status)) 353 printf(" stop=%d", WSTOPSIG(status)); 354 355 printf("\n"); 356 } 357