builtin_unreachable is not available for older gcc
[uclibc-ng.git] / librt / spawn.c
blob07d40193ca658ee0d619e361a885cbc1c053eaf5
1 /* Copyright (C) 2000, 2011 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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/>. */
18 #include <errno.h>
19 #include <alloca.h>
20 #include <unistd.h>
21 #include <signal.h>
22 #include <stdbool.h>
23 #include <fcntl.h>
25 #include <sys/resource.h>
26 #include <not-cancel.h>
28 #include <spawn.h>
29 #include "spawn_int.h"
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
38 /* Execute file actions.
39 * Returns true on error.
41 inline static bool execute_file_actions(const posix_spawn_file_actions_t *fa)
43 struct rlimit64 fdlimit;
44 bool have_fdlimit = false;
45 int cnt;
47 for (cnt = 0; cnt < fa->__used; ++cnt) {
48 struct __spawn_action *action = &fa->__actions[cnt];
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;
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;
64 break;
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);
72 if (new_fd == -1)
73 return true;
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;
81 if (close_not_cancel(new_fd) != 0)
82 return true;
84 break;
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;
95 return false;
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)
104 inline static bool is_vfork_safe(short int flags)
106 return ((flags & POSIX_SPAWN_USEVFORK) || !(flags & DANGEROUS));
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
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)
118 short int flags = attrp ? attrp->__flags : 0;
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
131 if (new_pid) {
132 if (new_pid < 0)
133 return errno;
135 if (pid)
136 *pid = new_pid;
138 return 0;
141 if (flags & POSIX_SPAWN_SETSIGMASK) {
142 if (sigprocmask(SIG_SETMASK, &attrp->__ss, NULL) != 0)
143 goto error;
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;
154 memset(&sa, 0, sizeof(sa));
155 sa.sa_handler = SIG_DFL;
157 for (sig = 1; sig <= _NSIG; ++sig) {
158 if (sigismember(&attrp->__sd, sig)) {
159 if (sigaction(sig, &sa, NULL) != 0)
160 goto error;
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;
173 if (flags & POSIX_SPAWN_SETPGROUP) {
174 if (setpgid(0, attrp->__pgrp) != 0)
175 goto error;
178 if (flags & POSIX_SPAWN_RESETIDS) {
179 if (seteuid(getuid()) || setegid(getgid()))
180 goto error;
183 if (fa && execute_file_actions(fa))
184 goto error;
186 if (!path || strchr(file, '/')) {
187 execve(file, argv, envp);
188 goto error;
192 char *name;
194 size_t filelen = strlen(file) + 1;
195 size_t pathlen = strlen(path) + 1;
196 name = alloca(pathlen + filelen);
198 /* Copy the file name at the top. */
199 name = (char *) memcpy(name + pathlen, file, filelen);
201 /* And add the slash. */
202 *--name = '/';
205 char *p;
206 do {
207 char *startp;
208 p = strchrnul(path, ':');
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);
217 execve(startp, argv, envp);
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;
235 path = p;
236 } while (*p++ != '\0');
238 error:
239 _exit(SPAWN_ERROR);
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. */
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[])
249 return __spawni(pid, path, fa, attrp, argv, envp, NULL);
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. */
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[])
260 const char *path = getenv("PATH");
262 if (!path)
263 path = ":/bin:/usr/bin";
265 return __spawni(pid, file, fa, attrp, argv, envp, path);