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