1 /* Test capturing output from a subprocess.
2 Copyright (C) 2017-2019 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
23 #include <support/capture_subprocess.h>
24 #include <support/check.h>
25 #include <support/support.h>
26 #include <support/temp_file.h>
33 #include <array_length.h>
35 /* Nonzero if the program gets called via 'exec'. */
38 /* Hold the four initial argument used to respawn the process. */
39 static char *initial_argv
[5];
41 /* Write one byte at *P to FD and advance *P. Do nothing if *P is
44 transfer (const unsigned char **p
, int fd
)
48 TEST_VERIFY (write (fd
, *p
, 1) == 1);
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
};
58 write_mode_to_str (enum write_mode mode
)
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";
69 static enum write_mode
70 str_to_write_mode (const char *mode
)
72 if (strcmp (mode
, "out_first") == 0)
74 else if (strcmp (mode
, "err_first") == 0)
76 else if (strcmp (mode
, "interleave") == 0)
78 return write_mode_last
;
81 /* Describe what to write in the subprocess. */
86 enum write_mode write_mode
;
92 test_common (const struct test
*test
)
95 switch (test
->write_mode
)
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);
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);
113 const unsigned char *pout
= (const unsigned char *) test
->out
;
114 const unsigned char *perr
= (const unsigned char *) test
->err
;
117 transfer (&pout
, STDOUT_FILENO
);
118 transfer (&perr
, STDERR_FILENO
);
120 while (*pout
!= '\0' || *perr
!= '\0');
125 TEST_VERIFY (mode_ok
);
127 if (test
->signal
!= 0)
128 raise (test
->signal
);
133 parse_int (const char *str
)
136 long int ret
= strtol (str
, &endptr
, 10);
137 TEST_COMPARE (errno
, 0);
138 TEST_VERIFY (ret
>= 0 && ret
<= INT_MAX
);
142 /* For use with support_capture_subprogram. */
143 _Noreturn
static void
144 handle_restart (char *out
, char *err
, const char *write_mode
,
145 const char *signal
, const char *status
)
151 str_to_write_mode (write_mode
),
158 /* For use with support_capture_subprocess. */
159 _Noreturn
static void
160 callback (void *closure
)
162 const struct test
*test
= closure
;
166 /* Create a heap-allocated random string of letters. */
168 random_string (size_t length
)
170 char *result
= xmalloc (length
+ 1);
171 for (size_t i
= 0; i
< length
; ++i
)
172 result
[i
] = 'a' + (rand () % 26);
173 result
[length
] = '\0';
177 /* Check that the specific stream from the captured subprocess matches
180 check_stream (const char *what
, const struct xmemstream
*stream
,
181 const char *expected
)
183 if (strcmp (stream
->buffer
, expected
) != 0)
185 support_record_failure ();
186 printf ("error: captured %s data incorrect\n"
189 what
, expected
, stream
->buffer
);
191 if (stream
->length
!= strlen (expected
))
193 support_record_failure ();
194 printf ("error: captured %s data length incorrect\n"
197 what
, strlen (expected
), stream
->length
);
201 static struct support_capture_subprocess
202 do_subprocess (struct test
*test
)
204 return support_capture_subprocess (callback
, test
);
207 static struct support_capture_subprocess
208 do_subprogram (const struct test
*test
)
210 /* Three digits per byte plus null terminator. */
211 char signalstr
[3 * sizeof(int) + 1];
212 snprintf (signalstr
, sizeof (signalstr
), "%d", test
->signal
);
213 char statusstr
[3 * sizeof(int) + 1];
214 snprintf (statusstr
, sizeof (statusstr
), "%d", test
->status
);
218 /* 4 elements from initial_argv (path to ld.so, '--library-path', the
219 path', and application name'), 2 for restart argument ('--direct',
220 '--restart'), 5 arguments plus NULL. */
223 char *args
[argv_size
];
225 for (char **arg
= initial_argv
; *arg
!= NULL
; arg
++)
228 args
[argc
++] = (char*) "--direct";
229 args
[argc
++] = (char*) "--restart";
231 args
[argc
++] = test
->out
;
232 args
[argc
++] = test
->err
;
233 args
[argc
++] = (char*) write_mode_to_str (test
->write_mode
);
234 args
[argc
++] = signalstr
;
235 args
[argc
++] = statusstr
;
237 TEST_VERIFY (argc
< argv_size
);
239 return support_capture_subprogram (args
[0], args
);
249 do_multiple_tests (enum test_type type
)
251 const int lengths
[] = {0, 1, 17, 512, 20000, -1};
253 /* Test multiple combinations of support_capture_sub{process,program}.
255 length_idx_stdout: Index into the lengths array above,
256 controls how many bytes are written by the subprocess to
258 length_idx_stderr: Same for standard error.
259 write_mode: How standard output and standard error writes are
261 signal: Exit with no signal if zero, with SIGTERM if one.
262 status: Process exit status: 0 if zero, 3 if one. */
263 for (int length_idx_stdout
= 0; lengths
[length_idx_stdout
] >= 0;
265 for (int length_idx_stderr
= 0; lengths
[length_idx_stderr
] >= 0;
267 for (int write_mode
= 0; write_mode
< write_mode_last
; ++write_mode
)
268 for (int signal
= 0; signal
< 2; ++signal
)
269 for (int status
= 0; status
< 2; ++status
)
273 .out
= random_string (lengths
[length_idx_stdout
]),
274 .err
= random_string (lengths
[length_idx_stderr
]),
275 .write_mode
= write_mode
,
276 .signal
= signal
* SIGTERM
, /* 0 or SIGTERM. */
277 .status
= status
* 3, /* 0 or 3. */
279 TEST_VERIFY (strlen (test
.out
) == lengths
[length_idx_stdout
]);
280 TEST_VERIFY (strlen (test
.err
) == lengths
[length_idx_stderr
]);
282 struct support_capture_subprocess result
283 = type
== subprocess
? do_subprocess (&test
)
284 : do_subprogram (&test
);
286 check_stream ("stdout", &result
.out
, test
.out
);
287 check_stream ("stderr", &result
.err
, test
.err
);
289 /* Allowed output for support_capture_subprocess_check. */
291 if (lengths
[length_idx_stdout
] > 0)
292 check_allow
|= sc_allow_stdout
;
293 if (lengths
[length_idx_stderr
] > 0)
294 check_allow
|= sc_allow_stderr
;
295 if (check_allow
== 0)
296 check_allow
= sc_allow_none
;
298 if (test
.signal
!= 0)
300 TEST_VERIFY (WIFSIGNALED (result
.status
));
301 TEST_VERIFY (WTERMSIG (result
.status
) == test
.signal
);
302 support_capture_subprocess_check (&result
, "signal",
303 -SIGTERM
, check_allow
);
307 TEST_VERIFY (WIFEXITED (result
.status
));
308 TEST_VERIFY (WEXITSTATUS (result
.status
) == test
.status
);
309 support_capture_subprocess_check (&result
, "exit",
310 test
.status
, check_allow
);
312 support_capture_subprocess_free (&result
);
320 do_test (int argc
, char *argv
[])
322 /* We must have either:
324 - one or four parameters if called initially:
325 + argv[1]: path for ld.so optional
326 + argv[2]: "--library-path" optional
327 + argv[3]: the library path optional
328 + argv[4]: the application name
330 - six parameters left if called through re-execution:
331 + argv[1]: the application name
332 + argv[2]: the stdout to print
333 + argv[3]: the stderr to print
334 + argv[4]: the write mode to use
335 + argv[5]: the signal to issue
336 + argv[6]: the exit status code to use
338 * When built with --enable-hardcoded-path-in-tests or issued without
339 using the loader directly.
342 if (argc
!= (restart
? 6 : 5) && argc
!= (restart
? 6 : 2))
343 FAIL_EXIT1 ("wrong number of arguments (%d)", argc
);
347 handle_restart (argv
[1], /* stdout */
348 argv
[2], /* stderr */
349 argv
[3], /* write_mode */
350 argv
[4], /* signal */
351 argv
[5]); /* status */
354 initial_argv
[0] = argv
[1]; /* path for ld.so */
355 initial_argv
[1] = argv
[2]; /* "--library-path" */
356 initial_argv
[2] = argv
[3]; /* the library path */
357 initial_argv
[3] = argv
[4]; /* the application name */
358 initial_argv
[4] = NULL
;
360 do_multiple_tests (subprocess
);
361 do_multiple_tests (subprogram
);
366 #define CMDLINE_OPTIONS \
367 { "restart", no_argument, &restart, 1 },
368 #define TEST_FUNCTION_ARGV do_test
369 #include <support/test-driver.c>