posix_spawn*: Move AC_LIBOBJ invocations to module description.
[gnulib.git] / m4 / posix_spawn.m4
1 # posix_spawn.m4 serial 9
2 dnl Copyright (C) 2008-2011 Free Software Foundation, Inc.
3 dnl This file is free software; the Free Software Foundation
4 dnl gives unlimited permission to copy and/or distribute it,
5 dnl with or without modifications, as long as this notice is preserved.
6
7 dnl Tests whether the entire posix_spawn facility is available.
8 AC_DEFUN([gl_POSIX_SPAWN],
9 [
10   AC_REQUIRE([gl_POSIX_SPAWN_BODY])
11 ])
12
13 AC_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         dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDULER
40         dnl evaluates to nonzero.
41         dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedpolicy])
42         dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedpolicy])
43         AC_CACHE_CHECK([whether posix_spawnattr_setschedpolicy is supported],
44           [gl_cv_func_spawnattr_setschedpolicy],
45           [AC_EGREP_CPP([POSIX scheduling supported], [
46 #include <spawn.h>
47 #if POSIX_SPAWN_SETSCHEDULER
48  POSIX scheduling supported
49 #endif
50 ],
51              [gl_cv_func_spawnattr_setschedpolicy=yes],
52              [gl_cv_func_spawnattr_setschedpolicy=no])
53           ])
54         dnl Assume that these functions are available if POSIX_SPAWN_SETSCHEDPARAM
55         dnl evaluates to nonzero.
56         dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_getschedparam])
57         dnl AC_CHECK_FUNCS_ONCE([posix_spawnattr_setschedparam])
58         AC_CACHE_CHECK([whether posix_spawnattr_setschedparam is supported],
59           [gl_cv_func_spawnattr_setschedparam],
60           [AC_EGREP_CPP([POSIX scheduling supported], [
61 #include <spawn.h>
62 #if POSIX_SPAWN_SETSCHEDPARAM
63  POSIX scheduling supported
64 #endif
65 ],
66              [gl_cv_func_spawnattr_setschedparam=yes],
67              [gl_cv_func_spawnattr_setschedparam=no])
68           ])
69         ;;
70       *) REPLACE_POSIX_SPAWN=1 ;;
71     esac
72   fi
73 ])
74
75 dnl Test whether posix_spawn actually works.
76 dnl posix_spawn on AIX 5.3..6.1 has two bugs:
77 dnl 1) When it fails to execute the program, the child process exits with
78 dnl    exit() rather than _exit(), which causes the stdio buffers to be
79 dnl    flushed. Reported by Rainer Tammer.
80 dnl 2) The posix_spawn_file_actions_addopen function does not support file
81 dnl    names that contain a '*'.
82 dnl posix_spawn on AIX 5.3..6.1 has also a third bug: It does not work
83 dnl when POSIX threads are used. But we don't test against this bug here.
84 AC_DEFUN([gl_POSIX_SPAWN_WORKS],
85 [
86   AC_REQUIRE([AC_PROG_CC])
87   AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
88   AC_CACHE_CHECK([whether posix_spawn works], [gl_cv_func_posix_spawn_works],
89     [if test $cross_compiling = no; then
90        AC_LINK_IFELSE([AC_LANG_SOURCE([[
91 #include <errno.h>
92 #include <fcntl.h>
93 #include <signal.h>
94 #include <spawn.h>
95 #include <stdbool.h>
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include <string.h>
99 #include <unistd.h>
100 #include <sys/types.h>
101 #include <sys/wait.h>
102
103 extern char **environ;
104
105 #ifndef STDIN_FILENO
106 # define STDIN_FILENO 0
107 #endif
108 #ifndef STDOUT_FILENO
109 # define STDOUT_FILENO 1
110 #endif
111 #ifndef STDERR_FILENO
112 # define STDERR_FILENO 2
113 #endif
114
115 #ifndef WTERMSIG
116 # define WTERMSIG(x) ((x) & 0x7f)
117 #endif
118 #ifndef WIFEXITED
119 # define WIFEXITED(x) (WTERMSIG (x) == 0)
120 #endif
121 #ifndef WEXITSTATUS
122 # define WEXITSTATUS(x) (((x) >> 8) & 0xff)
123 #endif
124
125 #define CHILD_PROGRAM_FILENAME "/non/exist/ent"
126
127 static int
128 fd_safer (int fd)
129 {
130   if (0 <= fd && fd <= 2)
131     {
132       int f = fd_safer (dup (fd));
133       int e = errno;
134       close (fd);
135       errno = e;
136       fd = f;
137     }
138
139   return fd;
140 }
141
142 int
143 main ()
144 {
145   char *argv[2] = { CHILD_PROGRAM_FILENAME, NULL };
146   int ofd[2];
147   sigset_t blocked_signals;
148   sigset_t fatal_signal_set;
149   posix_spawn_file_actions_t actions;
150   bool actions_allocated;
151   posix_spawnattr_t attrs;
152   bool attrs_allocated;
153   int err;
154   pid_t child;
155   int status;
156   int exitstatus;
157
158   setvbuf (stdout, NULL, _IOFBF, 0);
159   puts ("This should be seen only once.");
160   if (pipe (ofd) < 0 || (ofd[1] = fd_safer (ofd[1])) < 0)
161     {
162       perror ("cannot create pipe");
163       exit (1);
164     }
165   sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
166   sigemptyset (&fatal_signal_set);
167   sigaddset (&fatal_signal_set, SIGINT);
168   sigaddset (&fatal_signal_set, SIGTERM);
169   sigaddset (&fatal_signal_set, SIGHUP);
170   sigaddset (&fatal_signal_set, SIGPIPE);
171   sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
172   actions_allocated = false;
173   attrs_allocated = false;
174   if ((err = posix_spawn_file_actions_init (&actions)) != 0
175       || (actions_allocated = true,
176           (err = posix_spawn_file_actions_adddup2 (&actions, ofd[0], STDIN_FILENO)) != 0
177           || (err = posix_spawn_file_actions_addclose (&actions, ofd[0])) != 0
178           || (err = posix_spawn_file_actions_addclose (&actions, ofd[1])) != 0
179           || (err = posix_spawnattr_init (&attrs)) != 0
180           || (attrs_allocated = true,
181               (err = posix_spawnattr_setsigmask (&attrs, &blocked_signals)) != 0
182               || (err = posix_spawnattr_setflags (&attrs, POSIX_SPAWN_SETSIGMASK)) != 0)
183           || (err = posix_spawnp (&child, CHILD_PROGRAM_FILENAME, &actions, &attrs, argv, environ)) != 0))
184     {
185       if (actions_allocated)
186         posix_spawn_file_actions_destroy (&actions);
187       if (attrs_allocated)
188         posix_spawnattr_destroy (&attrs);
189       sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
190       if (err == ENOENT)
191         return 0;
192       else
193         {
194           errno = err;
195           perror ("subprocess failed");
196           exit (1);
197         }
198     }
199   posix_spawn_file_actions_destroy (&actions);
200   posix_spawnattr_destroy (&attrs);
201   sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
202   close (ofd[0]);
203   close (ofd[1]);
204   status = 0;
205   while (waitpid (child, &status, 0) != child)
206     ;
207   if (!WIFEXITED (status))
208     {
209       fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
210       exit (1);
211     }
212   exitstatus = WEXITSTATUS (status);
213   if (exitstatus != 127)
214     {
215       fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
216       exit (1);
217     }
218   return 0;
219 }
220 ]])],
221          [if test -s conftest$ac_exeext \
222              && ./conftest$ac_exeext > conftest.out \
223              && echo 'This should be seen only once.' > conftest.ok \
224              && cmp conftest.out conftest.ok > /dev/null; then
225             gl_cv_func_posix_spawn_works=yes
226           else
227             gl_cv_func_posix_spawn_works=no
228           fi],
229          [gl_cv_func_posix_spawn_works=no])
230        if test $gl_cv_func_posix_spawn_works = yes; then
231          AC_RUN_IFELSE([AC_LANG_SOURCE([[
232 /* Test whether posix_spawn_file_actions_addopen supports filename arguments
233    that contain special characters such as '*'.  */
234
235 #include <errno.h>
236 #include <fcntl.h>
237 #include <signal.h>
238 #include <spawn.h>
239 #include <stdbool.h>
240 #include <stdio.h>
241 #include <string.h>
242 #include <unistd.h>
243 #include <sys/types.h>
244 #include <sys/wait.h>
245
246 extern char **environ;
247
248 #ifndef STDIN_FILENO
249 # define STDIN_FILENO 0
250 #endif
251 #ifndef STDOUT_FILENO
252 # define STDOUT_FILENO 1
253 #endif
254 #ifndef STDERR_FILENO
255 # define STDERR_FILENO 2
256 #endif
257
258 #ifndef WTERMSIG
259 # define WTERMSIG(x) ((x) & 0x7f)
260 #endif
261 #ifndef WIFEXITED
262 # define WIFEXITED(x) (WTERMSIG (x) == 0)
263 #endif
264 #ifndef WEXITSTATUS
265 # define WEXITSTATUS(x) (((x) >> 8) & 0xff)
266 #endif
267
268 #define CHILD_PROGRAM_FILENAME "conftest"
269 #define DATA_FILENAME "conftest%=*#?"
270
271 static int
272 parent_main (void)
273 {
274   FILE *fp;
275   char *argv[3] = { CHILD_PROGRAM_FILENAME, "-child", NULL };
276   posix_spawn_file_actions_t actions;
277   bool actions_allocated;
278   int err;
279   pid_t child;
280   int status;
281   int exitstatus;
282
283   /* Create a data file with specific contents.  */
284   fp = fopen (DATA_FILENAME, "wb");
285   if (fp == NULL)
286     {
287       perror ("cannot create data file");
288       return 1;
289     }
290   fwrite ("Halle Potta", 1, 11, fp);
291   if (fflush (fp) || fclose (fp))
292     {
293       perror ("cannot prepare data file");
294       return 2;
295     }
296
297   /* Avoid reading from our stdin, as it could block.  */
298   freopen ("/dev/null", "rb", stdin);
299
300   /* Test whether posix_spawn_file_actions_addopen with this file name
301      actually works, but spawning a child that reads from this file.  */
302   actions_allocated = false;
303   if ((err = posix_spawn_file_actions_init (&actions)) != 0
304       || (actions_allocated = true,
305           (err = posix_spawn_file_actions_addopen (&actions, STDIN_FILENO, DATA_FILENAME, O_RDONLY, 0600)) != 0
306           || (err = posix_spawn (&child, CHILD_PROGRAM_FILENAME, &actions, NULL, argv, environ)) != 0))
307     {
308       if (actions_allocated)
309         posix_spawn_file_actions_destroy (&actions);
310       errno = err;
311       perror ("subprocess failed");
312       return 3;
313     }
314   posix_spawn_file_actions_destroy (&actions);
315   status = 0;
316   while (waitpid (child, &status, 0) != child)
317     ;
318   if (!WIFEXITED (status))
319     {
320       fprintf (stderr, "subprocess terminated with unexpected wait status %d\n", status);
321       return 4;
322     }
323   exitstatus = WEXITSTATUS (status);
324   if (exitstatus != 0)
325     {
326       fprintf (stderr, "subprocess terminated with unexpected exit status %d\n", exitstatus);
327       return 5;
328     }
329   return 0;
330 }
331
332 static int
333 child_main (void)
334 {
335   char buf[1024];
336
337   /* See if reading from STDIN_FILENO yields the expected contents.  */
338   if (fread (buf, 1, sizeof (buf), stdin) == 11
339       && memcmp (buf, "Halle Potta", 11) == 0)
340     return 0;
341   else
342     return 8;
343 }
344
345 static void
346 cleanup_then_die (int sig)
347 {
348   /* Clean up data file.  */
349   unlink (DATA_FILENAME);
350
351   /* Re-raise the signal and die from it.  */
352   signal (sig, SIG_DFL);
353   raise (sig);
354 }
355
356 int
357 main (int argc, char *argv[])
358 {
359   int exitstatus;
360
361   if (!(argc > 1 && strcmp (argv[1], "-child") == 0))
362     {
363       /* This is the parent process.  */
364       signal (SIGINT, cleanup_then_die);
365       signal (SIGTERM, cleanup_then_die);
366       #ifdef SIGHUP
367       signal (SIGHUP, cleanup_then_die);
368       #endif
369
370       exitstatus = parent_main ();
371     }
372   else
373     {
374       /* This is the child process.  */
375
376       exitstatus = child_main ();
377     }
378   unlink (DATA_FILENAME);
379   return exitstatus;
380 }
381 ]])],
382            [],
383            [gl_cv_func_posix_spawn_works=no])
384        fi
385      else
386        case "$host_os" in
387          aix*) gl_cv_func_posix_spawn_works="guessing no";;
388          *)    gl_cv_func_posix_spawn_works="guessing yes";;
389        esac
390      fi
391     ])
392 ])
393
394 # Prerequisites of lib/spawni.c.
395 AC_DEFUN([gl_PREREQ_POSIX_SPAWN_INTERNAL],
396 [
397   AC_CHECK_HEADERS([paths.h])
398   AC_CHECK_FUNCS([confstr sched_setparam sched_setscheduler setegid seteuid vfork])
399 ])