1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2024 Oxide Computer Company 14 */ 15 16 /* 17 * Verify that our file descriptors starting after stderr are correct based upon 18 * the series of passed in arguments from the 'oclo' program. Arguments are 19 * passed as a string that represents the flags that were originally verified 20 * pre-fork/exec via fcntl(F_GETFD). In addition, anything that was originally 21 * closed because it had FD_CLOFORK set was reopened with the same flags so we 22 * can verify that things with only FD_CLOFORK survive exec. 23 */ 24 25 #include <err.h> 26 #include <stdlib.h> 27 #include <unistd.h> 28 #include <fcntl.h> 29 #include <stdbool.h> 30 #include <errno.h> 31 #include <string.h> 32 33 static int 34 verify_fdwalk_cb(void *arg, int fd) 35 { 36 int *max = arg; 37 *max = fd; 38 return (0); 39 } 40 41 static bool 42 verify_flags(int fd, int exp_flags) 43 { 44 bool fail = (exp_flags & FD_CLOEXEC) != 0; 45 int flags = fcntl(fd, F_GETFD, NULL); 46 47 if (flags < 0) { 48 int e = errno; 49 50 if (fail) { 51 if (e == EBADF) { 52 (void) printf("TEST PASSED: post-exec fd %d: " 53 "flags 0x%x: correctly closed\n", fd, 54 exp_flags); 55 return (true); 56 } 57 58 59 warn("TEST FAILED: post-fork fd %d: expected fcntl to " 60 "fail with EBADF, but found %s", fd, 61 strerrorname_np(e)); 62 return (false); 63 } 64 65 warnx("TEST FAILED: post-fork fd %d: fcntl(F_GETFD) " 66 "unexpectedly failed with %s, expected flags %d", fd, 67 strerrorname_np(e), exp_flags); 68 return (false); 69 } 70 71 if (fail) { 72 warnx("TEST FAILED: post-fork fd %d: received flags %d, but " 73 "expected to fail based on flags %d", fd, flags, exp_flags); 74 return (false); 75 } 76 77 if (flags != exp_flags) { 78 warnx("TEST FAILED: post-exec fd %d: discovered flags 0x%x do " 79 "not match expected flags 0x%x", fd, flags, exp_flags); 80 return (false); 81 } 82 83 (void) printf("TEST PASSED: post-exec fd %d: flags 0x%x: successfully " 84 "matched\n", fd, exp_flags); 85 return (true); 86 } 87 88 int 89 main(int argc, char *argv[]) 90 { 91 int maxfd = STDIN_FILENO; 92 int ret = EXIT_SUCCESS; 93 94 /* 95 * We should have one argument for each fd we found, ignoring stdin, 96 * stdout, and stderr. argc will also have an additional entry for our 97 * program name, which we want to skip. Note, the last fd may not exist 98 * because it was marked for close, hence the use of '>' below. 99 */ 100 (void) fdwalk(verify_fdwalk_cb, &maxfd); 101 if (maxfd - 3 > argc - 1) { 102 errx(EXIT_FAILURE, "TEST FAILED: found more fds %d than " 103 "arguments %d", maxfd - 3, argc - 1); 104 } 105 106 for (int i = 1; i < argc; i++) { 107 const char *errstr; 108 int targ_fd = i + STDERR_FILENO; 109 long long targ_flags = strtonumx(argv[i], 0, 110 FD_CLOEXEC | FD_CLOFORK, &errstr, 0); 111 112 if (errstr != NULL) { 113 errx(EXIT_FAILURE, "TEST FAILED: failed to parse " 114 "argument %d: %s is %s", i, argv[i], errstr); 115 } 116 117 if (!verify_flags(targ_fd, (int)targ_flags)) 118 ret = EXIT_FAILURE; 119 } 120 121 return (ret); 122 } 123