xref: /illumos-gate/usr/src/test/os-tests/tests/execvpe/execvpe-test.c (revision fb2cb638e5604b214d8ea8d4f01ad2e77b437c17)
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