Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / gnulib-lib / spawni.c
1 /* Guts of POSIX spawn interface.  Generic POSIX.1 version.
2    Copyright (C) 2000-2006, 2008-2015 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
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.
9
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.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include <spawn.h>
22 #include "spawn_int.h"
23
24 #include <alloca.h>
25 #include <errno.h>
26
27 #include <fcntl.h>
28 #ifndef O_LARGEFILE
29 # define O_LARGEFILE 0
30 #endif
31
32 #if _LIBC || HAVE_PATHS_H
33 # include <paths.h>
34 #else
35 # define _PATH_BSHELL "/bin/sh"
36 #endif
37
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #if _LIBC
44 # include <not-cancel.h>
45 #else
46 # define close_not_cancel close
47 # define open_not_cancel open
48 #endif
49
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
62
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 #else
79 # undef internal_function
80 # define internal_function /* empty */
81 #endif
82
83
84 /* The Unix standard contains a long explanation of the way to signal
85    an error after the fork() was successful.  Since no new wait status
86    was wanted there is no way to signal an error using one of the
87    available methods.  The committee chose to signal an error by a
88    normal program exit with the exit code 127.  */
89 #define SPAWN_ERROR     127
90
91
92 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
93
94 /* Native Windows API.  */
95 int
96 __spawni (pid_t *pid, const char *file,
97           const posix_spawn_file_actions_t *file_actions,
98           const posix_spawnattr_t *attrp, char *const argv[],
99           char *const envp[], int use_path)
100 {
101   /* Not yet implemented.  */
102   return ENOSYS;
103 }
104
105 #else
106
107
108 /* The file is accessible but it is not an executable file.  Invoke
109    the shell to interpret it as a script.  */
110 static void
111 internal_function
112 script_execute (const char *file, char *const argv[], char *const envp[])
113 {
114   /* Count the arguments.  */
115   int argc = 0;
116   while (argv[argc++])
117     ;
118
119   /* Construct an argument list for the shell.  */
120   {
121     char **new_argv = (char **) alloca ((argc + 1) * sizeof (char *));
122     new_argv[0] = (char *) _PATH_BSHELL;
123     new_argv[1] = (char *) file;
124     while (argc > 1)
125       {
126         new_argv[argc] = argv[argc - 1];
127         --argc;
128       }
129
130     /* Execute the shell.  */
131     execve (new_argv[0], new_argv, envp);
132   }
133 }
134
135
136 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
137    Before running the process perform the actions described in FILE-ACTIONS. */
138 int
139 __spawni (pid_t *pid, const char *file,
140           const posix_spawn_file_actions_t *file_actions,
141           const posix_spawnattr_t *attrp, char *const argv[],
142           char *const envp[], int use_path)
143 {
144   pid_t new_pid;
145   char *path, *p, *name;
146   size_t len;
147   size_t pathlen;
148
149   /* Do this once.  */
150   short int flags = attrp == NULL ? 0 : attrp->_flags;
151
152   /* Avoid gcc warning
153        "variable 'flags' might be clobbered by 'longjmp' or 'vfork'"  */
154   (void) &flags;
155
156   /* Generate the new process.  */
157 #if HAVE_VFORK
158   if ((flags & POSIX_SPAWN_USEVFORK) != 0
159       /* If no major work is done, allow using vfork.  Note that we
160          might perform the path searching.  But this would be done by
161          a call to execvp(), too, and such a call must be OK according
162          to POSIX.  */
163       || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF
164                     | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER
165                     | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0
166           && file_actions == NULL))
167     new_pid = vfork ();
168   else
169 #endif
170     new_pid = fork ();
171
172   if (new_pid != 0)
173     {
174       if (new_pid < 0)
175         return errno;
176
177       /* The call was successful.  Store the PID if necessary.  */
178       if (pid != NULL)
179         *pid = new_pid;
180
181       return 0;
182     }
183
184   /* Set signal mask.  */
185   if ((flags & POSIX_SPAWN_SETSIGMASK) != 0
186       && sigprocmask (SIG_SETMASK, &attrp->_ss, NULL) != 0)
187     _exit (SPAWN_ERROR);
188
189   /* Set signal default action.  */
190   if ((flags & POSIX_SPAWN_SETSIGDEF) != 0)
191     {
192       /* We have to iterate over all signals.  This could possibly be
193          done better but it requires system specific solutions since
194          the sigset_t data type can be very different on different
195          architectures.  */
196       int sig;
197       struct sigaction sa;
198
199       memset (&sa, '\0', sizeof (sa));
200       sa.sa_handler = SIG_DFL;
201
202       for (sig = 1; sig <= NSIG; ++sig)
203         if (sigismember (&attrp->_sd, sig) != 0
204             && sigaction (sig, &sa, NULL) != 0)
205           _exit (SPAWN_ERROR);
206
207     }
208
209 #if (_LIBC ? defined _POSIX_PRIORITY_SCHEDULING : HAVE_SCHED_SETPARAM && HAVE_SCHED_SETSCHEDULER)
210   /* Set the scheduling algorithm and parameters.  */
211   if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
212       == POSIX_SPAWN_SETSCHEDPARAM)
213     {
214       if (sched_setparam (0, &attrp->_sp) == -1)
215         _exit (SPAWN_ERROR);
216     }
217   else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
218     {
219       if (sched_setscheduler (0, attrp->_policy,
220                               (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
221                               ? &attrp->_sp : NULL) == -1)
222         _exit (SPAWN_ERROR);
223     }
224 #endif
225
226   /* Set the process group ID.  */
227   if ((flags & POSIX_SPAWN_SETPGROUP) != 0
228       && setpgid (0, attrp->_pgrp) != 0)
229     _exit (SPAWN_ERROR);
230
231   /* Set the effective user and group IDs.  */
232   if ((flags & POSIX_SPAWN_RESETIDS) != 0
233       && (local_seteuid (getuid ()) != 0
234           || local_setegid (getgid ()) != 0))
235     _exit (SPAWN_ERROR);
236
237   /* Execute the file actions.  */
238   if (file_actions != NULL)
239     {
240       int cnt;
241
242       for (cnt = 0; cnt < file_actions->_used; ++cnt)
243         {
244           struct __spawn_action *action = &file_actions->_actions[cnt];
245
246           switch (action->tag)
247             {
248             case spawn_do_close:
249               if (close_not_cancel (action->action.close_action.fd) != 0)
250                 /* Signal the error.  */
251                 _exit (SPAWN_ERROR);
252               break;
253
254             case spawn_do_open:
255               {
256                 int new_fd = open_not_cancel (action->action.open_action.path,
257                                               action->action.open_action.oflag
258                                               | O_LARGEFILE,
259                                               action->action.open_action.mode);
260
261                 if (new_fd == -1)
262                   /* The 'open' call failed.  */
263                   _exit (SPAWN_ERROR);
264
265                 /* Make sure the desired file descriptor is used.  */
266                 if (new_fd != action->action.open_action.fd)
267                   {
268                     if (dup2 (new_fd, action->action.open_action.fd)
269                         != action->action.open_action.fd)
270                       /* The 'dup2' call failed.  */
271                       _exit (SPAWN_ERROR);
272
273                     if (close_not_cancel (new_fd) != 0)
274                       /* The 'close' call failed.  */
275                       _exit (SPAWN_ERROR);
276                   }
277               }
278               break;
279
280             case spawn_do_dup2:
281               if (dup2 (action->action.dup2_action.fd,
282                         action->action.dup2_action.newfd)
283                   != action->action.dup2_action.newfd)
284                 /* The 'dup2' call failed.  */
285                 _exit (SPAWN_ERROR);
286               break;
287             }
288         }
289     }
290
291   if (! use_path || strchr (file, '/') != NULL)
292     {
293       /* The FILE parameter is actually a path.  */
294       execve (file, argv, envp);
295
296       if (errno == ENOEXEC)
297         script_execute (file, argv, envp);
298
299       /* Oh, oh.  'execve' returns.  This is bad.  */
300       _exit (SPAWN_ERROR);
301     }
302
303   /* We have to search for FILE on the path.  */
304   path = getenv ("PATH");
305   if (path == NULL)
306     {
307 #if HAVE_CONFSTR
308       /* There is no 'PATH' in the environment.
309          The default search path is the current directory
310          followed by the path 'confstr' returns for '_CS_PATH'.  */
311       len = confstr (_CS_PATH, (char *) NULL, 0);
312       path = (char *) alloca (1 + len);
313       path[0] = ':';
314       (void) confstr (_CS_PATH, path + 1, len);
315 #else
316       /* Pretend that the PATH contains only the current directory.  */
317       path = "";
318 #endif
319     }
320
321   len = strlen (file) + 1;
322   pathlen = strlen (path);
323   name = alloca (pathlen + len + 1);
324   /* Copy the file name at the top.  */
325   name = (char *) memcpy (name + pathlen + 1, file, len);
326   /* And add the slash.  */
327   *--name = '/';
328
329   p = path;
330   do
331     {
332       char *startp;
333
334       path = p;
335       p = strchrnul (path, ':');
336
337       if (p == path)
338         /* Two adjacent colons, or a colon at the beginning or the end
339            of 'PATH' means to search the current directory.  */
340         startp = name + 1;
341       else
342         startp = (char *) memcpy (name - (p - path), path, p - path);
343
344       /* Try to execute this name.  If it works, execv will not return.  */
345       execve (startp, argv, envp);
346
347       if (errno == ENOEXEC)
348         script_execute (startp, argv, envp);
349
350       switch (errno)
351         {
352         case EACCES:
353         case ENOENT:
354         case ESTALE:
355         case ENOTDIR:
356           /* Those errors indicate the file is missing or not executable
357              by us, in which case we want to just try the next path
358              directory.  */
359           break;
360
361         default:
362           /* Some other error means we found an executable file, but
363              something went wrong executing it; return the error to our
364              caller.  */
365           _exit (SPAWN_ERROR);
366         }
367     }
368   while (*p++ != '\0');
369
370   /* Return with an error.  */
371   _exit (SPAWN_ERROR);
372 }
373
374 #endif