xref: /freebsd/contrib/atf/atf-c/check.c (revision f81cdf24ba5436367377f7c8e8f51f6df2a75ca7)
1 /* Copyright (c) 2008 The NetBSD Foundation, Inc.
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  */
25 
26 #include "atf-c/check.h"
27 
28 #include <sys/wait.h>
29 
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #include "atf-c/build.h"
39 #include "atf-c/defs.h"
40 #include "atf-c/detail/dynstr.h"
41 #include "atf-c/detail/env.h"
42 #include "atf-c/detail/fs.h"
43 #include "atf-c/detail/list.h"
44 #include "atf-c/detail/process.h"
45 #include "atf-c/detail/sanity.h"
46 #include "atf-c/error.h"
47 #include "atf-c/utils.h"
48 
49 /* ---------------------------------------------------------------------
50  * Auxiliary functions.
51  * --------------------------------------------------------------------- */
52 
53 static
54 atf_error_t
55 create_tmpdir(atf_fs_path_t *dir)
56 {
57     atf_error_t err;
58 
59     err = atf_fs_path_init_fmt(dir, "%s/check.XXXXXX",
60                                atf_env_get_with_default("TMPDIR", "/tmp"));
61     if (atf_is_error(err))
62         goto out;
63 
64     err = atf_fs_mkdtemp(dir);
65     if (atf_is_error(err)) {
66         atf_fs_path_fini(dir);
67         goto out;
68     }
69 
70     INV(!atf_is_error(err));
71 out:
72     return err;
73 }
74 
75 static
76 void
77 cleanup_tmpdir(const atf_fs_path_t *dir, const atf_fs_path_t *outfile,
78                const atf_fs_path_t *errfile)
79 {
80     {
81         atf_error_t err = atf_fs_unlink(outfile);
82         if (atf_is_error(err)) {
83             INV(atf_error_is(err, "libc") &&
84                 atf_libc_error_code(err) == ENOENT);
85             atf_error_free(err);
86         } else
87             INV(!atf_is_error(err));
88     }
89 
90     {
91         atf_error_t err = atf_fs_unlink(errfile);
92         if (atf_is_error(err)) {
93             INV(atf_error_is(err, "libc") &&
94                 atf_libc_error_code(err) == ENOENT);
95             atf_error_free(err);
96         } else
97             INV(!atf_is_error(err));
98     }
99 
100     {
101         atf_error_t err = atf_fs_rmdir(dir);
102         INV(!atf_is_error(err));
103     }
104 }
105 
106 static
107 int
108 const_execvp(const char *file, const char *const *argv)
109 {
110 #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
111     return execvp(file, UNCONST(argv));
112 #undef UNCONST
113 }
114 
115 static
116 atf_error_t
117 init_sb(const atf_fs_path_t *path, atf_process_stream_t *sb)
118 {
119     atf_error_t err;
120 
121     if (path == NULL)
122         err = atf_process_stream_init_inherit(sb);
123     else
124         err = atf_process_stream_init_redirect_path(sb, path);
125 
126     return err;
127 }
128 
129 static
130 atf_error_t
131 init_sbs(const atf_fs_path_t *outfile, atf_process_stream_t *outsb,
132          const atf_fs_path_t *errfile, atf_process_stream_t *errsb)
133 {
134     atf_error_t err;
135 
136     err = init_sb(outfile, outsb);
137     if (atf_is_error(err))
138         goto out;
139 
140     err = init_sb(errfile, errsb);
141     if (atf_is_error(err)) {
142         atf_process_stream_fini(outsb);
143         goto out;
144     }
145 
146 out:
147     return err;
148 }
149 
150 struct exec_data {
151     const char *const *m_argv;
152 };
153 
154 static void exec_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
155 
156 static
157 void
158 exec_child(void *v)
159 {
160     struct exec_data *ea = v;
161 
162     const_execvp(ea->m_argv[0], ea->m_argv);
163     fprintf(stderr, "execvp(%s) failed: %s\n", ea->m_argv[0], strerror(errno));
164     exit(127);
165 }
166 
167 static
168 atf_error_t
169 fork_and_wait(const char *const *argv, const atf_fs_path_t *outfile,
170               const atf_fs_path_t *errfile, atf_process_status_t *status)
171 {
172     atf_error_t err;
173     atf_process_child_t child;
174     atf_process_stream_t outsb, errsb;
175     struct exec_data ea = { argv };
176 
177     err = init_sbs(outfile, &outsb, errfile, &errsb);
178     if (atf_is_error(err))
179         goto out;
180 
181     err = atf_process_fork(&child, exec_child, &outsb, &errsb, &ea);
182     if (atf_is_error(err))
183         goto out_sbs;
184 
185     err = atf_process_child_wait(&child, status);
186 
187 out_sbs:
188     atf_process_stream_fini(&errsb);
189     atf_process_stream_fini(&outsb);
190 out:
191     return err;
192 }
193 
194 static
195 void
196 update_success_from_status(const char *progname,
197                            const atf_process_status_t *status, bool *success)
198 {
199     bool s = atf_process_status_exited(status) &&
200              atf_process_status_exitstatus(status) == EXIT_SUCCESS;
201 
202     if (atf_process_status_exited(status)) {
203         if (atf_process_status_exitstatus(status) == EXIT_SUCCESS)
204             INV(s);
205         else {
206             INV(!s);
207             fprintf(stderr, "%s failed with exit code %d\n", progname,
208                     atf_process_status_exitstatus(status));
209         }
210     } else if (atf_process_status_signaled(status)) {
211         INV(!s);
212         fprintf(stderr, "%s failed due to signal %d%s\n", progname,
213                 atf_process_status_termsig(status),
214                 atf_process_status_coredump(status) ? " (core dumped)" : "");
215     } else {
216         INV(!s);
217         fprintf(stderr, "%s failed due to unknown reason\n", progname);
218     }
219 
220     *success = s;
221 }
222 
223 static
224 atf_error_t
225 array_to_list(const char *const *a, atf_list_t *l)
226 {
227     atf_error_t err;
228 
229     err = atf_list_init(l);
230     if (atf_is_error(err))
231         goto out;
232 
233     while (*a != NULL) {
234         char *item = strdup(*a);
235         if (item == NULL) {
236             err = atf_no_memory_error();
237             goto out;
238         }
239 
240         err = atf_list_append(l, item, true);
241         if (atf_is_error(err))
242             goto out;
243 
244         a++;
245     }
246 
247 out:
248     return err;
249 }
250 
251 static void
252 print_array(const char *const *array, const char *pfx)
253 {
254     const char *const *ptr;
255 
256     printf("%s", pfx);
257     for (ptr = array; *ptr != NULL; ptr++)
258         printf(" %s", *ptr);
259     printf("\n");
260 }
261 
262 static
263 atf_error_t
264 check_build_run(const char *const *argv, bool *success)
265 {
266     atf_error_t err;
267     atf_process_status_t status;
268 
269     print_array(argv, ">");
270 
271     err = fork_and_wait(argv, NULL, NULL, &status);
272     if (atf_is_error(err))
273         goto out;
274 
275     update_success_from_status(argv[0], &status, success);
276     atf_process_status_fini(&status);
277 
278     INV(!atf_is_error(err));
279 out:
280     return err;
281 }
282 
283 /* ---------------------------------------------------------------------
284  * The "atf_check_result" type.
285  * --------------------------------------------------------------------- */
286 
287 struct atf_check_result_impl {
288     atf_list_t m_argv;
289     atf_fs_path_t m_dir;
290     atf_fs_path_t m_stdout;
291     atf_fs_path_t m_stderr;
292     atf_process_status_t m_status;
293 };
294 
295 static
296 atf_error_t
297 atf_check_result_init(atf_check_result_t *r, const char *const *argv,
298                       const atf_fs_path_t *dir)
299 {
300     atf_error_t err;
301 
302     r->pimpl = malloc(sizeof(struct atf_check_result_impl));
303     if (r->pimpl == NULL)
304         return atf_no_memory_error();
305 
306     err = array_to_list(argv, &r->pimpl->m_argv);
307     if (atf_is_error(err))
308         goto out;
309 
310     err = atf_fs_path_copy(&r->pimpl->m_dir, dir);
311     if (atf_is_error(err))
312         goto err_argv;
313 
314     err = atf_fs_path_init_fmt(&r->pimpl->m_stdout, "%s/stdout",
315                                atf_fs_path_cstring(dir));
316     if (atf_is_error(err))
317         goto err_dir;
318 
319     err = atf_fs_path_init_fmt(&r->pimpl->m_stderr, "%s/stderr",
320                                atf_fs_path_cstring(dir));
321     if (atf_is_error(err))
322         goto err_stdout;
323 
324     INV(!atf_is_error(err));
325     goto out;
326 
327 err_stdout:
328     atf_fs_path_fini(&r->pimpl->m_stdout);
329 err_dir:
330     atf_fs_path_fini(&r->pimpl->m_dir);
331 err_argv:
332     atf_list_fini(&r->pimpl->m_argv);
333 out:
334     return err;
335 }
336 
337 void
338 atf_check_result_fini(atf_check_result_t *r)
339 {
340     atf_process_status_fini(&r->pimpl->m_status);
341 
342     cleanup_tmpdir(&r->pimpl->m_dir, &r->pimpl->m_stdout,
343                    &r->pimpl->m_stderr);
344     atf_fs_path_fini(&r->pimpl->m_stdout);
345     atf_fs_path_fini(&r->pimpl->m_stderr);
346     atf_fs_path_fini(&r->pimpl->m_dir);
347 
348     atf_list_fini(&r->pimpl->m_argv);
349 
350     free(r->pimpl);
351 }
352 
353 const char *
354 atf_check_result_stdout(const atf_check_result_t *r)
355 {
356     return atf_fs_path_cstring(&r->pimpl->m_stdout);
357 }
358 
359 const char *
360 atf_check_result_stderr(const atf_check_result_t *r)
361 {
362     return atf_fs_path_cstring(&r->pimpl->m_stderr);
363 }
364 
365 bool
366 atf_check_result_exited(const atf_check_result_t *r)
367 {
368     return atf_process_status_exited(&r->pimpl->m_status);
369 }
370 
371 int
372 atf_check_result_exitcode(const atf_check_result_t *r)
373 {
374     return atf_process_status_exitstatus(&r->pimpl->m_status);
375 }
376 
377 bool
378 atf_check_result_signaled(const atf_check_result_t *r)
379 {
380     return atf_process_status_signaled(&r->pimpl->m_status);
381 }
382 
383 int
384 atf_check_result_termsig(const atf_check_result_t *r)
385 {
386     return atf_process_status_termsig(&r->pimpl->m_status);
387 }
388 
389 /* ---------------------------------------------------------------------
390  * Free functions.
391  * --------------------------------------------------------------------- */
392 
393 /* XXX: This function shouldn't be in this module.  It messes with stdout
394  * and stderr, and it provides a very high-end interface.  This belongs,
395  * probably, somewhere related to test cases (such as in the tc module). */
396 atf_error_t
397 atf_check_build_c_o(const char *sfile,
398                     const char *ofile,
399                     const char *const optargs[],
400                     bool *success)
401 {
402     atf_error_t err;
403     char **argv;
404 
405     err = atf_build_c_o(sfile, ofile, optargs, &argv);
406     if (atf_is_error(err))
407         goto out;
408 
409     err = check_build_run((const char *const *)argv, success);
410 
411     atf_utils_free_charpp(argv);
412 out:
413     return err;
414 }
415 
416 atf_error_t
417 atf_check_build_cpp(const char *sfile,
418                     const char *ofile,
419                     const char *const optargs[],
420                     bool *success)
421 {
422     atf_error_t err;
423     char **argv;
424 
425     err = atf_build_cpp(sfile, ofile, optargs, &argv);
426     if (atf_is_error(err))
427         goto out;
428 
429     err = check_build_run((const char *const *)argv, success);
430 
431     atf_utils_free_charpp(argv);
432 out:
433     return err;
434 }
435 
436 atf_error_t
437 atf_check_build_cxx_o(const char *sfile,
438                       const char *ofile,
439                       const char *const optargs[],
440                       bool *success)
441 {
442     atf_error_t err;
443     char **argv;
444 
445     err = atf_build_cxx_o(sfile, ofile, optargs, &argv);
446     if (atf_is_error(err))
447         goto out;
448 
449     err = check_build_run((const char *const *)argv, success);
450 
451     atf_utils_free_charpp(argv);
452 out:
453     return err;
454 }
455 
456 atf_error_t
457 atf_check_exec_array(const char *const *argv, atf_check_result_t *r)
458 {
459     atf_error_t err;
460     atf_fs_path_t dir;
461 
462     err = create_tmpdir(&dir);
463     if (atf_is_error(err))
464         goto out;
465 
466     err = atf_check_result_init(r, argv, &dir);
467     if (atf_is_error(err)) {
468         atf_error_t err2 = atf_fs_rmdir(&dir);
469         INV(!atf_is_error(err2));
470         goto out;
471     }
472 
473     err = fork_and_wait(argv, &r->pimpl->m_stdout, &r->pimpl->m_stderr,
474                         &r->pimpl->m_status);
475     if (atf_is_error(err)) {
476         atf_check_result_fini(r);
477         goto out;
478     }
479 
480     INV(!atf_is_error(err));
481 
482     atf_fs_path_fini(&dir);
483 out:
484     return err;
485 }
486