1/* 2 * Copyright (c) 2006-2012 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * [SPN] Support for _POSIX_SPAWN 26 */ 27 28#include <spawn.h> 29#include <spawn_private.h> 30#include <sys/spawn_internal.h> 31#include <stdlib.h> 32#include <errno.h> 33#include <limits.h> /* for OPEN_MAX, PATH_MAX */ 34#include <string.h> /* for strlcpy() */ 35#include <paths.h> /* for _PATH_DEFPATH */ 36#include <sys/stat.h> /* for struct stat */ 37 38/* 39 * posix_spawnp 40 * 41 * Description: Create a new process from the process image corresponding to 42 * the supplied 'file' argument and the parent processes path 43 * environment. 44 * 45 * Parameters: pid Pointer to pid_t to receive the 46 * PID of the spawned process, if 47 * successful and 'pid' != NULL 48 * file Name of image file to spawn 49 * file_actions spawn file actions object which 50 * describes file actions to be 51 * performed during the spawn 52 * attrp spawn attributes object which 53 * describes attributes to be 54 * applied during the spawn 55 * argv argument vector array; NULL 56 * terminated 57 * envp environment vector array; NULL 58 * terminated 59 * 60 * Returns: 0 Success 61 * !0 An errno value indicating the 62 * cause of the failure to spawn 63 * 64 * Notes: Much of this function is derived from code from execvP() from 65 * exec.c in libc; this common code should be factored out at 66 * some point to prevent code duplication or desynchronization vs. 67 * bug fixes applied to one set of code but not the other. 68 */ 69int 70posix_spawnp(pid_t * __restrict pid, const char * __restrict file, 71 const posix_spawn_file_actions_t *file_actions, 72 const posix_spawnattr_t * __restrict attrp, 73 char *const argv[ __restrict], char *const envp[ __restrict]) 74{ 75 const char *env_path; 76 char *bp; 77 char *cur; 78 char *p; 79 char **memp; 80 int lp; 81 int ln; 82 int cnt; 83 int err = 0; 84 int eacces = 0; 85 struct stat sb; 86 char path_buf[PATH_MAX]; 87 88 if ((env_path = getenv("PATH")) == NULL) 89 env_path = _PATH_DEFPATH; 90 91 /* If it's an absolute or relative path name, it's easy. */ 92 if (index(file, '/')) { 93 bp = (char *)file; 94 cur = NULL; 95 goto retry; 96 } 97 bp = path_buf; 98 99 /* If it's an empty path name, fail in the usual POSIX way. */ 100 if (*file == '\0') 101 return (ENOENT); 102 103 if ((cur = alloca(strlen(env_path) + 1)) == NULL) 104 return ENOMEM; 105 strcpy(cur, env_path); 106 while ((p = strsep(&cur, ":")) != NULL) { 107 /* 108 * It's a SHELL path -- double, leading and trailing colons 109 * mean the current directory. 110 */ 111 if (*p == '\0') { 112 p = "."; 113 lp = 1; 114 } else { 115 lp = strlen(p); 116 } 117 ln = strlen(file); 118 119 /* 120 * If the path is too long complain. This is a possible 121 * security issue; given a way to make the path too long 122 * the user may spawn the wrong program. 123 */ 124 if (lp + ln + 2 > sizeof(path_buf)) { 125 err = ENAMETOOLONG; 126 goto done; 127 } 128 bcopy(p, path_buf, lp); 129 path_buf[lp] = '/'; 130 bcopy(file, path_buf + lp + 1, ln); 131 path_buf[lp + ln + 1] = '\0'; 132 133retry: err = posix_spawn(pid, bp, file_actions, attrp, argv, envp); 134 switch (err) { 135 case E2BIG: 136 case ENOMEM: 137 case ETXTBSY: 138 goto done; 139 case ELOOP: 140 case ENAMETOOLONG: 141 case ENOENT: 142 case ENOTDIR: 143 break; 144 case ENOEXEC: 145 for (cnt = 0; argv[cnt]; ++cnt) 146 ; 147 memp = alloca((cnt + 2) * sizeof(char *)); 148 if (memp == NULL) { 149 /* errno = ENOMEM; XXX override ENOEXEC? */ 150 goto done; 151 } 152 memp[0] = "sh"; 153 memp[1] = bp; 154 bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); 155 err = posix_spawn(pid, _PATH_BSHELL, file_actions, attrp, memp, envp); 156 goto done; 157 default: 158 /* 159 * EACCES may be for an inaccessible directory or 160 * a non-executable file. Call stat() to decide 161 * which. This also handles ambiguities for EFAULT 162 * and EIO, and undocumented errors like ESTALE. 163 * We hope that the race for a stat() is unimportant. 164 */ 165 if (stat(bp, &sb) != 0) 166 break; 167 if (err == EACCES) { 168 eacces = 1; 169 continue; 170 } 171 goto done; 172 } 173 } 174 if (eacces) 175 err = EACCES; 176 else 177 err = ENOENT; 178done: 179 return (err); 180} 181