1 /* Test capturing output from a subprocess.
2    Copyright (C) 2017-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <support/capture_subprocess.h>
24 #include <support/check.h>
25 #include <support/support.h>
26 #include <support/temp_file.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 #include <paths.h>
30 #include <getopt.h>
31 #include <limits.h>
32 #include <errno.h>
33 #include <array_length.h>
34 
35 /* Nonzero if the program gets called via 'exec'.  */
36 static int restart;
37 
38 /* Hold the four initial argument used to respawn the process.  */
39 static char *initial_argv[5];
40 
41 /* Write one byte at *P to FD and advance *P.  Do nothing if *P is
42    '\0'.  */
43 static void
transfer(const unsigned char ** p,int fd)44 transfer (const unsigned char **p, int fd)
45 {
46   if (**p != '\0')
47     {
48       TEST_VERIFY (write (fd, *p, 1) == 1);
49       ++*p;
50     }
51 }
52 
53 /* Determine the order in which stdout and stderr are written.  */
54 enum write_mode { out_first, err_first, interleave,
55                   write_mode_last =  interleave };
56 
57 static const char *
write_mode_to_str(enum write_mode mode)58 write_mode_to_str (enum write_mode mode)
59 {
60   switch (mode)
61     {
62     case out_first:  return "out_first";
63     case err_first:  return "err_first";
64     case interleave: return "interleave";
65     default:         return "write_mode_last";
66     }
67 }
68 
69 static enum write_mode
str_to_write_mode(const char * mode)70 str_to_write_mode (const char *mode)
71 {
72   if (strcmp (mode, "out_first") == 0)
73     return out_first;
74   else if (strcmp (mode, "err_first") == 0)
75     return err_first;
76   else if (strcmp (mode, "interleave") == 0)
77     return interleave;
78   return write_mode_last;
79 }
80 
81 /* Describe what to write in the subprocess.  */
82 struct test
83 {
84   char *out;
85   char *err;
86   enum write_mode write_mode;
87   int signal;
88   int status;
89 };
90 
91 _Noreturn static void
test_common(const struct test * test)92 test_common (const struct test *test)
93 {
94   bool mode_ok = false;
95   switch (test->write_mode)
96     {
97     case out_first:
98       TEST_VERIFY (fputs (test->out, stdout) >= 0);
99       TEST_VERIFY (fflush (stdout) == 0);
100       TEST_VERIFY (fputs (test->err, stderr) >= 0);
101       TEST_VERIFY (fflush (stderr) == 0);
102       mode_ok = true;
103       break;
104     case err_first:
105       TEST_VERIFY (fputs (test->err, stderr) >= 0);
106       TEST_VERIFY (fflush (stderr) == 0);
107       TEST_VERIFY (fputs (test->out, stdout) >= 0);
108       TEST_VERIFY (fflush (stdout) == 0);
109       mode_ok = true;
110       break;
111     case interleave:
112       {
113         const unsigned char *pout = (const unsigned char *) test->out;
114         const unsigned char *perr = (const unsigned char *) test->err;
115         do
116           {
117             transfer (&pout, STDOUT_FILENO);
118             transfer (&perr, STDERR_FILENO);
119           }
120         while (*pout != '\0' || *perr != '\0');
121       }
122       mode_ok = true;
123       break;
124     }
125   TEST_VERIFY (mode_ok);
126 
127   if (test->signal != 0)
128     raise (test->signal);
129   exit (test->status);
130 }
131 
132 static int
parse_int(const char * str)133 parse_int (const char *str)
134 {
135   char *endptr;
136   long int ret;
137   errno = 0;
138   ret = strtol (str, &endptr, 10);
139   TEST_COMPARE (errno, 0);
140   TEST_VERIFY (ret >= 0 && ret <= INT_MAX);
141   return ret;
142 }
143 
144 /* For use with support_capture_subprogram.  */
145 _Noreturn static void
handle_restart(char * out,char * err,const char * write_mode,const char * signal,const char * status)146 handle_restart (char *out, char *err, const char *write_mode,
147 		const char *signal, const char *status)
148 {
149   struct test test =
150     {
151       out,
152       err,
153       str_to_write_mode (write_mode),
154       parse_int (signal),
155       parse_int (status)
156     };
157   test_common (&test);
158 }
159 
160 /* For use with support_capture_subprocess.  */
161 _Noreturn static void
callback(void * closure)162 callback (void *closure)
163 {
164   const struct test *test = closure;
165   test_common (test);
166 }
167 
168 /* Create a heap-allocated random string of letters.  */
169 static char *
random_string(size_t length)170 random_string (size_t length)
171 {
172   char *result = xmalloc (length + 1);
173   for (size_t i = 0; i < length; ++i)
174     result[i] = 'a' + (rand () % 26);
175   result[length] = '\0';
176   return result;
177 }
178 
179 /* Check that the specific stream from the captured subprocess matches
180    expectations.  */
181 static void
check_stream(const char * what,const struct xmemstream * stream,const char * expected)182 check_stream (const char *what, const struct xmemstream *stream,
183               const char *expected)
184 {
185   if (strcmp (stream->buffer, expected) != 0)
186     {
187       support_record_failure ();
188       printf ("error: captured %s data incorrect\n"
189               "  expected: %s\n"
190               "  actual:   %s\n",
191               what, expected, stream->buffer);
192     }
193   if (stream->length != strlen (expected))
194     {
195       support_record_failure ();
196       printf ("error: captured %s data length incorrect\n"
197               "  expected: %zu\n"
198               "  actual:   %zu\n",
199               what, strlen (expected), stream->length);
200     }
201 }
202 
203 static struct support_capture_subprocess
do_subprocess(struct test * test)204 do_subprocess (struct test *test)
205 {
206   return support_capture_subprocess (callback, test);
207 }
208 
209 static struct support_capture_subprocess
do_subprogram(const struct test * test)210 do_subprogram (const struct test *test)
211 {
212   /* Three digits per byte plus null terminator.  */
213   char signalstr[3 * sizeof(int) + 1];
214   snprintf (signalstr, sizeof (signalstr), "%d", test->signal);
215   char statusstr[3 * sizeof(int) + 1];
216   snprintf (statusstr, sizeof (statusstr), "%d", test->status);
217 
218   int argc = 0;
219   enum {
220     /* 4 elements from initial_argv (path to ld.so, '--library-path', the
221        path', and application name'), 2 for restart argument ('--direct',
222        '--restart'), 5 arguments plus NULL.  */
223     argv_size = 12
224   };
225   char *args[argv_size];
226 
227   for (char **arg = initial_argv; *arg != NULL; arg++)
228     args[argc++] = *arg;
229 
230   args[argc++] = (char*) "--direct";
231   args[argc++] = (char*) "--restart";
232 
233   args[argc++] = test->out;
234   args[argc++] = test->err;
235   args[argc++] = (char*) write_mode_to_str (test->write_mode);
236   args[argc++] = signalstr;
237   args[argc++] = statusstr;
238   args[argc]   = NULL;
239   TEST_VERIFY (argc < argv_size);
240 
241   return support_capture_subprogram (args[0], args);
242 }
243 
244 enum test_type
245 {
246   subprocess,
247   subprogram,
248 };
249 
250 static int
do_multiple_tests(enum test_type type)251 do_multiple_tests (enum test_type type)
252 {
253   const int lengths[] = {0, 1, 17, 512, 20000, -1};
254 
255   /* Test multiple combinations of support_capture_sub{process,program}.
256 
257      length_idx_stdout: Index into the lengths array above,
258        controls how many bytes are written by the subprocess to
259        standard output.
260      length_idx_stderr: Same for standard error.
261      write_mode: How standard output and standard error writes are
262        ordered.
263      signal: Exit with no signal if zero, with SIGTERM if one.
264      status: Process exit status: 0 if zero, 3 if one.  */
265   for (int length_idx_stdout = 0; lengths[length_idx_stdout] >= 0;
266        ++length_idx_stdout)
267     for (int length_idx_stderr = 0; lengths[length_idx_stderr] >= 0;
268          ++length_idx_stderr)
269       for (int write_mode = 0; write_mode < write_mode_last; ++write_mode)
270         for (int signal = 0; signal < 2; ++signal)
271           for (int status = 0; status < 2; ++status)
272             {
273               struct test test =
274                 {
275                   .out = random_string (lengths[length_idx_stdout]),
276                   .err = random_string (lengths[length_idx_stderr]),
277                   .write_mode = write_mode,
278                   .signal = signal * SIGTERM, /* 0 or SIGTERM.  */
279                   .status = status * 3,       /* 0 or 3.  */
280                 };
281               TEST_VERIFY (strlen (test.out) == lengths[length_idx_stdout]);
282               TEST_VERIFY (strlen (test.err) == lengths[length_idx_stderr]);
283 
284 	      struct support_capture_subprocess result
285 		= type == subprocess ? do_subprocess (&test)
286 				     : do_subprogram (&test);
287 
288               check_stream ("stdout", &result.out, test.out);
289               check_stream ("stderr", &result.err, test.err);
290 
291               /* Allowed output for support_capture_subprocess_check.  */
292               int check_allow = 0;
293               if (lengths[length_idx_stdout] > 0)
294                 check_allow |= sc_allow_stdout;
295               if (lengths[length_idx_stderr] > 0)
296                 check_allow |= sc_allow_stderr;
297               if (check_allow == 0)
298                 check_allow = sc_allow_none;
299 
300               if (test.signal != 0)
301                 {
302                   TEST_VERIFY (WIFSIGNALED (result.status));
303                   TEST_VERIFY (WTERMSIG (result.status) == test.signal);
304                   support_capture_subprocess_check (&result, "signal",
305                                                     -SIGTERM, check_allow);
306                 }
307               else
308                 {
309                   TEST_VERIFY (WIFEXITED (result.status));
310                   TEST_VERIFY (WEXITSTATUS (result.status) == test.status);
311                   support_capture_subprocess_check (&result, "exit",
312                                                     test.status, check_allow);
313                 }
314               support_capture_subprocess_free (&result);
315               free (test.out);
316               free (test.err);
317             }
318   return 0;
319 }
320 
321 static int
do_test(int argc,char * argv[])322 do_test (int argc, char *argv[])
323 {
324   /* We must have either:
325 
326      - one or four parameters if called initially:
327        + argv[1]: path for ld.so        optional
328        + argv[2]: "--library-path"      optional
329        + argv[3]: the library path      optional
330        + argv[4]: the application name
331 
332      - six parameters left if called through re-execution:
333        + argv[1]: the application name
334        + argv[2]: the stdout to print
335        + argv[3]: the stderr to print
336        + argv[4]: the write mode to use
337        + argv[5]: the signal to issue
338        + argv[6]: the exit status code to use
339 
340      * When built with --enable-hardcoded-path-in-tests or issued without
341        using the loader directly.
342   */
343 
344   if (argc != (restart ? 6 : 5) && argc != (restart ? 6 : 2))
345     FAIL_EXIT1 ("wrong number of arguments (%d)", argc);
346 
347   if (restart)
348     {
349       handle_restart (argv[1],  /* stdout  */
350 		      argv[2],  /* stderr  */
351 		      argv[3],  /* write_mode  */
352 		      argv[4],  /* signal  */
353 		      argv[5]); /* status  */
354     }
355 
356   initial_argv[0] = argv[1]; /* path for ld.so  */
357   initial_argv[1] = argv[2]; /* "--library-path"  */
358   initial_argv[2] = argv[3]; /* the library path  */
359   initial_argv[3] = argv[4]; /* the application name  */
360   initial_argv[4] = NULL;
361 
362   do_multiple_tests (subprocess);
363   do_multiple_tests (subprogram);
364 
365   return 0;
366 }
367 
368 #define CMDLINE_OPTIONS \
369   { "restart", no_argument, &restart, 1 },
370 #define TEST_FUNCTION_ARGV do_test
371 #include <support/test-driver.c>
372