/ sys / posix_spawn.c
posix_spawn.c
  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   */
 69  int
 70  posix_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  
133  retry:		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;
178  done:
179  	return (err);
180  }