~ubuntu-branches/ubuntu/utopic/gettext/utopic

« back to all changes in this revision

Viewing changes to gettext-tools/gnulib-lib/spawni.c

  • Committer: Colin Watson
  • Date: 2010-08-01 21:36:08 UTC
  • mfrom: (2.1.10 sid)
  • Revision ID: cjwatson@canonical.com-20100801213608-yy7vkm8lpatep3ci
merge from Debian 0.18.1.1-1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Guts of POSIX spawn interface.  Generic POSIX.1 version.
 
2
   Copyright (C) 2000-2006, 2008-2010 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 Woe32 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