pipe: be robust in face of closed fds
[gnulib.git] / tests / test-pipe.c
1 /* Test of create_pipe_bidi/wait_subprocess.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 #include <config.h>
19
20 #include "pipe.h"
21 #include "wait-process.h"
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 /* Depending on arguments, this test intentionally closes stderr or
31    starts life with stderr closed.  So, the error messages might not
32    always print, but at least the exit status will be reliable.  */
33 #define ASSERT(expr) \
34   do                                                                         \
35     {                                                                        \
36       if (!(expr))                                                           \
37         {                                                                    \
38           fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
39           fflush (stderr);                                                   \
40           abort ();                                                          \
41         }                                                                    \
42     }                                                                        \
43   while (0)
44
45 /* Create a bi-directional pipe to a test child, and validate that the
46    child program returns the expected output.  The child is the same
47    program as the parent ARGV0, but with different arguments.
48    STDERR_CLOSED is true if we have already closed fd 2.  */
49 static void
50 test_pipe (const char *argv0, bool stderr_closed)
51 {
52   int fd[2];
53   const char *argv[3];
54   pid_t pid;
55   char buffer[2] = { 'a', 'a' };
56
57   /* Set up child.  */
58   argv[0] = argv0;
59   argv[1] = stderr_closed ? "9" : "8";
60   argv[2] = NULL;
61   pid = create_pipe_bidi(argv0, argv0, (char **) argv,
62                          false, true, true, fd);
63   ASSERT (0 <= pid);
64   ASSERT (STDERR_FILENO < fd[0]);
65   ASSERT (STDERR_FILENO < fd[1]);
66
67   /* Push child's input.  */
68   ASSERT (write (fd[1], buffer, 1) == 1);
69
70   /* Get child's output.  */
71   ASSERT (read (fd[0], buffer, 2) == 1);
72
73   /* Wait for child.  */
74   ASSERT (wait_subprocess (pid, argv0, true, false, false, true, NULL) == 0);
75   ASSERT (close (fd[0]) == 0);
76   ASSERT (close (fd[1]) == 0);
77
78   /* Check the result.  */
79   ASSERT (buffer[0] == 'b');
80   ASSERT (buffer[1] == 'a');
81 }
82
83 int
84 main (int argc, const char *argv[])
85 {
86   int i;
87   int test;
88   ASSERT (argc == 2);
89   test = atoi (argv[1]);
90   switch (test)
91     {
92       /* Driver cases.  Selectively close various standard fds, to
93          ensure the child process is not impacted by this.  */
94     case 0:
95       break;
96     case 1:
97       close (0);
98       break;
99     case 2:
100       close (1);
101       break;
102     case 3:
103       close (0);
104       close (1);
105       break;
106     case 4:
107       close (2);
108       break;
109     case 5:
110       close (0);
111       close (2);
112       break;
113     case 6:
114       close (1);
115       close (2);
116       break;
117     case 7:
118       close (0);
119       close (1);
120       close (2);
121       break;
122
123       /* Slave cases.  Read one byte from fd 0, and write its value
124          plus one to fd 1.  fd 2 should be closed iff the argument is
125          9.  Check that no other fd's leaked.  */
126     case 8:
127     case 9:
128       {
129         char buffer[1];
130         ASSERT (read (STDIN_FILENO, buffer, 1) == 1);
131         buffer[0]++;
132         ASSERT (write (STDOUT_FILENO, buffer, 1) == 1);
133         errno = 0;
134         i = fcntl (STDERR_FILENO, F_GETFL);
135         if (test == 8)
136           ASSERT (0 <= i);
137         else
138           {
139             ASSERT (i < 0);
140             ASSERT (errno == EBADF);
141           }
142         for (i = 3; i < 7; i++)
143           {
144             errno = 0;
145             ASSERT (close (i) == -1);
146             ASSERT (errno == EBADF);
147           }
148         return 0;
149       }
150     default:
151       ASSERT (0);
152     }
153   /* All remaining code is for the driver.  Plug any leaks inherited
154      from outside world before starting, so that child has a clean
155      slate (at least for the fds that we might be manipulating).  */
156   for (i = 3; i < 7; i++)
157     close (i);
158   test_pipe (argv[0], 3 < test);
159   return 0;
160 }