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 * This tests various behaviors of execvpe to try and verify that it is working 18 * as expected. 19 */ 20 21 #include <stdlib.h> 22 #include <err.h> 23 #include <unistd.h> 24 #include <stdbool.h> 25 #include <errno.h> 26 #include <sys/sysmacros.h> 27 #include <sys/fork.h> 28 #include <sys/types.h> 29 #include <sys/wait.h> 30 #include <stdio.h> 31 32 typedef struct { 33 const char *et_desc; 34 const char *et_prog; 35 const char *et_path; 36 bool et_pass; 37 int et_errno; 38 } execvpe_test_t; 39 40 static const execvpe_test_t execvpe_tests[] = { 41 { .et_desc = "execute ls on default path", .et_prog = "ls", 42 .et_path = NULL, .et_pass = true }, 43 { .et_desc = "execute ls on specified path (1)", .et_prog = "ls", 44 .et_path = "/usr/bin", .et_pass = true }, 45 { .et_desc = "execute ls on specified path (2)", .et_prog = "ls", 46 .et_path = "/usr/lib/fm/topo/maps:/enoent:/usr/bin", 47 .et_pass = true }, 48 { .et_desc = "fail to find ls on path", .et_prog = "ls", 49 .et_path = "/usr/lib/mdb/raw", .et_pass = false, 50 .et_errno = ENOENT }, 51 { .et_desc = "fail to find program on default path", 52 .et_prog = "()theroadgoeseveronandon?!@#$%,downfromthedoor", 53 .et_path = NULL, .et_pass = false, .et_errno = ENOENT }, 54 { .et_desc = "shell executes script without #!", 55 .et_prog = "execvpe-script", 56 .et_path = "/opt/os-tests/tests/execvpe", .et_pass = true }, 57 { .et_desc = "properly fail with non-executable file", 58 .et_prog = "execvpe-noperm", 59 .et_path = "/opt/os-tests/tests/execvpe", .et_pass = false, 60 .et_errno = EACCES }, 61 { .et_desc = "absolute path works if not in PATH", 62 .et_prog = "/usr/bin/true", .et_path = "/usr/lib", .et_pass = true } 63 }; 64 65 static bool 66 execvpe_test_one(const execvpe_test_t *test) 67 { 68 pid_t pid, wpid; 69 int stat; 70 const char *envp[4]; 71 const char *argv[2]; 72 73 if (test->et_path != NULL) { 74 if (setenv("PATH", test->et_path, 1) != 0) { 75 err(EXIT_FAILURE, "TEST FAILED: %s: fatal error: " 76 "failed to set PATH", test->et_desc); 77 } 78 } else { 79 if (unsetenv("PATH") != 0) { 80 err(EXIT_FAILURE, "TEST FAILED: %s: fatal error: " 81 "failed to unset PATH", test->et_desc); 82 } 83 } 84 85 envp[0] = "PATH=/this/should/not/interfere:/with/the/test"; 86 envp[1] = "EXECVPE_TEST=Keep it secret, keep it safe!"; 87 envp[2] = "WHOAMI=gandalf"; 88 envp[3] = NULL; 89 90 argv[0] = test->et_prog; 91 argv[1] = NULL; 92 93 pid = forkx(FORK_NOSIGCHLD | FORK_WAITPID); 94 if (pid == 0) { 95 int ret = EXIT_SUCCESS, e; 96 (void) execvpe(test->et_prog, (char * const *)argv, 97 (char *const *)envp); 98 e = errno; 99 if (test->et_pass) { 100 warnc(e, "TEST FAILED: %s: expected execvpe success, " 101 "but no such luck", test->et_desc); 102 ret = EXIT_FAILURE; 103 } else if (test->et_errno != e) { 104 warnx("TEST FAILED: %s: execvpe failed with errno %d, " 105 "expected %d", test->et_desc, e, test->et_errno); 106 ret = EXIT_FAILURE; 107 } 108 _exit(ret); 109 } 110 111 wpid = waitpid(pid, &stat, 0); 112 if (wpid != pid) { 113 errx(EXIT_FAILURE, "TEST FAILED: %s: encountered fatal error " 114 "waitpid returned wrong pid: %" _PRIdID ", expected " 115 "%" _PRIdID, test->et_desc, wpid, pid); 116 } 117 118 if (stat == EXIT_SUCCESS) { 119 (void) printf("TEST PASSED: %s\n", test->et_desc); 120 return (true); 121 } 122 123 return (false); 124 } 125 126 int 127 main(void) 128 { 129 int ret = EXIT_SUCCESS; 130 131 for (size_t i = 0; i < ARRAY_SIZE(execvpe_tests); i++) { 132 if (!execvpe_test_one(&execvpe_tests[i])) 133 ret = EXIT_FAILURE; 134 } 135 136 if (ret == EXIT_SUCCESS) { 137 (void) printf("All tests passed successfully!\n"); 138 } 139 140 return (ret); 141 } 142