1 /* Copyright (C) 2000, 2011 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <http://www.gnu.org/licenses/>.  */
17 
18 #include <errno.h>
19 #include <alloca.h>
20 #include <unistd.h>
21 #include <signal.h>
22 #include <stdbool.h>
23 #include <fcntl.h>
24 
25 #include <sys/resource.h>
26 #include <not-cancel.h>
27 
28 #include <spawn.h>
29 #include "spawn_int.h"
30 
31 /* The Unix standard contains a long explanation of the way to signal
32    an error after the fork() was successful.  Since no new wait status
33    was wanted there is no way to signal an error using one of the
34    available methods.  The committee chose to signal an error by a
35    normal program exit with the exit code 127.  */
36 #define SPAWN_ERROR	127
37 
38 /* Execute file actions.
39  * Returns true on error.
40  */
execute_file_actions(const posix_spawn_file_actions_t * fa)41 inline static bool execute_file_actions(const posix_spawn_file_actions_t *fa)
42 {
43 	struct rlimit64 fdlimit;
44 	bool have_fdlimit = false;
45 	int cnt;
46 
47 	for (cnt = 0; cnt < fa->__used; ++cnt) {
48 		struct __spawn_action *action = &fa->__actions[cnt];
49 
50 		switch (action->tag) {
51 		case spawn_do_close:
52 			if (close_not_cancel(action->action.close_action.fd) != 0) {
53 				if (!have_fdlimit) {
54 					getrlimit64(RLIMIT_NOFILE, &fdlimit);
55 					have_fdlimit = true;
56 				}
57 
58 				/* Only signal errors for file descriptors out of range.  */
59 				if (0 > action->action.close_action.fd
60 				    || action->action.close_action.fd >= fdlimit.rlim_cur)
61 					/* Signal the error.  */
62 					return true;
63 			}
64 			break;
65 
66 		case spawn_do_open:;
67 			int new_fd = open_not_cancel(action->action.open_action.path,
68 						     action->action.open_action.oflag
69 						     | O_LARGEFILE,
70 						     action->action.open_action.mode);
71 
72 			if (new_fd == -1)
73 				return true;
74 
75 			/* Make sure the desired file descriptor is used.  */
76 			if (new_fd != action->action.open_action.fd) {
77 				if (dup2(new_fd, action->action.open_action.fd)
78 				    != action->action.open_action.fd)
79 					return true;
80 
81 				if (close_not_cancel(new_fd) != 0)
82 					return true;
83 			}
84 			break;
85 
86 		case spawn_do_dup2:
87 			if (dup2(action->action.dup2_action.fd,
88 				   action->action.dup2_action.newfd)
89 			    != action->action.dup2_action.newfd)
90 				return true;
91 			break;
92 		}
93 	}
94 
95 	return false;
96 }
97 
98 #define DANGEROUS (POSIX_SPAWN_SETSIGMASK		\
99 		   | POSIX_SPAWN_SETSIGDEF		\
100 		   | POSIX_SPAWN_SETSCHEDPARAM		\
101 		   | POSIX_SPAWN_SETSCHEDULER		\
102 		   | POSIX_SPAWN_SETPGROUP		\
103 		   | POSIX_SPAWN_RESETIDS)
is_vfork_safe(short int flags)104 inline static bool is_vfork_safe(short int flags)
105 {
106 	return ((flags & POSIX_SPAWN_USEVFORK) || !(flags & DANGEROUS));
107 }
108 
109 
110 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
111    Before running the process perform the actions described in FILE-ACTIONS. */
112 static int
__spawni(pid_t * pid,const char * file,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[],const char * path)113 __spawni(pid_t *pid, const char *file,
114 	 const posix_spawn_file_actions_t *fa,
115 	 const posix_spawnattr_t *attrp, char *const argv[],
116 	 char *const envp[], const char *path)
117 {
118 	short int flags = attrp ? attrp->__flags : 0;
119 
120 	pid_t new_pid;
121 	if (is_vfork_safe(flags) && !fa)
122 		new_pid = vfork();
123 	else {
124 #ifdef __ARCH_USE_MMU__
125 		new_pid = fork();
126 #else
127 		return ENOSYS;
128 #endif
129 	}
130 
131 	if (new_pid) {
132 		if (new_pid < 0)
133 			return errno;
134 
135 		if (pid)
136 			*pid = new_pid;
137 
138 		return 0;
139 	}
140 
141 	if (flags & POSIX_SPAWN_SETSIGMASK) {
142 		if (sigprocmask(SIG_SETMASK, &attrp->__ss, NULL) != 0)
143 			goto error;
144 	}
145 
146 	if (flags & POSIX_SPAWN_SETSIGDEF) {
147 		/* We have to iterate over all signals.  This could possibly be
148 		   done better but it requires system specific solutions since
149 		   the sigset_t data type can be very different on different
150 		   architectures.  */
151 		struct sigaction sa;
152 		int sig;
153 
154 		memset(&sa, 0, sizeof(sa));
155 		sa.sa_handler = SIG_DFL;
156 
157 		for (sig = 1; sig <= _NSIG; ++sig) {
158 			if (sigismember(&attrp->__sd, sig)) {
159 				if (sigaction(sig, &sa, NULL) != 0)
160 					goto error;
161 			}
162 		}
163 	}
164 
165 	if (flags & POSIX_SPAWN_SETSCHEDULER) {
166 		if (sched_setscheduler(0, attrp->__policy, &attrp->__sp) == -1)
167 			goto error;
168 	} else if (flags & POSIX_SPAWN_SETSCHEDPARAM) {
169 		if (sched_setparam(0, &attrp->__sp) == -1)
170 			goto error;
171 	}
172 
173 	if (flags & POSIX_SPAWN_SETPGROUP) {
174 		if (setpgid(0, attrp->__pgrp) != 0)
175 			goto error;
176 	}
177 
178 	if (flags & POSIX_SPAWN_RESETIDS) {
179 		if (seteuid(getuid()) || setegid(getgid()))
180 			goto error;
181 	}
182 
183 	if (fa && execute_file_actions(fa))
184 		goto error;
185 
186 	if (!path || strchr(file, '/')) {
187 		execve(file, argv, envp);
188 		goto error;
189 	}
190 
191 
192 	char *name;
193 	{
194 		size_t filelen = strlen(file) + 1;
195 		size_t pathlen = strlen(path) + 1;
196 		name = alloca(pathlen + filelen);
197 
198 		/* Copy the file name at the top. */
199 		name = (char *) memcpy(name + pathlen, file, filelen);
200 
201 		/* And add the slash.  */
202 		*--name = '/';
203 	}
204 
205 	char *p;
206 	do {
207 		char *startp;
208 		p = strchrnul(path, ':');
209 
210 		/* Two adjacent colons, or a colon at the beginning or the end
211 		   of `PATH' means to search the current directory.  */
212 		if (p == path)
213 			startp = name + 1;
214 		else
215 			startp = (char *) memcpy(name - (p - path), path, p - path);
216 
217 		execve(startp, argv, envp);
218 
219 		switch (errno) {
220 		case EACCES:
221 		case ENOENT:
222 		case ESTALE:
223 		case ENOTDIR:
224 			/* Those errors indicate the file is missing or not
225 			   executable by us, in which case we want to just try
226 			   the next path directory. */
227 			break;
228 		default:
229 			/* Some other error means we found an executable file,
230 			   but something went wrong executing it; return the
231 			   error to our caller. */
232 			goto error;
233 		}
234 
235 		path = p;
236 	} while (*p++ != '\0');
237 
238 error:
239 	_exit(SPAWN_ERROR);
240 }
241 
242 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
243    Before running the process perform the actions described in FILE-ACTIONS. */
posix_spawn(pid_t * pid,const char * path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[])244 int posix_spawn (pid_t *pid, const char *path,
245 	       const posix_spawn_file_actions_t *fa,
246 	       const posix_spawnattr_t *attrp, char *const argv[],
247 	       char *const envp[])
248 {
249 	return __spawni(pid, path, fa, attrp, argv, envp, NULL);
250 }
251 
252 /* Spawn a new process executing FILE with the attributes describes in *ATTRP.
253    Before running the process perform the actions described in FILE-ACTIONS. */
254 int
posix_spawnp(pid_t * pid,const char * file,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[])255 posix_spawnp(pid_t *pid, const char *file,
256 	     const posix_spawn_file_actions_t *fa,
257 	     const posix_spawnattr_t *attrp, char *const argv[],
258 	     char *const envp[])
259 {
260 	const char *path = getenv("PATH");
261 
262 	if (!path)
263 		path = ":/bin:/usr/bin";
264 
265 	return __spawni(pid, file, fa, attrp, argv, envp, path);
266 }
267