(gaih_inet): Recognize all the IPv4 numeric address formats inet_addr knows.
[kopensolaris-gnu/glibc.git] / sysdeps / posix / spawni.c
1 /* Guts of POSIX spawn interface.  Generic POSIX.1 version.
2    Copyright (C) 2000,01,02, 2003 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library 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 GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <paths.h>
23 #include <spawn.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include "spawn_int.h"
28 #include <not-cancel.h>
29
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
39 /* The file is accessible but it is not an executable file.  Invoke
40    the shell to interpret it as a script.  */
41 static void
42 internal_function
43 script_execute (const char *file, char *const argv[], char *const envp[])
44 {
45   /* Count the arguments.  */
46   int argc = 0;
47   while (argv[argc++])
48     ;
49
50   /* Construct an argument list for the shell.  */
51   {
52     char *new_argv[argc + 1];
53     new_argv[0] = (char *) _PATH_BSHELL;
54     new_argv[1] = (char *) file;
55     while (argc > 1)
56       {
57         new_argv[argc] = argv[argc - 1];
58         --argc;
59       }
60
61     /* Execute the shell.  */
62     __execve (new_argv[0], new_argv, envp);
63   }
64 }
65
66
67 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
68    Before running the process perform the actions described in FILE-ACTIONS. */
69 int
70 __spawni (pid_t *pid, const char *file,
71           const posix_spawn_file_actions_t *file_actions,
72           const posix_spawnattr_t *attrp, char *const argv[],
73           char *const envp[], int use_path)
74 {
75   pid_t new_pid;
76   char *path, *p, *name;
77   size_t len;
78   size_t pathlen;
79   short int flags;
80
81   /* Generate the new process.  */
82   new_pid = __fork ();
83   if (new_pid != 0)
84     {
85       if (new_pid < 0)
86         return errno;
87
88       /* The call was successful.  Store the PID if necessary.  */
89       if (pid != NULL)
90         *pid = new_pid;
91
92       return 0;
93     }
94
95   /* Do this once.  */
96   flags = attrp == NULL ? 0 : attrp->__flags;
97
98   /* Set signal mask.  */
99   if ((flags & POSIX_SPAWN_SETSIGMASK) != 0
100       && __sigprocmask (SIG_SETMASK, &attrp->__ss, NULL) != 0)
101     _exit (SPAWN_ERROR);
102
103   /* Set signal default action.  */
104   if ((flags & POSIX_SPAWN_SETSIGDEF) != 0)
105     {
106       /* We have to iterate over all signals.  This could possibly be
107          done better but it requires system specific solutions since
108          the sigset_t data type can be very different on different
109          architectures.  */
110       int sig;
111       struct sigaction sa;
112
113       memset (&sa, '\0', sizeof (sa));
114       sa.sa_handler = SIG_DFL;
115
116       for (sig = 1; sig <= _NSIG; ++sig)
117         if (__sigismember (&attrp->__sd, sig) != 0
118             && __sigaction (sig, &sa, NULL) != 0)
119           _exit (SPAWN_ERROR);
120
121     }
122
123 #ifdef _POSIX_PRIORITY_SCHEDULING
124   /* Set the scheduling algorithm and parameters.  */
125   if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
126       == POSIX_SPAWN_SETSCHEDPARAM)
127     {
128       if (__sched_setparam (0, &attrp->__sp) == -1)
129         _exit (SPAWN_ERROR);
130     }
131   else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
132     {
133       if (__sched_setscheduler (0, attrp->__policy,
134                                 (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
135                                 ? &attrp->__sp : NULL) == -1)
136         _exit (SPAWN_ERROR);
137     }
138 #endif
139
140   /* Set the process group ID.  */
141   if ((flags & POSIX_SPAWN_SETPGROUP) != 0
142       && __setpgid (0, attrp->__pgrp) != 0)
143     _exit (SPAWN_ERROR);
144
145   /* Set the effective user and group IDs.  */
146   if ((flags & POSIX_SPAWN_RESETIDS) != 0
147       && (seteuid (__getuid ()) != 0 || setegid (__getgid ()) != 0))
148     _exit (SPAWN_ERROR);
149
150   /* Execute the file actions.  */
151   if (file_actions != NULL)
152     {
153       int cnt;
154
155       for (cnt = 0; cnt < file_actions->__used; ++cnt)
156         {
157           struct __spawn_action *action = &file_actions->__actions[cnt];
158
159           switch (action->tag)
160             {
161             case spawn_do_close:
162               if (close_not_cancel (action->action.close_action.fd) != 0)
163                 /* Signal the error.  */
164                 _exit (SPAWN_ERROR);
165               break;
166
167             case spawn_do_open:
168               {
169                 int new_fd = __open64 (action->action.open_action.path,
170                                        action->action.open_action.oflag,
171                                        action->action.open_action.mode);
172
173                 if (new_fd == -1)
174                   /* The `open' call failed.  */
175                   _exit (SPAWN_ERROR);
176
177                 /* Make sure the desired file descriptor is used.  */
178                 if (new_fd != action->action.open_action.fd)
179                   {
180                     if (__dup2 (new_fd, action->action.open_action.fd)
181                         != action->action.open_action.fd)
182                       /* The `dup2' call failed.  */
183                       _exit (SPAWN_ERROR);
184
185                     if (__close (new_fd) != 0)
186                       /* The `close' call failed.  */
187                       _exit (SPAWN_ERROR);
188                   }
189               }
190               break;
191
192             case spawn_do_dup2:
193               if (__dup2 (action->action.dup2_action.fd,
194                           action->action.dup2_action.newfd)
195                   != action->action.dup2_action.newfd)
196                 /* The `dup2' call failed.  */
197                 _exit (SPAWN_ERROR);
198               break;
199             }
200         }
201     }
202
203   if (! use_path || strchr (file, '/') != NULL)
204     {
205       /* The FILE parameter is actually a path.  */
206       __execve (file, argv, envp);
207
208       if (errno == ENOEXEC)
209         script_execute (file, argv, envp);
210
211       /* Oh, oh.  `execve' returns.  This is bad.  */
212       _exit (SPAWN_ERROR);
213     }
214
215   /* We have to search for FILE on the path.  */
216   path = getenv ("PATH");
217   if (path == NULL)
218     {
219       /* There is no `PATH' in the environment.
220          The default search path is the current directory
221          followed by the path `confstr' returns for `_CS_PATH'.  */
222       len = confstr (_CS_PATH, (char *) NULL, 0);
223       path = (char *) __alloca (1 + len);
224       path[0] = ':';
225       (void) confstr (_CS_PATH, path + 1, len);
226     }
227
228   len = strlen (file) + 1;
229   pathlen = strlen (path);
230   name = __alloca (pathlen + len + 1);
231   /* Copy the file name at the top.  */
232   name = (char *) memcpy (name + pathlen + 1, file, len);
233   /* And add the slash.  */
234   *--name = '/';
235
236   p = path;
237   do
238     {
239       char *startp;
240
241       path = p;
242       p = __strchrnul (path, ':');
243
244       if (p == path)
245         /* Two adjacent colons, or a colon at the beginning or the end
246            of `PATH' means to search the current directory.  */
247         startp = name + 1;
248       else
249         startp = (char *) memcpy (name - (p - path), path, p - path);
250
251       /* Try to execute this name.  If it works, execv will not return.  */
252       __execve (startp, argv, envp);
253
254       if (errno == ENOEXEC)
255         script_execute (startp, argv, envp);
256
257       switch (errno)
258         {
259         case EACCES:
260         case ENOENT:
261         case ESTALE:
262         case ENOTDIR:
263           /* Those errors indicate the file is missing or not executable
264              by us, in which case we want to just try the next path
265              directory.  */
266           break;
267
268         default:
269           /* Some other error means we found an executable file, but
270              something went wrong executing it; return the error to our
271              caller.  */
272           _exit (SPAWN_ERROR);
273             }
274     }
275   while (*p++ != '\0');
276
277   /* Return with an error.  */
278   _exit (SPAWN_ERROR);
279 }