Use spaces for indentation, not tabs.
[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 <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 /* Depending on arguments, this test intentionally closes stderr or
31    starts life with stderr closed.  So, we arrange to have fd 10
32    (outside the range of interesting fd's during the test) set up to
33    duplicate the original stderr.  */
34
35 #define BACKUP_STDERR_FILENO 10
36 static FILE *myerr;
37
38 #define ASSERT(expr) \
39   do                                                                         \
40     {                                                                        \
41       if (!(expr))                                                           \
42         {                                                                    \
43           fprintf (myerr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
44           fflush (myerr);                                                    \
45           abort ();                                                          \
46         }                                                                    \
47     }                                                                        \
48   while (0)
49
50 /* Code executed by the child process.  argv[1] = "child".  */
51 static int
52 child_main (int argc, char *argv[])
53 {
54   char buffer[2] = { 's', 't' };
55   int fd;
56   int ret;
57
58   ASSERT (argc == 3);
59
60   /* Read one byte from fd 0, and write its value plus one to fd 1.
61      fd 2 should be closed iff the argument is 1.  Check that no other file
62      descriptors leaked.  */
63
64   ASSERT (read (STDIN_FILENO, buffer, 2) == 1);
65
66   buffer[0]++;
67   ASSERT (write (STDOUT_FILENO, buffer, 1) == 1);
68
69   errno = 0;
70   ret = dup2 (STDERR_FILENO, STDERR_FILENO);
71   switch (atoi (argv[2]))
72     {
73     case 0:
74       /* Expect fd 2 is open.  */
75       ASSERT (ret == STDERR_FILENO);
76       break;
77     case 1:
78       /* Expect fd 2 is closed.  */
79       ASSERT (ret == -1);
80       ASSERT (errno == EBADF);
81       break;
82     default:
83       ASSERT (false);
84     }
85
86   for (fd = 3; fd < 7; fd++)
87     {
88       errno = 0;
89       ASSERT (close (fd) == -1);
90       ASSERT (errno == EBADF);
91     }
92
93   return 0;
94 }
95
96 /* Create a bi-directional pipe to a test child, and validate that the
97    child program returns the expected output.  The child is the same
98    program as the parent ARGV0, but with different arguments.
99    STDERR_CLOSED is true if we have already closed fd 2.  */
100 static void
101 test_pipe (const char *argv0, bool stderr_closed)
102 {
103   int fd[2];
104   char *argv[4];
105   pid_t pid;
106   char buffer[2] = { 'a', 't' };
107
108   /* Set up child.  */
109   argv[0] = (char *) argv0;
110   argv[1] = (char *) "child";
111   argv[2] = (char *) (stderr_closed ? "1" : "0");
112   argv[3] = NULL;
113   pid = create_pipe_bidi (argv0, argv0, argv, false, true, true, fd);
114   ASSERT (0 <= pid);
115   ASSERT (STDERR_FILENO < fd[0]);
116   ASSERT (STDERR_FILENO < fd[1]);
117
118   /* Push child's input.  */
119   ASSERT (write (fd[1], buffer, 1) == 1);
120   ASSERT (close (fd[1]) == 0);
121
122   /* Get child's output.  */
123   ASSERT (read (fd[0], buffer, 2) == 1);
124
125   /* Wait for child.  */
126   ASSERT (wait_subprocess (pid, argv0, true, false, true, true, NULL) == 0);
127   ASSERT (close (fd[0]) == 0);
128
129   /* Check the result.  */
130   ASSERT (buffer[0] == 'b');
131   ASSERT (buffer[1] == 't');
132 }
133
134 /* Code executed by the parent process.  */
135 static int
136 parent_main (int argc, char *argv[])
137 {
138   int test;
139   int fd;
140
141   ASSERT (argc == 2);
142
143   /* Selectively close various standard fds, to verify the child process is
144      not impacted by this.  */
145   test = atoi (argv[1]);
146   switch (test)
147     {
148     case 0:
149       break;
150     case 1:
151       close (0);
152       break;
153     case 2:
154       close (1);
155       break;
156     case 3:
157       close (0);
158       close (1);
159       break;
160     case 4:
161       close (2);
162       break;
163     case 5:
164       close (0);
165       close (2);
166       break;
167     case 6:
168       close (1);
169       close (2);
170       break;
171     case 7:
172       close (0);
173       close (1);
174       close (2);
175       break;
176     default:
177       ASSERT (false);
178     }
179
180   /* Plug any file descriptor leaks inherited from outside world before
181      starting, so that child has a clean slate (at least for the fds that we
182      might be manipulating).  */
183   for (fd = 3; fd < 7; fd++)
184     close (fd);
185
186   test_pipe (argv[0], test >= 4);
187
188   return 0;
189 }
190
191 int
192 main (int argc, char *argv[])
193 {
194   if (argc < 2)
195     {
196       fprintf (stderr, "%s: need arguments\n", argv[0]);
197       return 2;
198     }
199   if (strcmp (argv[1], "child") == 0)
200     {
201       /* fd 2 might be closed, but fd BACKUP_STDERR_FILENO is the original
202          stderr.  */
203       myerr = fdopen (BACKUP_STDERR_FILENO, "w");
204       if (!myerr)
205         return 2;
206       return child_main (argc, argv);
207     }
208   /* We might close fd 2 later, so save it in fd 10.  */
209   if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO
210       || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL)
211     return 2;
212   return parent_main (argc, argv);
213 }