1 /*- 2 * Copyright (c) 2011 Jilles Tjoelker 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 /* 28 * Test program for posix_spawn() and posix_spawnp() as specified by 29 * IEEE Std. 1003.1-2008. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/stat.h> 34 #include <sys/wait.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <spawn.h> 41 42 #include <atf-c.h> 43 44 static const char true_script[] = 45 "#!/usr/bin/env\n" 46 "/usr/bin/true\n"; 47 48 char *myenv[2] = { "answer=42", NULL }; 49 50 ATF_TC_WITHOUT_HEAD(posix_spawn_simple_test); 51 ATF_TC_BODY(posix_spawn_simple_test, tc) 52 { 53 char *myargs[4]; 54 int error, status; 55 pid_t pid, waitres; 56 57 /* Make sure we have no child processes. */ 58 while (waitpid(-1, NULL, 0) != -1) 59 ; 60 ATF_REQUIRE_MSG(errno == ECHILD, "errno was not ECHILD: %d", errno); 61 62 /* Simple test. */ 63 myargs[0] = "sh"; 64 myargs[1] = "-c"; 65 myargs[2] = "exit $answer"; 66 myargs[3] = NULL; 67 error = posix_spawnp(&pid, myargs[0], NULL, NULL, myargs, myenv); 68 ATF_REQUIRE(error == 0); 69 waitres = waitpid(pid, &status, 0); 70 ATF_REQUIRE(waitres == pid); 71 ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42); 72 } 73 74 ATF_TC_WITHOUT_HEAD(posix_spawn_no_such_command_negative_test); 75 ATF_TC_BODY(posix_spawn_no_such_command_negative_test, tc) 76 { 77 char *myargs[4]; 78 int error, status; 79 pid_t pid, waitres; 80 81 /* 82 * If the executable does not exist, the function shall either fail 83 * and not create a child process or succeed and create a child 84 * process that exits with status 127. 85 */ 86 myargs[0] = "/var/empty/nonexistent"; 87 myargs[1] = NULL; 88 error = posix_spawn(&pid, myargs[0], NULL, NULL, myargs, myenv); 89 if (error == 0) { 90 waitres = waitpid(pid, &status, 0); 91 ATF_REQUIRE(waitres == pid); 92 ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 127); 93 } else { 94 ATF_REQUIRE(error == ENOENT); 95 waitres = waitpid(-1, NULL, 0); 96 ATF_REQUIRE(waitres == -1 && errno == ECHILD); 97 } 98 } 99 100 ATF_TC_WITHOUT_HEAD(posix_spawnp_enoexec_fallback); 101 ATF_TC_BODY(posix_spawnp_enoexec_fallback, tc) 102 { 103 char buf[FILENAME_MAX]; 104 char *myargs[2]; 105 int error, status; 106 pid_t pid, waitres; 107 108 snprintf(buf, sizeof(buf), "%s/spawnp_enoexec.sh", 109 atf_tc_get_config_var(tc, "srcdir")); 110 myargs[0] = buf; 111 myargs[1] = NULL; 112 error = posix_spawnp(&pid, myargs[0], NULL, NULL, myargs, myenv); 113 ATF_REQUIRE(error == 0); 114 waitres = waitpid(pid, &status, 0); 115 ATF_REQUIRE(waitres == pid); 116 ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42); 117 } 118 119 ATF_TC_WITHOUT_HEAD(posix_spawnp_enoexec_fallback_null_argv0); 120 ATF_TC_BODY(posix_spawnp_enoexec_fallback_null_argv0, tc) 121 { 122 char buf[FILENAME_MAX]; 123 char *myargs[1]; 124 int error; 125 pid_t pid; 126 127 snprintf(buf, sizeof(buf), "%s/spawnp_enoexec.sh", 128 atf_tc_get_config_var(tc, "srcdir")); 129 myargs[0] = NULL; 130 error = posix_spawnp(&pid, buf, NULL, NULL, myargs, myenv); 131 ATF_REQUIRE(error == EINVAL); 132 } 133 134 ATF_TC(posix_spawnp_eacces); 135 ATF_TC_HEAD(posix_spawnp_eacces, tc) 136 { 137 atf_tc_set_md_var(tc, "descr", "Verify EACCES behavior in posix_spawnp"); 138 atf_tc_set_md_var(tc, "require.user", "unprivileged"); 139 } 140 ATF_TC_BODY(posix_spawnp_eacces, tc) 141 { 142 const struct spawnp_eacces_tc { 143 const char *pathvar; 144 int error_expected; 145 } spawnp_eacces_tests[] = { 146 { ".", EACCES }, /* File exists, but not +x */ 147 { "unsearchable", ENOENT }, /* File exists, dir not +x */ 148 }; 149 char *myargs[2] = { "eacces", NULL }; 150 int error; 151 152 error = mkdir("unsearchable", 0755); 153 ATF_REQUIRE(error == 0); 154 error = symlink("/usr/bin/true", "unsearchable/eacces"); 155 ATF_REQUIRE(error == 0); 156 157 (void)chmod("unsearchable", 0444); 158 159 /* this will create a non-executable file */ 160 atf_utils_create_file("eacces", true_script); 161 162 for (size_t i = 0; i < nitems(spawnp_eacces_tests); i++) { 163 const struct spawnp_eacces_tc *tc = &spawnp_eacces_tests[i]; 164 pid_t pid; 165 166 error = setenv("PATH", tc->pathvar, 1); 167 ATF_REQUIRE_EQ(0, error); 168 169 error = posix_spawnp(&pid, myargs[0], NULL, NULL, myargs, 170 myenv); 171 ATF_CHECK_INTEQ_MSG(tc->error_expected, error, 172 "path '%s'", tc->pathvar); 173 } 174 } 175 176 ATF_TP_ADD_TCS(tp) 177 { 178 179 ATF_TP_ADD_TC(tp, posix_spawn_simple_test); 180 ATF_TP_ADD_TC(tp, posix_spawn_no_such_command_negative_test); 181 ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback); 182 ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback_null_argv0); 183 ATF_TP_ADD_TC(tp, posix_spawnp_eacces); 184 185 return (atf_no_error()); 186 } 187