From: Bruno Haible Date: Tue, 27 Jan 2004 11:10:45 +0000 (+0000) Subject: New module 'execute'. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=93d436c91edffd70086e32b26e1f570256b71a8a;p=pspp New module 'execute'. --- diff --git a/ChangeLog b/ChangeLog index 90e9a716c2..f179faf1a6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2004-01-27 Bruno Haible + + * modules/execute: New file. + * MODULES.html.sh (func_all_modules): Add execute. + 2004-01-23 Paul Eggert * modules/argmatch, modules/obstack, modules/xstrtol: diff --git a/MODULES.html.sh b/MODULES.html.sh index 0821cb4cce..23e95e74b4 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -1873,7 +1873,7 @@ func_all_modules () func_begin_table func_module findprog func_module wait-process - #func_module execute + func_module execute #func_module pipe #func_module sh-quote func_end_table diff --git a/lib/ChangeLog b/lib/ChangeLog index 6a0abc6a83..82231d5b90 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,9 @@ +2004-01-27 Bruno Haible + + * execute.h: New file, from GNU gettext. + * execute.c: New file, from GNU gettext. + * w32spawn.h: New file, from GNU gettext. + 2004-01-23 Paul Eggert Exit-status fix from coreutils. diff --git a/lib/execute.c b/lib/execute.c new file mode 100644 index 0000000000..86df308f2d --- /dev/null +++ b/lib/execute.c @@ -0,0 +1,314 @@ +/* Creation of autonomous subprocesses. + Copyright (C) 2001-2003 Free Software Foundation, Inc. + Written by Bruno Haible , 2001. + + 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* Specification. */ +#include "execute.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "error.h" +#include "exit.h" +#include "fatal-signal.h" +#include "wait-process.h" +#include "gettext.h" + +#define _(str) gettext (str) + +#if defined _MSC_VER || defined __MINGW32__ + +/* Native Woe32 API. */ +# include +# include "w32spawn.h" + +#else + +/* Unix API. */ +# ifdef HAVE_POSIX_SPAWN +# include +# else +# ifdef HAVE_VFORK_H +# include +# endif +# endif + +#endif + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + + +#ifdef EINTR + +/* EINTR handling for close(), open(). + These functions can return -1/EINTR even though we don't have any + signal handlers set up, namely when we get interrupted via SIGSTOP. */ + +static inline int +nonintr_close (int fd) +{ + int retval; + + do + retval = close (fd); + while (retval < 0 && errno == EINTR); + + return retval; +} +#define close nonintr_close + +static inline int +nonintr_open (const char *pathname, int oflag, mode_t mode) +{ + int retval; + + do + retval = open (pathname, oflag, mode); + while (retval < 0 && errno == EINTR); + + return retval; +} +#undef open /* avoid warning on VMS */ +#define open nonintr_open + +#endif + + +/* Execute a command, optionally redirecting any of the three standard file + descriptors to /dev/null. Return its exit code. + If it didn't terminate correctly, exit if exit_on_error is true, otherwise + return 127. + If slave_process is true, the child process will be terminated when its + creator receives a catchable fatal signal. */ +int +execute (const char *progname, + const char *prog_path, char **prog_argv, + bool ignore_sigpipe, + bool null_stdin, bool null_stdout, bool null_stderr, + bool slave_process, bool exit_on_error) +{ +#if defined _MSC_VER || defined __MINGW32__ + + /* Native Woe32 API. */ + int orig_stdin; + int orig_stdout; + int orig_stderr; + int exitcode; + int nullinfd; + int nulloutfd; + + prog_argv = prepare_spawn (prog_argv); + + /* Save standard file handles of parent process. */ + if (null_stdin) + orig_stdin = dup_noinherit (STDIN_FILENO); + if (null_stdout) + orig_stdout = dup_noinherit (STDOUT_FILENO); + if (null_stderr) + orig_stderr = dup_noinherit (STDERR_FILENO); + exitcode = -1; + + /* Create standard file handles of child process. */ + nullinfd = -1; + nulloutfd = -1; + if ((!null_stdin + || ((nullinfd = open ("NUL", O_RDONLY, 0)) >= 0 + && (nullinfd == STDIN_FILENO + || (dup2 (nullinfd, STDIN_FILENO) >= 0 + && close (nullinfd) >= 0)))) + && (!(null_stdout || null_stderr) + || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0 + && (!null_stdout + || nulloutfd == STDOUT_FILENO + || dup2 (nulloutfd, STDOUT_FILENO) >= 0) + && (!null_stderr + || nulloutfd == STDERR_FILENO + || dup2 (nulloutfd, STDERR_FILENO) >= 0) + && ((null_stdout && nulloutfd == STDOUT_FILENO) + || (null_stderr && nulloutfd == STDERR_FILENO) + || close (nulloutfd) >= 0)))) + exitcode = spawnvp (P_WAIT, prog_path, prog_argv); + if (nulloutfd >= 0) + close (nulloutfd); + if (nullinfd >= 0) + close (nullinfd); + + /* Restore standard file handles of parent process. */ + if (null_stderr) + dup2 (orig_stderr, STDERR_FILENO), close (orig_stderr); + if (null_stdout) + dup2 (orig_stdout, STDOUT_FILENO), close (orig_stdout); + if (null_stdin) + dup2 (orig_stdin, STDIN_FILENO), close (orig_stdin); + + if (exitcode == -1) + { + if (exit_on_error || !null_stderr) + error (exit_on_error ? EXIT_FAILURE : 0, errno, + _("%s subprocess failed"), progname); + return 127; + } + + return exitcode; + +#else + + /* Unix API. */ + /* Note about 127: Some errors during posix_spawnp() cause the function + posix_spawnp() to return an error code; some other errors cause the + subprocess to exit with return code 127. It is implementation + dependent which error is reported which way. We treat both cases as + equivalent. */ +#if HAVE_POSIX_SPAWN + sigset_t blocked_signals; + posix_spawn_file_actions_t actions; + bool actions_allocated; + posix_spawnattr_t attrs; + bool attrs_allocated; + int err; + pid_t child; +#else + int child; +#endif + +#if HAVE_POSIX_SPAWN + if (slave_process) + { + sigprocmask (SIG_SETMASK, NULL, &blocked_signals); + block_fatal_signals (); + } + actions_allocated = false; + attrs_allocated = false; + if ((err = posix_spawn_file_actions_init (&actions)) != 0 + || (actions_allocated = true, + (null_stdin + && (err = posix_spawn_file_actions_addopen (&actions, + STDIN_FILENO, + "/dev/null", O_RDONLY, + 0)) + != 0) + || (null_stdout + && (err = posix_spawn_file_actions_addopen (&actions, + STDOUT_FILENO, + "/dev/null", O_RDWR, + 0)) + != 0) + || (null_stderr + && (err = posix_spawn_file_actions_addopen (&actions, + STDERR_FILENO, + "/dev/null", O_RDWR, + 0)) + != 0) + || (slave_process + && ((err = posix_spawnattr_init (&attrs)) != 0 + || (attrs_allocated = true, + (err = posix_spawnattr_setsigmask (&attrs, + &blocked_signals)) + != 0 + || (err = posix_spawnattr_setflags (&attrs, + POSIX_SPAWN_SETSIGMASK)) + != 0))) + || (err = posix_spawnp (&child, prog_path, &actions, + attrs_allocated ? &attrs : NULL, prog_argv, + environ)) + != 0)) + { + if (actions_allocated) + posix_spawn_file_actions_destroy (&actions); + if (attrs_allocated) + posix_spawnattr_destroy (&attrs); + if (slave_process) + unblock_fatal_signals (); + if (exit_on_error || !null_stderr) + error (exit_on_error ? EXIT_FAILURE : 0, err, + _("%s subprocess failed"), progname); + return 127; + } + posix_spawn_file_actions_destroy (&actions); + if (attrs_allocated) + posix_spawnattr_destroy (&attrs); +#else + if (slave_process) + block_fatal_signals (); + /* Use vfork() instead of fork() for efficiency. */ + if ((child = vfork ()) == 0) + { + /* Child process code. */ + int nullinfd; + int nulloutfd; + + if ((!null_stdin + || ((nullinfd = open ("/dev/null", O_RDONLY, 0)) >= 0 + && (nullinfd == STDIN_FILENO + || (dup2 (nullinfd, STDIN_FILENO) >= 0 + && close (nullinfd) >= 0)))) + && (!(null_stdout || null_stderr) + || ((nulloutfd = open ("/dev/null", O_RDWR, 0)) >= 0 + && (!null_stdout + || nulloutfd == STDOUT_FILENO + || dup2 (nulloutfd, STDOUT_FILENO) >= 0) + && (!null_stderr + || nulloutfd == STDERR_FILENO + || dup2 (nulloutfd, STDERR_FILENO) >= 0) + && ((null_stdout && nulloutfd == STDOUT_FILENO) + || (null_stderr && nulloutfd == STDERR_FILENO) + || close (nulloutfd) >= 0))) + && (!slave_process || (unblock_fatal_signals (), true))) + execvp (prog_path, prog_argv); + _exit (127); + } + if (child == -1) + { + if (slave_process) + unblock_fatal_signals (); + if (exit_on_error || !null_stderr) + error (exit_on_error ? EXIT_FAILURE : 0, errno, + _("%s subprocess failed"), progname); + return 127; + } +#endif + if (slave_process) + { + register_slave_subprocess (child); + unblock_fatal_signals (); + } + + return wait_subprocess (child, progname, ignore_sigpipe, null_stderr, + slave_process, exit_on_error); + +#endif +} diff --git a/lib/execute.h b/lib/execute.h new file mode 100644 index 0000000000..18c9c43264 --- /dev/null +++ b/lib/execute.h @@ -0,0 +1,41 @@ +/* Creation of autonomous subprocesses. + Copyright (C) 2001-2003 Free Software Foundation, Inc. + Written by Bruno Haible , 2001. + + 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef _EXECUTE_H +#define _EXECUTE_H + +#include + +/* Execute a command, optionally redirecting any of the three standard file + descriptors to /dev/null. Return its exit code. + If it didn't terminate correctly, exit if exit_on_error is true, otherwise + return 127. + If ignore_sigpipe is true, consider a subprocess termination due to SIGPIPE + as equivalent to a success. This is suitable for processes whose only + purpose is to write to standard output. + If slave_process is true, the child process will be terminated when its + creator receives a catchable fatal signal. + It is recommended that no signal is blocked or ignored while execute() + is called. See pipe.h for the reason. */ +extern int execute (const char *progname, + const char *prog_path, char **prog_argv, + bool ignore_sigpipe, + bool null_stdin, bool null_stdout, bool null_stderr, + bool slave_process, bool exit_on_error); + +#endif /* _EXECUTE_H */ diff --git a/lib/w32spawn.h b/lib/w32spawn.h new file mode 100644 index 0000000000..18e64d7d5f --- /dev/null +++ b/lib/w32spawn.h @@ -0,0 +1,169 @@ +/* Auxiliary functions for the creation of subprocesses. Native Woe32 API. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 2003. + + 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 2, 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, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Get declarations of the Win32 API functions. */ +#define WIN32_LEAN_AND_MEAN +#include + +/* Get _get_osfhandle() and _open_osfhandle(). */ +#include + +#include +#include + +#include "strpbrk.h" +#include "xalloc.h" + +/* Duplicates a file handle, making the copy uninheritable. */ +static int +dup_noinherit (int fd) +{ + HANDLE curr_process = GetCurrentProcess (); + HANDLE old_handle = (HANDLE) _get_osfhandle (fd); + HANDLE new_handle; + int nfd; + + if (!DuplicateHandle (curr_process, /* SourceProcessHandle */ + old_handle, /* SourceHandle */ + curr_process, /* TargetProcessHandle */ + (PHANDLE) &new_handle, /* TargetHandle */ + (DWORD) 0, /* DesiredAccess */ + FALSE, /* InheritHandle */ + DUPLICATE_SAME_ACCESS)) /* Options */ + error (EXIT_FAILURE, 0, _("DuplicateHandle failed with error code 0x%08x"), + GetLastError ()); + + nfd = _open_osfhandle ((long) new_handle, O_BINARY); + if (nfd < 0) + error (EXIT_FAILURE, errno, _("_open_osfhandle failed")); + + return nfd; +} + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +static char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = (char **) xmalloc ((argc + 1) * sizeof (char *)); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + bool quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = (char *) xmalloc (length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} diff --git a/m4/ChangeLog b/m4/ChangeLog index e6558389b6..17352859dd 100644 --- a/m4/ChangeLog +++ b/m4/ChangeLog @@ -1,3 +1,7 @@ +2004-01-27 Bruno Haible + + * execute.m4: New file, from GNU gettext. + 2003-11-24 Bruno Haible * allocsa.m4: New file, from GNU gettext. diff --git a/m4/execute.m4 b/m4/execute.m4 new file mode 100644 index 0000000000..ed0105488d --- /dev/null +++ b/m4/execute.m4 @@ -0,0 +1,17 @@ +# execute.m4 serial 1 +dnl Copyright (C) 2003 Free Software Foundation, Inc. +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +AC_DEFUN([gl_EXECUTE], +[ + dnl Prerequisites of lib/execute.c. + AC_REQUIRE([AC_C_INLINE]) + AC_REQUIRE([AC_TYPE_MODE_T]) + AC_CHECK_HEADERS_ONCE(unistd.h) + AC_REQUIRE([AC_FUNC_FORK]) + AC_CHECK_FUNCS(posix_spawn) +]) diff --git a/modules/execute b/modules/execute new file mode 100644 index 0000000000..f28640ff61 --- /dev/null +++ b/modules/execute @@ -0,0 +1,30 @@ +Description: +Creation of autonomous subprocesses. + +Files: +lib/execute.h +lib/execute.c +lib/w32spawn.h +m4/execute.m4 + +Depends-on: +error +exit +fatal-signal +wait-process +gettext +stdbool +strpbrk + +configure.ac: +gl_EXECUTE + +Makefile.am: +lib_SOURCES += execute.h execute.c w32spawn.h + +Include: +"execute.h" + +Maintainer: +Bruno Haible +