Don't execute scripts without '#!' marker through /bin/sh.
[gnulib.git] / lib / spawni.c
blob182d13ff24ca2212f43b68570b8f05e39e472301
1 /* Guts of POSIX spawn interface. Generic POSIX.1 version.
2 Copyright (C) 2000-2006, 2008-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18 #include <config.h>
20 /* Specification. */
21 #include <spawn.h>
22 #include "spawn_int.h"
24 #include <alloca.h>
25 #include <errno.h>
27 #include <fcntl.h>
28 #ifndef O_LARGEFILE
29 # define O_LARGEFILE 0
30 #endif
32 #if _LIBC || HAVE_PATHS_H
33 # include <paths.h>
34 #else
35 # define _PATH_BSHELL BOURNE_SHELL
36 #endif
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
43 #if _LIBC
44 # include <not-cancel.h>
45 #else
46 # define close_not_cancel close
47 # define open_not_cancel open
48 #endif
50 #if _LIBC
51 # include <local-setxid.h>
52 #else
53 # if !HAVE_SETEUID
54 # define seteuid(id) setresuid (-1, id, -1)
55 # endif
56 # if !HAVE_SETEGID
57 # define setegid(id) setresgid (-1, id, -1)
58 # endif
59 # define local_seteuid(id) seteuid (id)
60 # define local_setegid(id) setegid (id)
61 #endif
63 #if _LIBC
64 # define alloca __alloca
65 # define execve __execve
66 # define dup2 __dup2
67 # define fork __fork
68 # define getgid __getgid
69 # define getuid __getuid
70 # define sched_setparam __sched_setparam
71 # define sched_setscheduler __sched_setscheduler
72 # define setpgid __setpgid
73 # define sigaction __sigaction
74 # define sigismember __sigismember
75 # define sigprocmask __sigprocmask
76 # define strchrnul __strchrnul
77 # define vfork __vfork
78 #endif
81 /* The Unix standard contains a long explanation of the way to signal
82 an error after the fork() was successful. Since no new wait status
83 was wanted there is no way to signal an error using one of the
84 available methods. The committee chose to signal an error by a
85 normal program exit with the exit code 127. */
86 #define SPAWN_ERROR 127
89 #if defined _WIN32 && ! defined __CYGWIN__
91 /* Native Windows API. */
92 int
93 __spawni (pid_t *pid, const char *file,
94 const posix_spawn_file_actions_t *file_actions,
95 const posix_spawnattr_t *attrp, const char *const argv[],
96 const char *const envp[], int use_path)
98 /* Not yet implemented. */
99 return ENOSYS;
102 #else
105 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
106 Before running the process perform the actions described in FILE-ACTIONS. */
108 __spawni (pid_t *pid, const char *file,
109 const posix_spawn_file_actions_t *file_actions,
110 const posix_spawnattr_t *attrp, const char *const argv[],
111 const char *const envp[], int use_path)
113 pid_t new_pid;
114 char *path, *p, *name;
115 size_t len;
116 size_t pathlen;
118 /* Do this once. */
119 short int flags = attrp == NULL ? 0 : attrp->_flags;
121 /* Avoid gcc warning
122 "variable 'flags' might be clobbered by 'longjmp' or 'vfork'" */
123 (void) &flags;
125 /* Generate the new process. */
126 #if HAVE_VFORK
127 if ((flags & POSIX_SPAWN_USEVFORK) != 0
128 /* If no major work is done, allow using vfork. Note that we
129 might perform the path searching. But this would be done by
130 a call to execvp(), too, and such a call must be OK according
131 to POSIX. */
132 || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF
133 | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER
134 | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0
135 && file_actions == NULL))
136 new_pid = vfork ();
137 else
138 #endif
139 new_pid = fork ();
141 if (new_pid != 0)
143 if (new_pid < 0)
144 return errno;
146 /* The call was successful. Store the PID if necessary. */
147 if (pid != NULL)
148 *pid = new_pid;
150 return 0;
153 /* Set signal mask. */
154 if ((flags & POSIX_SPAWN_SETSIGMASK) != 0
155 && sigprocmask (SIG_SETMASK, &attrp->_ss, NULL) != 0)
156 _exit (SPAWN_ERROR);
158 /* Set signal default action. */
159 if ((flags & POSIX_SPAWN_SETSIGDEF) != 0)
161 /* We have to iterate over all signals. This could possibly be
162 done better but it requires system specific solutions since
163 the sigset_t data type can be very different on different
164 architectures. */
165 int sig;
166 struct sigaction sa;
168 memset (&sa, '\0', sizeof (sa));
169 sa.sa_handler = SIG_DFL;
171 for (sig = 1; sig <= NSIG; ++sig)
172 if (sigismember (&attrp->_sd, sig) != 0
173 && sigaction (sig, &sa, NULL) != 0)
174 _exit (SPAWN_ERROR);
178 #if (_LIBC ? defined _POSIX_PRIORITY_SCHEDULING : HAVE_SCHED_SETPARAM && HAVE_SCHED_SETSCHEDULER)
179 /* Set the scheduling algorithm and parameters. */
180 if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
181 == POSIX_SPAWN_SETSCHEDPARAM)
183 if (sched_setparam (0, &attrp->_sp) == -1)
184 _exit (SPAWN_ERROR);
186 else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
188 if (sched_setscheduler (0, attrp->_policy,
189 (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
190 ? &attrp->_sp : NULL) == -1)
191 _exit (SPAWN_ERROR);
193 #endif
195 /* Set the process group ID. */
196 if ((flags & POSIX_SPAWN_SETPGROUP) != 0
197 && setpgid (0, attrp->_pgrp) != 0)
198 _exit (SPAWN_ERROR);
200 /* Set the effective user and group IDs. */
201 if ((flags & POSIX_SPAWN_RESETIDS) != 0
202 && (local_seteuid (getuid ()) != 0
203 || local_setegid (getgid ()) != 0))
204 _exit (SPAWN_ERROR);
206 /* Execute the file actions. */
207 if (file_actions != NULL)
209 int cnt;
211 for (cnt = 0; cnt < file_actions->_used; ++cnt)
213 struct __spawn_action *action = &file_actions->_actions[cnt];
215 switch (action->tag)
217 case spawn_do_close:
218 if (close_not_cancel (action->action.close_action.fd) != 0)
219 /* Signal the error. */
220 _exit (SPAWN_ERROR);
221 break;
223 case spawn_do_open:
225 int new_fd = open_not_cancel (action->action.open_action.path,
226 action->action.open_action.oflag
227 | O_LARGEFILE,
228 action->action.open_action.mode);
230 if (new_fd == -1)
231 /* The 'open' call failed. */
232 _exit (SPAWN_ERROR);
234 /* Make sure the desired file descriptor is used. */
235 if (new_fd != action->action.open_action.fd)
237 if (dup2 (new_fd, action->action.open_action.fd)
238 != action->action.open_action.fd)
239 /* The 'dup2' call failed. */
240 _exit (SPAWN_ERROR);
242 if (close_not_cancel (new_fd) != 0)
243 /* The 'close' call failed. */
244 _exit (SPAWN_ERROR);
247 break;
249 case spawn_do_dup2:
250 if (dup2 (action->action.dup2_action.fd,
251 action->action.dup2_action.newfd)
252 != action->action.dup2_action.newfd)
253 /* The 'dup2' call failed. */
254 _exit (SPAWN_ERROR);
255 break;
257 case spawn_do_chdir:
258 if (chdir (action->action.chdir_action.path) < 0)
259 /* The 'chdir' call failed. */
260 _exit (SPAWN_ERROR);
261 break;
263 case spawn_do_fchdir:
264 if (fchdir (action->action.fchdir_action.fd) < 0)
265 /* The 'fchdir' call failed. */
266 _exit (SPAWN_ERROR);
267 break;
272 if (! use_path || strchr (file, '/') != NULL)
274 /* The FILE parameter is actually a path. */
275 execve (file, (char * const *) argv, (char * const *) envp);
277 /* Oh, oh. 'execve' returns. This is bad. */
278 _exit (SPAWN_ERROR);
281 /* We have to search for FILE on the path. */
282 path = getenv ("PATH");
283 if (path == NULL)
285 #if HAVE_CONFSTR
286 /* There is no 'PATH' in the environment.
287 The default search path is the current directory
288 followed by the path 'confstr' returns for '_CS_PATH'. */
289 len = confstr (_CS_PATH, (char *) NULL, 0);
290 path = (char *) alloca (1 + len);
291 path[0] = ':';
292 (void) confstr (_CS_PATH, path + 1, len);
293 #else
294 /* Pretend that the PATH contains only the current directory. */
295 path = "";
296 #endif
299 len = strlen (file) + 1;
300 pathlen = strlen (path);
301 name = alloca (pathlen + len + 1);
302 /* Copy the file name at the top. */
303 name = (char *) memcpy (name + pathlen + 1, file, len);
304 /* And add the slash. */
305 *--name = '/';
307 p = path;
310 char *startp;
312 path = p;
313 p = strchrnul (path, ':');
315 if (p == path)
316 /* Two adjacent colons, or a colon at the beginning or the end
317 of 'PATH' means to search the current directory. */
318 startp = name + 1;
319 else
320 startp = (char *) memcpy (name - (p - path), path, p - path);
322 /* Try to execute this name. If it works, execv will not return. */
323 execve (startp, (char * const *) argv, (char * const *) envp);
325 switch (errno)
327 case EACCES:
328 case ENOENT:
329 case ESTALE:
330 case ENOTDIR:
331 /* Those errors indicate the file is missing or not executable
332 by us, in which case we want to just try the next path
333 directory. */
334 break;
336 default:
337 /* Some other error means we found an executable file, but
338 something went wrong executing it; return the error to our
339 caller. */
340 _exit (SPAWN_ERROR);
343 while (*p++ != '\0');
345 /* Return with an error. */
346 _exit (SPAWN_ERROR);
349 #endif