1# posix_spawn.m4 serial 11
2dnl Copyright (C) 2008-2012 Free Software Foundation, Inc.
3dnl This file is free software; the Free Software Foundation
4dnl gives unlimited permission to copy and/or distribute it,
5dnl with or without modifications, as long as this notice is preserved.
6
7dnl Tests whether the entire posix_spawn facility is available.
8AC_DEFUN([gl_POSIX_SPAWN],
9[
10  AC_REQUIRE([gl_POSIX_SPAWN_BODY])
11])
12
13AC_DEFUN([gl_POSIX_SPAWN_BODY],
14[
15  AC_REQUIRE([gl_SPAWN_H_DEFAULTS])
16  AC_REQUIRE([gl_HAVE_POSIX_SPAWN])
17  dnl Assume that when the main function exists, all the others,
18  dnl except posix_spawnattr_{get,set}sched*, are available as well.
19  dnl AC_CHECK_FUNCS_ONCE([posix_spawnp])
20  dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_init])
21  dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_addclose])
22  dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_adddup2])
23  dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_addopen])
24  dnl AC_CHECK_FUNCS_ONCE([posix_spawn_file_actions_destroy])
25  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_init])
26  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getflags])
27  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setflags])
28  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getpgroup])
29  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setpgroup])
30  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getsigdefault])
31  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setsigdefault])
32  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getsigmask])
33  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setsigmask])
34  dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_destroy])
35  if test $ac_cv_func_posix_spawn = yes; then
36    gl_POSIX_SPAWN_WORKS
37    case "$gl_cv_func_posix_spawn_works" in
38      *yes)
39        AC_DEFINE([HAVE_WORKING_POSIX_SPAWN], [1],
40          [Define if you have the posix_spawn and posix_spawnp functions and
41           they work.])
42        dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDULER
43        dnl evaluates to nonzero.
44        dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedpolicy])
45        dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedpolicy])
46        AC_CACHE_CHECK([whether posix_spawnattr_setschedpolicy is supported],
47          [gl_cv_func_spawnattr_setschedpolicy],
48          [AC_EGREP_CPP([POSIX scheduling supported], [
49#include <spawn.h>
50#if POSIX_SPAWN_SETSCHEDULER
51 POSIX scheduling supported
52#endif
53],
54             [gl_cv_func_spawnattr_setschedpolicy=yes],
55             [gl_cv_func_spawnattr_setschedpolicy=no])
56          ])
57        dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDPARAM
58        dnl evaluates to nonzero.
59        dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedparam])
60        dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedparam])
61        AC_CACHE_CHECK([whether posix_spawnattr_setschedparam is supported],
62          [gl_cv_func_spawnattr_setschedparam],
63          [AC_EGREP_CPP([POSIX scheduling supported], [
64#include <spawn.h>
65#if POSIX_SPAWN_SETSCHEDPARAM
66 POSIX scheduling supported
67#endif
68],
69             [gl_cv_func_spawnattr_setschedparam=yes],
70             [gl_cv_func_spawnattr_setschedparam=no])
71          ])
72        ;;
73      *) REPLACE_POSIX_SPAWN=1 ;;
74    esac
75  fi
76])
77
78dnl Test whether posix_spawn actually works.
79dnl posix_spawn on AIX 5.3..6.1 has two bugs:
80dnl 1) When it fails to execute the program, the child process exits with
81dnl    exit() rather than _exit(), which causes the stdio buffers to be
82dnl    flushed. Reported by Rainer Tammer.
83dnl 2) The posix_spawn_file_actions_addopen function does not support file
84dnl    names that contain a '*'.
85dnl posix_spawn on AIX 5.3..6.1 has also a third bug: It does not work
86dnl when POSIX threads are used. But we don't test against this bug here.
87AC_DEFUN([gl_POSIX_SPAWN_WORKS],
88[
89  AC_REQUIRE([AC_PROG_CC])
90  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
91  AC_CACHE_CHECK([whether posix_spawn works], [gl_cv_func_posix_spawn_works],
92    [if test $cross_compiling = no; then
93       AC_LINK_IFELSE([AC_LANG_SOURCE([[
94#include <errno.h>
95#include <fcntl.h>
96#include <signal.h>
97#include <spawn.h>
98#include <stdbool.h>
99#include <stdio.h>
100#include <stdlib.h>
101#include <string.h>
102#include <unistd.h>
103#include <sys/types.h>
104#include <sys/wait.h>
105
106extern char **environ;
107
108#ifndef STDIN_FILENO
109# define STDIN_FILENO 0
110#endif
111#ifndef STDOUT_FILENO
112# define STDOUT_FILENO 1
113#endif
114#ifndef STDERR_FILENO
115# define STDERR_FILENO 2
116#endif
117
118#ifndef WTERMSIG
119# define WTERMSIG(x) ((x) & 0x7f)
120#endif
121#ifndef WIFEXITED
122# define WIFEXITED(x) (WTERMSIG (x) == 0)
123#endif
124#ifndef WEXITSTATUS
125# define WEXITSTATUS(x) (((x) >> 8) & 0xff)
126#endif
127
128#define CHILD_PROGRAM_FILENAME "/non/exist/ent"
129
130static int
131fd_safer (int fd)
132{
133  if (0 <= fd && fd <= 2)
134    {
135      int f = fd_safer (dup (fd));
136      int e = errno;
137      close (fd);
138      errno = e;
139      fd = f;
140    }
141
142  return fd;
143}
144
145int
146main ()
147{
148  char *argv[2] = { CHILD_PROGRAM_FILENAME, NULL };
149  int ofd[2];
150  sigset_t blocked_signals;
151  sigset_t fatal_signal_set;
152  posix_spawn_file_actions_t actions;
153  bool actions_allocated;
154  posix_spawnattr_t attrs;
155  bool attrs_allocated;
156  int err;
157  pid_t child;
158  int status;
159  int exitstatus;
160
161  setvbuf (stdout, NULL, _IOFBF, 0);
162  puts ("This should be seen only once.");
163  if (pipe (ofd) < 0 || (ofd[1] = fd_safer (ofd[1])) < 0)
164    {
165      perror ("cannot create pipe");
166      exit (1);
167    }
168  sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
169  sigemptyset (&fatal_signal_set);
170  sigaddset (&fatal_signal_set, SIGINT);
171  sigaddset (&fatal_signal_set, SIGTERM);
172  sigaddset (&fatal_signal_set, SIGHUP);
173  sigaddset (&fatal_signal_set, SIGPIPE);
174  sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
175  actions_allocated = false;
176  attrs_allocated = false;
177  if ((err = posix_spawn_file_actions_init (&actions)) != 0
178      || (actions_allocated = true,
179          (err = posix_spawn_file_actions_adddup2 (&actions, ofd[0], STDIN_FILENO)) != 0
180          || (err = posix_spawn_file_actions_addclose (&actions, ofd[0])) != 0
181          || (err = posix_spawn_file_actions_addclose (&actions, ofd[1])) != 0
182          || (err = posix_spawnattr_init (&attrs)) != 0
183          || (attrs_allocated = true,
184              (err = posix_spawnattr_setsigmask (&attrs, &blocked_signals)) != 0
185              || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0)
186          || (err = posix_spawnp (&child, CHILD_PROGRAM_FILENAME, &actions, &attrs, argv, environ)) != 0))
187    {
188      if (actions_allocated)
189        posix_spawn_file_actions_destroy (&actions);
190      if (attrs_allocated)
191        posix_spawnattr_destroy (&attrs);
192      sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
193      if (err == ENOENT)
194        return 0;
195      else
196        {
197          errno = err;
198          perror ("subprocess failed");
199          exit (1);
200        }
201    }
202  posix_spawn_file_actions_destroy (&actions);
203  posix_spawnattr_destroy (&attrs);
204  sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
205  close (ofd[0]);
206  close (ofd[1]);
207  status = 0;
208  while (waitpid (child, &status, 0) != child)
209    ;
210  if (!WIFEXITED (status))
211    {
212      fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
213      exit (1);
214    }
215  exitstatus = WEXITSTATUS (status);
216  if (exitstatus != 127)
217    {
218      fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
219      exit (1);
220    }
221  return 0;
222}
223]])],
224         [if test -s conftest$ac_exeext \
225             && ./conftest$ac_exeext > conftest.out \
226             && echo 'This should be seen only once.' > conftest.ok \
227             && cmp conftest.out conftest.ok > /dev/null; then
228            gl_cv_func_posix_spawn_works=yes
229          else
230            gl_cv_func_posix_spawn_works=no
231          fi],
232         [gl_cv_func_posix_spawn_works=no])
233       if test $gl_cv_func_posix_spawn_works = yes; then
234         AC_RUN_IFELSE([AC_LANG_SOURCE([[
235/* Test whether posix_spawn_file_actions_addopen supports filename arguments
236   that contain special characters such as '*'.  */
237
238#include <errno.h>
239#include <fcntl.h>
240#include <signal.h>
241#include <spawn.h>
242#include <stdbool.h>
243#include <stdio.h>
244#include <string.h>
245#include <unistd.h>
246#include <sys/types.h>
247#include <sys/wait.h>
248
249extern char **environ;
250
251#ifndef STDIN_FILENO
252# define STDIN_FILENO 0
253#endif
254#ifndef STDOUT_FILENO
255# define STDOUT_FILENO 1
256#endif
257#ifndef STDERR_FILENO
258# define STDERR_FILENO 2
259#endif
260
261#ifndef WTERMSIG
262# define WTERMSIG(x) ((x) & 0x7f)
263#endif
264#ifndef WIFEXITED
265# define WIFEXITED(x) (WTERMSIG (x) == 0)
266#endif
267#ifndef WEXITSTATUS
268# define WEXITSTATUS(x) (((x) >> 8) & 0xff)
269#endif
270
271#define CHILD_PROGRAM_FILENAME "conftest"
272#define DATA_FILENAME "conftest%=*#?"
273
274static int
275parent_main (void)
276{
277  FILE *fp;
278  char *argv[3] = { CHILD_PROGRAM_FILENAME, "-child", NULL };
279  posix_spawn_file_actions_t actions;
280  bool actions_allocated;
281  int err;
282  pid_t child;
283  int status;
284  int exitstatus;
285
286  /* Create a data file with specific contents.  */
287  fp = fopen (DATA_FILENAME, "wb");
288  if (fp == NULL)
289    {
290      perror ("cannot create data file");
291      return 1;
292    }
293  fwrite ("Halle Potta", 1, 11, fp);
294  if (fflush (fp) || fclose (fp))
295    {
296      perror ("cannot prepare data file");
297      return 2;
298    }
299
300  /* Avoid reading from our stdin, as it could block.  */
301  freopen ("/dev/null", "rb", stdin);
302
303  /* Test whether posix_spawn_file_actions_addopen with this file name
304     actually works, but spawning a child that reads from this file.  */
305  actions_allocated = false;
306  if ((err = posix_spawn_file_actions_init (&actions)) != 0
307      || (actions_allocated = true,
308          (err = posix_spawn_file_actions_addopen (&actions, STDIN_FILENO, DATA_FILENAME, O_RDONLY, 0600)) != 0
309          || (err = posix_spawn (&child, CHILD_PROGRAM_FILENAME, &actions, NULL, argv, environ)) != 0))
310    {
311      if (actions_allocated)
312        posix_spawn_file_actions_destroy (&actions);
313      errno = err;
314      perror ("subprocess failed");
315      return 3;
316    }
317  posix_spawn_file_actions_destroy (&actions);
318  status = 0;
319  while (waitpid (child, &status, 0) != child)
320    ;
321  if (!WIFEXITED (status))
322    {
323      fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
324      return 4;
325    }
326  exitstatus = WEXITSTATUS (status);
327  if (exitstatus != 0)
328    {
329      fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
330      return 5;
331    }
332  return 0;
333}
334
335static int
336child_main (void)
337{
338  char buf[1024];
339
340  /* See if reading from STDIN_FILENO yields the expected contents.  */
341  if (fread (buf, 1, sizeof (buf), stdin) == 11
342      && memcmp (buf, "Halle Potta", 11) == 0)
343    return 0;
344  else
345    return 8;
346}
347
348static void
349cleanup_then_die (int sig)
350{
351  /* Clean up data file.  */
352  unlink (DATA_FILENAME);
353
354  /* Re-raise the signal and die from it.  */
355  signal (sig, SIG_DFL);
356  raise (sig);
357}
358
359int
360main (int argc, char *argv[])
361{
362  int exitstatus;
363
364  if (!(argc > 1 && strcmp (argv[1], "-child") == 0))
365    {
366      /* This is the parent process.  */
367      signal (SIGINT, cleanup_then_die);
368      signal (SIGTERM, cleanup_then_die);
369      #ifdef SIGHUP
370      signal (SIGHUP, cleanup_then_die);
371      #endif
372
373      exitstatus = parent_main ();
374    }
375  else
376    {
377      /* This is the child process.  */
378
379      exitstatus = child_main ();
380    }
381  unlink (DATA_FILENAME);
382  return exitstatus;
383}
384]])],
385           [],
386           [gl_cv_func_posix_spawn_works=no])
387       fi
388     else
389       case "$host_os" in
390         aix*) gl_cv_func_posix_spawn_works="guessing no";;
391         *)    gl_cv_func_posix_spawn_works="guessing yes";;
392       esac
393     fi
394    ])
395])
396
397# Prerequisites of lib/spawni.c.
398AC_DEFUN([gl_PREREQ_POSIX_SPAWN_INTERNAL],
399[
400  AC_CHECK_HEADERS([paths.h])
401  AC_CHECK_FUNCS([confstr sched_setparam sched_setscheduler setegid seteuid vfork])
402])
403
404AC_DEFUN([gl_FUNC_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE],
405[
406  AC_REQUIRE([gl_SPAWN_H_DEFAULTS])
407  AC_REQUIRE([AC_PROG_CC])
408  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
409  gl_POSIX_SPAWN
410  if test $REPLACE_POSIX_SPAWN = 1; then
411    REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE=1
412  else
413    dnl On Solaris 11 2011-11, posix_spawn_file_actions_addclose succeeds even
414    dnl if the fd argument is out of range.
415    AC_CACHE_CHECK([whether posix_spawn_file_actions_addclose works],
416      [gl_cv_func_posix_spawn_file_actions_addclose_works],
417      [AC_RUN_IFELSE(
418         [AC_LANG_SOURCE([[
419#include <spawn.h>
420int main ()
421{
422  posix_spawn_file_actions_t actions;
423  if (posix_spawn_file_actions_init (&actions) != 0)
424    return 1;
425  if (posix_spawn_file_actions_addclose (&actions, 10000000) == 0)
426    return 2;
427  return 0;
428}]])],
429         [gl_cv_func_posix_spawn_file_actions_addclose_works=yes],
430         [gl_cv_func_posix_spawn_file_actions_addclose_works=no],
431         [# Guess no on Solaris, yes otherwise.
432          case "$host_os" in
433            solaris*) gl_cv_func_posix_spawn_file_actions_addclose_works="guessing no";;
434            *)        gl_cv_func_posix_spawn_file_actions_addclose_works="guessing yes";;
435          esac
436         ])
437      ])
438    case "$gl_cv_func_posix_spawn_file_actions_addclose_works" in
439      *yes) ;;
440      *) REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSE=1 ;;
441    esac
442  fi
443])
444
445AC_DEFUN([gl_FUNC_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2],
446[
447  AC_REQUIRE([gl_SPAWN_H_DEFAULTS])
448  AC_REQUIRE([AC_PROG_CC])
449  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
450  gl_POSIX_SPAWN
451  if test $REPLACE_POSIX_SPAWN = 1; then
452    REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2=1
453  else
454    dnl On Solaris 11 2011-11, posix_spawn_file_actions_adddup2 succeeds even
455    dnl if the fd argument is out of range.
456    AC_CACHE_CHECK([whether posix_spawn_file_actions_adddup2 works],
457      [gl_cv_func_posix_spawn_file_actions_adddup2_works],
458      [AC_RUN_IFELSE(
459         [AC_LANG_SOURCE([[
460#include <spawn.h>
461int main ()
462{
463  posix_spawn_file_actions_t actions;
464  if (posix_spawn_file_actions_init (&actions) != 0)
465    return 1;
466  if (posix_spawn_file_actions_adddup2 (&actions, 10000000, 2) == 0)
467    return 2;
468  return 0;
469}]])],
470         [gl_cv_func_posix_spawn_file_actions_adddup2_works=yes],
471         [gl_cv_func_posix_spawn_file_actions_adddup2_works=no],
472         [# Guess no on Solaris, yes otherwise.
473          case "$host_os" in
474            solaris*) gl_cv_func_posix_spawn_file_actions_adddup2_works="guessing no";;
475            *)        gl_cv_func_posix_spawn_file_actions_adddup2_works="guessing yes";;
476          esac
477         ])
478      ])
479    case "$gl_cv_func_posix_spawn_file_actions_adddup2_works" in
480      *yes) ;;
481      *) REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDDUP2=1 ;;
482    esac
483  fi
484])
485
486AC_DEFUN([gl_FUNC_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN],
487[
488  AC_REQUIRE([gl_SPAWN_H_DEFAULTS])
489  AC_REQUIRE([AC_PROG_CC])
490  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
491  gl_POSIX_SPAWN
492  if test $REPLACE_POSIX_SPAWN = 1; then
493    REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN=1
494  else
495    dnl On Solaris 11 2011-11, posix_spawn_file_actions_addopen succeeds even
496    dnl if the fd argument is out of range.
497    AC_CACHE_CHECK([whether posix_spawn_file_actions_addopen works],
498      [gl_cv_func_posix_spawn_file_actions_addopen_works],
499      [AC_RUN_IFELSE(
500         [AC_LANG_SOURCE([[
501#include <spawn.h>
502#include <fcntl.h>
503int main ()
504{
505  posix_spawn_file_actions_t actions;
506  if (posix_spawn_file_actions_init (&actions) != 0)
507    return 1;
508  if (posix_spawn_file_actions_addopen (&actions, 10000000, "foo", 0, O_RDONLY)
509      == 0)
510    return 2;
511  return 0;
512}]])],
513         [gl_cv_func_posix_spawn_file_actions_addopen_works=yes],
514         [gl_cv_func_posix_spawn_file_actions_addopen_works=no],
515         [# Guess no on Solaris, yes otherwise.
516          case "$host_os" in
517            solaris*) gl_cv_func_posix_spawn_file_actions_addopen_works="guessing no";;
518            *)        gl_cv_func_posix_spawn_file_actions_addopen_works="guessing yes";;
519          esac
520         ])
521      ])
522    case "$gl_cv_func_posix_spawn_file_actions_addopen_works" in
523      *yes) ;;
524      *) REPLACE_POSIX_SPAWN_FILE_ACTIONS_ADDOPEN=1 ;;
525    esac
526  fi
527])
528