| # posix_spawn.m4 serial 11 |
| dnl Copyright (C) 2008-2012 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_REQUIRE([gl_HAVE_POSIX_SPAWN]) |
| dnl Assume that when the main function exists, all the others, |
| dnl except posix_spawnattr_{get,set}sched*, are 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_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 |
| gl_POSIX_SPAWN_WORKS |
| case "$gl_cv_func_posix_spawn_works" in |
| *yes) |
| AC_DEFINE([HAVE_WORKING_POSIX_SPAWN], [1], |
| [Define if you have the posix_spawn and posix_spawnp functions and |
| they work.]) |
| dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDULER |
| dnl evaluates to nonzero. |
| dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedpolicy]) |
| dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedpolicy]) |
| AC_CACHE_CHECK([whether posix_spawnattr_setschedpolicy is supported], |
| [gl_cv_func_spawnattr_setschedpolicy], |
| [AC_EGREP_CPP([POSIX scheduling supported], [ |
| #include <spawn.h> |
| #if POSIX_SPAWN_SETSCHEDULER |
| POSIX scheduling supported |
| #endif |
| ], |
| [gl_cv_func_spawnattr_setschedpolicy=yes], |
| [gl_cv_func_spawnattr_setschedpolicy=no]) |
| ]) |
| dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDPARAM |
| dnl evaluates to nonzero. |
| dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedparam]) |
| dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedparam]) |
| AC_CACHE_CHECK([whether posix_spawnattr_setschedparam is supported], |
| [gl_cv_func_spawnattr_setschedparam], |
| [AC_EGREP_CPP([POSIX scheduling supported], [ |
| #include <spawn.h> |
| #if POSIX_SPAWN_SETSCHEDPARAM |
| POSIX scheduling supported |
| #endif |
| ], |
| [gl_cv_func_spawnattr_setschedparam=yes], |
| [gl_cv_func_spawnattr_setschedparam=no]) |
| ]) |
| ;; |
| *) REPLACE_POSIX_SPAWN=1 ;; |
| esac |
| fi |
| ]) |
| |
| dnl Test whether posix_spawn actually works. |
| dnl posix_spawn on AIX 5.3..6.1 has two bugs: |
| dnl 1) When it fails to execute the program, the child process exits with |
| dnl exit() rather than _exit(), which causes the stdio buffers to be |
| dnl flushed. Reported by Rainer Tammer. |
| dnl 2) The posix_spawn_file_actions_addopen function does not support file |
| dnl names that contain a '*'. |
| dnl posix_spawn on AIX 5.3..6.1 has also a third bug: It does not work |
| dnl when POSIX threads are used. But we don't test against this bug here. |
| AC_DEFUN([gl_POSIX_SPAWN_WORKS], |
| [ |
| AC_REQUIRE([AC_PROG_CC]) |
| AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles |
| AC_CACHE_CHECK([whether posix_spawn works], [gl_cv_func_posix_spawn_works], |
| [if test $cross_compiling = no; then |
| AC_LINK_IFELSE([AC_LANG_SOURCE([[ |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <spawn.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| extern char **environ; |
| |
| #ifndef STDIN_FILENO |
| # define STDIN_FILENO 0 |
| #endif |
| #ifndef STDOUT_FILENO |
| # define STDOUT_FILENO 1 |
| #endif |
| #ifndef STDERR_FILENO |
| # define STDERR_FILENO 2 |
| #endif |
| |
| #ifndef WTERMSIG |
| # define WTERMSIG(x) ((x) & 0x7f) |
| #endif |
| #ifndef WIFEXITED |
| # define WIFEXITED(x) (WTERMSIG (x) == 0) |
| #endif |
| #ifndef WEXITSTATUS |
| # define WEXITSTATUS(x) (((x) >> 8) & 0xff) |
| #endif |
| |
| #define CHILD_PROGRAM_FILENAME "/non/exist/ent" |
| |
| static int |
| fd_safer (int fd) |
| { |
| if (0 <= fd && fd <= 2) |
| { |
| int f = fd_safer (dup (fd)); |
| int e = errno; |
| close (fd); |
| errno = e; |
| fd = f; |
| } |
| |
| return fd; |
| } |
| |
| int |
| main () |
| { |
| char *argv[2] = { CHILD_PROGRAM_FILENAME, NULL }; |
| int ofd[2]; |
| sigset_t blocked_signals; |
| sigset_t fatal_signal_set; |
| posix_spawn_file_actions_t actions; |
| bool actions_allocated; |
| posix_spawnattr_t attrs; |
| bool attrs_allocated; |
| int err; |
| pid_t child; |
| int status; |
| int exitstatus; |
| |
| setvbuf (stdout, NULL, _IOFBF, 0); |
| puts ("This should be seen only once."); |
| if (pipe (ofd) < 0 || (ofd[1] = fd_safer (ofd[1])) < 0) |
| { |
| perror ("cannot create pipe"); |
| exit (1); |
| } |
| sigprocmask (SIG_SETMASK, NULL, &blocked_signals); |
| sigemptyset (&fatal_signal_set); |
| sigaddset (&fatal_signal_set, SIGINT); |
| sigaddset (&fatal_signal_set, SIGTERM); |
| sigaddset (&fatal_signal_set, SIGHUP); |
| sigaddset (&fatal_signal_set, SIGPIPE); |
| sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL); |
| actions_allocated = false; |
| attrs_allocated = false; |
| if ((err = posix_spawn_file_actions_init (&actions)) != 0 |
| || (actions_allocated = true, |
| (err = posix_spawn_file_actions_adddup2 (&actions, ofd[0], STDIN_FILENO)) != 0 |
| || (err = posix_spawn_file_actions_addclose (&actions, ofd[0])) != 0 |
| || (err = posix_spawn_file_actions_addclose (&actions, ofd[1])) != 0 |
| || (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, CHILD_PROGRAM_FILENAME, &actions, &attrs, argv, environ)) != 0)) |
| { |
| if (actions_allocated) |
| posix_spawn_file_actions_destroy (&actions); |
| if (attrs_allocated) |
| posix_spawnattr_destroy (&attrs); |
| sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL); |
| if (err == ENOENT) |
| return 0; |
| else |
| { |
| errno = err; |
| perror ("subprocess failed"); |
| exit (1); |
| } |
| } |
| posix_spawn_file_actions_destroy (&actions); |
| posix_spawnattr_destroy (&attrs); |
| sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL); |
| close (ofd[0]); |
| close (ofd[1]); |
| status = 0; |
| while (waitpid (child, &status, 0) != child) |
| ; |
| if (!WIFEXITED (status)) |
| { |
| fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status); |
| exit (1); |
| } |
| exitstatus = WEXITSTATUS (status); |
| if (exitstatus != 127) |
| { |
| fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus); |
| exit (1); |
| } |
| return 0; |
| } |
| ]])], |
| [if test -s conftest$ac_exeext \ |
| && ./conftest$ac_exeext > conftest.out \ |
| && echo 'This should be seen only once.' > conftest.ok \ |
| && cmp conftest.out conftest.ok > /dev/null; then |
| gl_cv_func_posix_spawn_works=yes |
| else |
| gl_cv_func_posix_spawn_works=no |
| fi], |
| [gl_cv_func_posix_spawn_works=no]) |
| if test $gl_cv_func_posix_spawn_works = yes; then |
| AC_RUN_IFELSE([AC_LANG_SOURCE([[ |
| /* Test whether posix_spawn_file_actions_addopen supports filename arguments |
| that contain special characters such as '*'. */ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| #include <spawn.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| extern char **environ; |
| |
| #ifndef STDIN_FILENO |
| # define STDIN_FILENO 0 |
| #endif |
| #ifndef STDOUT_FILENO |
| # define STDOUT_FILENO 1 |
| #endif |
| #ifndef STDERR_FILENO |
| # define STDERR_FILENO 2 |
| #endif |
| |
| #ifndef WTERMSIG |
| # define WTERMSIG(x) ((x) & 0x7f) |
| #endif |
| #ifndef WIFEXITED |
| # define WIFEXITED(x) (WTERMSIG (x) == 0) |
| #endif |
| #ifndef WEXITSTATUS |
| # define WEXITSTATUS(x) (((x) >> 8) & 0xff) |
| #endif |
| |
| #define CHILD_PROGRAM_FILENAME "conftest" |
| #define DATA_FILENAME "conftest%=*#?" |
| |
| static int |
| parent_main (void) |
| { |
| FILE *fp; |
| char *argv[3] = { CHILD_PROGRAM_FILENAME, "-child", NULL }; |
| posix_spawn_file_actions_t actions; |
| bool actions_allocated; |
| int err; |
| pid_t child; |
| int status; |
| int exitstatus; |
| |
| /* Create a data file with specific contents. */ |
| fp = fopen (DATA_FILENAME, "wb"); |
| if (fp == NULL) |
| { |
| perror ("cannot create data file"); |
| return 1; |
| } |
| fwrite ("Halle Potta", 1, 11, fp); |
| if (fflush (fp) || fclose (fp)) |
| { |
| perror ("cannot prepare data file"); |
| return 2; |
| } |
| |
| /* Avoid reading from our stdin, as it could block. */ |
| freopen ("/dev/null", "rb", stdin); |
| |
| /* Test whether posix_spawn_file_actions_addopen with this file name |
| actually works, but spawning a child that reads from this file. */ |
| actions_allocated = false; |
| if ((err = posix_spawn_file_actions_init (&actions)) != 0 |
| || (actions_allocated = true, |
| (err = posix_spawn_file_actions_addopen (&actions, STDIN_FILENO, DATA_FILENAME, O_RDONLY, 0600)) != 0 |
| || (err = posix_spawn (&child, CHILD_PROGRAM_FILENAME, &actions, NULL, argv, environ)) != 0)) |
| { |
| if (actions_allocated) |
| posix_spawn_file_actions_destroy (&actions); |
| errno = err; |
| perror ("subprocess failed"); |
| return 3; |
| } |
| posix_spawn_file_actions_destroy (&actions); |
| status = 0; |
| while (waitpid (child, &status, 0) != child) |
| ; |
| if (!WIFEXITED (status)) |
| { |
| fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status); |
| return 4; |
| } |
| exitstatus = WEXITSTATUS (status); |
| if (exitstatus != 0) |
| { |
| fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus); |
| return 5; |
| } |
| return 0; |
| } |
| |
| static int |
| child_main (void) |
| { |
| char buf[1024]; |
| |
| /* See if reading from STDIN_FILENO yields the expected contents. */ |
| if (fread (buf, 1, sizeof (buf), stdin) == 11 |
| && memcmp (buf, "Halle Potta", 11) == 0) |
| return 0; |
| else |
| return 8; |
| } |
| |
| static void |
| cleanup_then_die (int sig) |
| { |
| /* Clean up data file. */ |
| unlink (DATA_FILENAME); |
| |
| /* Re-raise the signal and die from it. */ |
| signal (sig, SIG_DFL); |
| raise (sig); |
| } |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| int exitstatus; |
| |
| if (!(argc > 1 && strcmp (argv[1], "-child") == 0)) |
| { |
| /* This is the parent process. */ |
| signal (SIGINT, cleanup_then_die); |
| signal (SIGTERM, cleanup_then_die); |
| #ifdef SIGHUP |
| signal (SIGHUP, cleanup_then_die); |
| #endif |
| |
| exitstatus = parent_main (); |
| } |
| else |
| { |
| /* This is the child process. */ |
| |
| exitstatus = child_main (); |
| } |
| unlink (DATA_FILENAME); |
| return exitstatus; |
| } |
| ]])], |
| [], |
| [gl_cv_func_posix_spawn_works=no]) |
| fi |
| else |
| case "$host_os" in |
| aix*) gl_cv_func_posix_spawn_works="guessing no";; |
| *) gl_cv_func_posix_spawn_works="guessing yes";; |
| esac |
| fi |
| ]) |
| ]) |
| |
| # Prerequisites of lib/spawni.c. |
| AC_DEFUN([gl_PREREQ_POSIX_SPAWN_INTERNAL], |
| [ |
| AC_CHECK_HEADERS([paths.h]) |
| AC_CHECK_FUNCS([confstr sched_setparam sched_setscheduler setegid seteuid vfork]) |
| ]) |
| |
| AC_DEFUN([gl_FUNC_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE], |
| [ |
| AC_REQUIRE([gl_SPAWN_H_DEFAULTS]) |
| AC_REQUIRE([AC_PROG_CC]) |
| AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles |
| gl_POSIX_SPAWN |
| if test $REPLACE_POSIX_SPAWN = 1; then |
| REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE=1 |
| else |
| dnl On Solaris 11 2011-11, posix_spawn_file_actions_addclose succeeds even |
| dnl if the fd argument is out of range. |
| AC_CACHE_CHECK([whether posix_spawn_file_actions_addclose works], |
| [gl_cv_func_posix_spawn_file_actions_addclose_works], |
| [AC_RUN_IFELSE( |
| [AC_LANG_SOURCE([[ |
| #include <spawn.h> |
| int main () |
| { |
| posix_spawn_file_actions_t actions; |
| if (posix_spawn_file_actions_init (&actions) != 0) |
| return 1; |
| if (posix_spawn_file_actions_addclose (&actions, 10000000) == 0) |
| return 2; |
| return 0; |
| }]])], |
| [gl_cv_func_posix_spawn_file_actions_addclose_works=yes], |
| [gl_cv_func_posix_spawn_file_actions_addclose_works=no], |
| [# Guess no on Solaris, yes otherwise. |
| case "$host_os" in |
| solaris*) gl_cv_func_posix_spawn_file_actions_addclose_works="guessing no";; |
| *) gl_cv_func_posix_spawn_file_actions_addclose_works="guessing yes";; |
| esac |
| ]) |
| ]) |
| case "$gl_cv_func_posix_spawn_file_actions_addclose_works" in |
| *yes) ;; |
| *) REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE=1 ;; |
| esac |
| fi |
| ]) |
| |
| AC_DEFUN([gl_FUNC_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2], |
| [ |
| AC_REQUIRE([gl_SPAWN_H_DEFAULTS]) |
| AC_REQUIRE([AC_PROG_CC]) |
| AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles |
| gl_POSIX_SPAWN |
| if test $REPLACE_POSIX_SPAWN = 1; then |
| REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2=1 |
| else |
| dnl On Solaris 11 2011-11, posix_spawn_file_actions_adddup2 succeeds even |
| dnl if the fd argument is out of range. |
| AC_CACHE_CHECK([whether posix_spawn_file_actions_adddup2 works], |
| [gl_cv_func_posix_spawn_file_actions_adddup2_works], |
| [AC_RUN_IFELSE( |
| [AC_LANG_SOURCE([[ |
| #include <spawn.h> |
| int main () |
| { |
| posix_spawn_file_actions_t actions; |
| if (posix_spawn_file_actions_init (&actions) != 0) |
| return 1; |
| if (posix_spawn_file_actions_adddup2 (&actions, 10000000, 2) == 0) |
| return 2; |
| return 0; |
| }]])], |
| [gl_cv_func_posix_spawn_file_actions_adddup2_works=yes], |
| [gl_cv_func_posix_spawn_file_actions_adddup2_works=no], |
| [# Guess no on Solaris, yes otherwise. |
| case "$host_os" in |
| solaris*) gl_cv_func_posix_spawn_file_actions_adddup2_works="guessing no";; |
| *) gl_cv_func_posix_spawn_file_actions_adddup2_works="guessing yes";; |
| esac |
| ]) |
| ]) |
| case "$gl_cv_func_posix_spawn_file_actions_adddup2_works" in |
| *yes) ;; |
| *) REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2=1 ;; |
| esac |
| fi |
| ]) |
| |
| AC_DEFUN([gl_FUNC_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN], |
| [ |
| AC_REQUIRE([gl_SPAWN_H_DEFAULTS]) |
| AC_REQUIRE([AC_PROG_CC]) |
| AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles |
| gl_POSIX_SPAWN |
| if test $REPLACE_POSIX_SPAWN = 1; then |
| REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN=1 |
| else |
| dnl On Solaris 11 2011-11, posix_spawn_file_actions_addopen succeeds even |
| dnl if the fd argument is out of range. |
| AC_CACHE_CHECK([whether posix_spawn_file_actions_addopen works], |
| [gl_cv_func_posix_spawn_file_actions_addopen_works], |
| [AC_RUN_IFELSE( |
| [AC_LANG_SOURCE([[ |
| #include <spawn.h> |
| #include <fcntl.h> |
| int main () |
| { |
| posix_spawn_file_actions_t actions; |
| if (posix_spawn_file_actions_init (&actions) != 0) |
| return 1; |
| if (posix_spawn_file_actions_addopen (&actions, 10000000, "foo", 0, O_RDONLY) |
| == 0) |
| return 2; |
| return 0; |
| }]])], |
| [gl_cv_func_posix_spawn_file_actions_addopen_works=yes], |
| [gl_cv_func_posix_spawn_file_actions_addopen_works=no], |
| [# Guess no on Solaris, yes otherwise. |
| case "$host_os" in |
| solaris*) gl_cv_func_posix_spawn_file_actions_addopen_works="guessing no";; |
| *) gl_cv_func_posix_spawn_file_actions_addopen_works="guessing yes";; |
| esac |
| ]) |
| ]) |
| case "$gl_cv_func_posix_spawn_file_actions_addopen_works" in |
| *yes) ;; |
| *) REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN=1 ;; |
| esac |
| fi |
| ]) |