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