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