Mercurial > hg > octave-kai > gnulib-hg
changeset 10505:bb42f3fdba16
New module 'posix_spawn-internal'.
author | Bruno Haible <bruno@clisp.org> |
---|---|
date | Sun, 28 Sep 2008 13:30:49 +0200 |
parents | 21e72b03bb51 |
children | 989d25db0548 |
files | ChangeLog lib/spawn_int.h lib/spawni.c m4/posix_spawn.m4 modules/posix_spawn-internal |
diffstat | 5 files changed, 500 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,14 @@ 2008-09-28 Bruno Haible <bruno@clisp.org> + New module 'posix_spawn-internal'. + * modules/posix_spawn-internal: New file. + * lib/spawn_int.h: New file, from GNU libc with modifications. + * lib/spawni.c: New file, from GNU libc with modifications. + * m4/posix_spawn.m4: New file. + New module 'spawn'. * modules/spawn: New file. - * lib/spawn.in.h: New file. + * lib/spawn.in.h: New file, from GNU libc with modifications. * m4/spawn_h.m4: New file. * doc/posix-headers/spawn.texi: Mention the new module.
new file mode 100644 --- /dev/null +++ b/lib/spawn_int.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2000, 2008 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <sys/types.h> + +/* Data structure to contain the action information. */ +struct __spawn_action +{ + enum + { + spawn_do_close, + spawn_do_dup2, + spawn_do_open + } tag; + + union + { + struct + { + int fd; + } close_action; + struct + { + int fd; + int newfd; + } dup2_action; + struct + { + int fd; + const char *path; + int oflag; + mode_t mode; + } open_action; + } action; +}; + +#if !_LIBC +# define __posix_spawn_file_actions_realloc gl_posix_spawn_file_actions_realloc +#endif +extern int __posix_spawn_file_actions_realloc (posix_spawn_file_actions_t * + file_actions); + +#if !_LIBC +# define __spawni gl_posix_spawn_internal +#endif +extern int __spawni (pid_t *pid, const char *path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], + char *const envp[], int use_path);
new file mode 100644 --- /dev/null +++ b/lib/spawni.c @@ -0,0 +1,352 @@ +/* Guts of POSIX spawn interface. Generic POSIX.1 version. + Copyright (C) 2000-2005, 2006, 2008 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +/* Specification. */ +#include <spawn.h> +#include "spawn_int.h" + +#include <alloca.h> +#include <errno.h> + +#include <fcntl.h> +#ifndef O_LARGEFILE +# define O_LARGEFILE 0 +#endif + +#if _LIBC || HAVE_PATHS_H +# include <paths.h> +#else +# define _PATH_BSHELL "/bin/sh" +#endif + +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if _LIBC +# include <not-cancel.h> +#else +# define close_not_cancel close +# define open_not_cancel open +#endif + +#if _LIBC +# include <local-setxid.h> +#else +# if !HAVE_SETEUID +# define seteuid(id) setresuid (-1, id, -1) +# endif +# if !HAVE_SETEGID +# define setegid(id) setresgid (-1, id, -1) +# endif +# define local_seteuid(id) seteuid (id) +# define local_setegid(id) setegid (id) +#endif + +#if _LIBC +# define alloca __alloca +# define execve __execve +# define dup2 __dup2 +# define fork __fork +# define getgid __getgid +# define getuid __getuid +# define sched_setparam __sched_setparam +# define sched_setscheduler __sched_setscheduler +# define setpgid __setpgid +# define sigaction __sigaction +# define sigismember __sigismember +# define sigprocmask __sigprocmask +# define strchrnul __strchrnul +# define vfork __vfork +#else +# undef internal_function +# define internal_function /* empty */ +#endif + + +/* The Unix standard contains a long explanation of the way to signal + an error after the fork() was successful. Since no new wait status + was wanted there is no way to signal an error using one of the + available methods. The committee chose to signal an error by a + normal program exit with the exit code 127. */ +#define SPAWN_ERROR 127 + + +/* The file is accessible but it is not an executable file. Invoke + the shell to interpret it as a script. */ +static void +internal_function +script_execute (const char *file, char *const argv[], char *const envp[]) +{ + /* Count the arguments. */ + int argc = 0; + while (argv[argc++]) + ; + + /* Construct an argument list for the shell. */ + { + char **new_argv = (char **) alloca ((argc + 1) * sizeof (char *)); + new_argv[0] = (char *) _PATH_BSHELL; + new_argv[1] = (char *) file; + while (argc > 1) + { + new_argv[argc] = argv[argc - 1]; + --argc; + } + + /* Execute the shell. */ + execve (new_argv[0], new_argv, envp); + } +} + + +/* Spawn a new process executing PATH with the attributes describes in *ATTRP. + Before running the process perform the actions described in FILE-ACTIONS. */ +int +__spawni (pid_t *pid, const char *file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *attrp, char *const argv[], + char *const envp[], int use_path) +{ + pid_t new_pid; + char *path, *p, *name; + size_t len; + size_t pathlen; + + /* Do this once. */ + short int flags = attrp == NULL ? 0 : attrp->_flags; + + /* Generate the new process. */ +#if HAVE_VFORK + if ((flags & POSIX_SPAWN_USEVFORK) != 0 + /* If no major work is done, allow using vfork. Note that we + might perform the path searching. But this would be done by + a call to execvp(), too, and such a call must be OK according + to POSIX. */ + || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF + | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER + | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0 + && file_actions == NULL)) + new_pid = vfork (); + else +#endif + new_pid = fork (); + + if (new_pid != 0) + { + if (new_pid < 0) + return errno; + + /* The call was successful. Store the PID if necessary. */ + if (pid != NULL) + *pid = new_pid; + + return 0; + } + + /* Set signal mask. */ + if ((flags & POSIX_SPAWN_SETSIGMASK) != 0 + && sigprocmask (SIG_SETMASK, &attrp->_ss, NULL) != 0) + _exit (SPAWN_ERROR); + + /* Set signal default action. */ + if ((flags & POSIX_SPAWN_SETSIGDEF) != 0) + { + /* We have to iterate over all signals. This could possibly be + done better but it requires system specific solutions since + the sigset_t data type can be very different on different + architectures. */ + int sig; + struct sigaction sa; + + memset (&sa, '\0', sizeof (sa)); + sa.sa_handler = SIG_DFL; + + for (sig = 1; sig <= NSIG; ++sig) + if (sigismember (&attrp->_sd, sig) != 0 + && sigaction (sig, &sa, NULL) != 0) + _exit (SPAWN_ERROR); + + } + +#if (_LIBC ? defined _POSIX_PRIORITY_SCHEDULING : HAVE_SCHED_SETPARAM && HAVE_SCHED_SETSCHEDULER) + /* Set the scheduling algorithm and parameters. */ + if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) + == POSIX_SPAWN_SETSCHEDPARAM) + { + if (sched_setparam (0, &attrp->_sp) == -1) + _exit (SPAWN_ERROR); + } + else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0) + { + if (sched_setscheduler (0, attrp->_policy, + (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0 + ? &attrp->_sp : NULL) == -1) + _exit (SPAWN_ERROR); + } +#endif + + /* Set the process group ID. */ + if ((flags & POSIX_SPAWN_SETPGROUP) != 0 + && setpgid (0, attrp->_pgrp) != 0) + _exit (SPAWN_ERROR); + + /* Set the effective user and group IDs. */ + if ((flags & POSIX_SPAWN_RESETIDS) != 0 + && (local_seteuid (getuid ()) != 0 + || local_setegid (getgid ()) != 0)) + _exit (SPAWN_ERROR); + + /* Execute the file actions. */ + if (file_actions != NULL) + { + int cnt; + + for (cnt = 0; cnt < file_actions->_used; ++cnt) + { + struct __spawn_action *action = &file_actions->_actions[cnt]; + + switch (action->tag) + { + case spawn_do_close: + if (close_not_cancel (action->action.close_action.fd) != 0) + /* Signal the error. */ + _exit (SPAWN_ERROR); + break; + + case spawn_do_open: + { + int new_fd = open_not_cancel (action->action.open_action.path, + action->action.open_action.oflag + | O_LARGEFILE, + action->action.open_action.mode); + + if (new_fd == -1) + /* The `open' call failed. */ + _exit (SPAWN_ERROR); + + /* Make sure the desired file descriptor is used. */ + if (new_fd != action->action.open_action.fd) + { + if (dup2 (new_fd, action->action.open_action.fd) + != action->action.open_action.fd) + /* The `dup2' call failed. */ + _exit (SPAWN_ERROR); + + if (close_not_cancel (new_fd) != 0) + /* The `close' call failed. */ + _exit (SPAWN_ERROR); + } + } + break; + + case spawn_do_dup2: + if (dup2 (action->action.dup2_action.fd, + action->action.dup2_action.newfd) + != action->action.dup2_action.newfd) + /* The `dup2' call failed. */ + _exit (SPAWN_ERROR); + break; + } + } + } + + if (! use_path || strchr (file, '/') != NULL) + { + /* The FILE parameter is actually a path. */ + execve (file, argv, envp); + + if (errno == ENOEXEC) + script_execute (file, argv, envp); + + /* Oh, oh. `execve' returns. This is bad. */ + _exit (SPAWN_ERROR); + } + + /* We have to search for FILE on the path. */ + path = getenv ("PATH"); + if (path == NULL) + { +#if HAVE_CONFSTR + /* There is no `PATH' in the environment. + The default search path is the current directory + followed by the path `confstr' returns for `_CS_PATH'. */ + len = confstr (_CS_PATH, (char *) NULL, 0); + path = (char *) alloca (1 + len); + path[0] = ':'; + (void) confstr (_CS_PATH, path + 1, len); +#else + /* Pretend that the PATH contains only the current directory. */ + path = ""; +#endif + } + + len = strlen (file) + 1; + pathlen = strlen (path); + name = alloca (pathlen + len + 1); + /* Copy the file name at the top. */ + name = (char *) memcpy (name + pathlen + 1, file, len); + /* And add the slash. */ + *--name = '/'; + + p = path; + do + { + char *startp; + + path = p; + p = strchrnul (path, ':'); + + if (p == path) + /* Two adjacent colons, or a colon at the beginning or the end + of `PATH' means to search the current directory. */ + startp = name + 1; + else + startp = (char *) memcpy (name - (p - path), path, p - path); + + /* Try to execute this name. If it works, execv will not return. */ + execve (startp, argv, envp); + + if (errno == ENOEXEC) + script_execute (startp, argv, envp); + + switch (errno) + { + case EACCES: + case ENOENT: + case ESTALE: + case ENOTDIR: + /* Those errors indicate the file is missing or not executable + by us, in which case we want to just try the next path + directory. */ + break; + + default: + /* Some other error means we found an executable file, but + something went wrong executing it; return the error to our + caller. */ + _exit (SPAWN_ERROR); + } + } + while (*p++ != '\0'); + + /* Return with an error. */ + _exit (SPAWN_ERROR); +}
new file mode 100644 --- /dev/null +++ b/m4/posix_spawn.m4 @@ -0,0 +1,54 @@ +# posix_spawn.m4 serial 1 +dnl Copyright (C) 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Tests whether the entire posix_spawn facility is available. +AC_DEFUN([gl_POSIX_SPAWN], +[ + AC_REQUIRE([gl_POSIX_SPAWN_BODY]) +]) + +AC_DEFUN([gl_POSIX_SPAWN_BODY], +[ + AC_REQUIRE([gl_SPAWN_H_DEFAULTS]) + AC_CHECK_FUNCS_ONCE([posix_spawn]) + dnl Assume that when the main function exists, all the others are + dnl available as well. + dnl AC_CHECK_FUNCS_ONCE([posix_spawnp]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_init]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_addclose]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_adddup2]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_addopen]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_destroy]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_init]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getflags]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setflags]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getpgroup]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setpgroup]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedparam]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedparam]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedpolicy]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedpolicy]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getsigdefault]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setsigdefault]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getsigmask]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setsigmask]) + dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_destroy]) + if test $ac_cv_func_posix_spawn != yes; then + HAVE_POSIX_SPAWN=0 + dnl For now, assume that if posix_spawn exists, it also works. + if false; then + REPLACE_POSIX_SPAWN=1 + fi + fi +]) + +AC_DEFUN([gl_POSIX_SPAWN_INTERNAL], +[ + AC_LIBOBJ([spawni]) + dnl Prerequisites of lib/spawni.c. + AC_CHECK_HEADERS([paths.h]) + AC_CHECK_FUNCS([confstr sched_setparam sched_setscheduler setegid seteuid vfork]) +])
new file mode 100644 --- /dev/null +++ b/modules/posix_spawn-internal @@ -0,0 +1,25 @@ +Description: +posix_spawn internals. + +Files: +lib/spawni.c +lib/spawn_int.h +m4/posix_spawn.m4 + +Depends-on: +spawn +alloca-opt +strchrnul + +configure.ac: + +Makefile.am: + +Include: + +License: +LGPL + +Maintainer: +Bruno Haible, glibc +