xref: /netbsd-src/external/bsd/atf/dist/atf-c/tc.c (revision 274254cdae52594c1aa480a736aef78313d15c9c)
1 /*
2  * Automated Testing Framework (atf)
3  *
4  * Copyright (c) 2008 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/types.h>
31 #include <sys/time.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34 
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <limits.h>
38 #include <stdarg.h>
39 #include <stdbool.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "atf-c/config.h"
46 #include "atf-c/env.h"
47 #include "atf-c/error.h"
48 #include "atf-c/fs.h"
49 #include "atf-c/process.h"
50 #include "atf-c/sanity.h"
51 #include "atf-c/signals.h"
52 #include "atf-c/tc.h"
53 #include "atf-c/tcr.h"
54 #include "atf-c/text.h"
55 #include "atf-c/user.h"
56 
57 /* ---------------------------------------------------------------------
58  * Auxiliary types and functions.
59  * --------------------------------------------------------------------- */
60 
61 /* Parent-only stuff. */
62 struct timeout_data;
63 static atf_error_t body_parent(const atf_tc_t *, const atf_fs_path_t *,
64                                pid_t, atf_tcr_t *);
65 static atf_error_t cleanup_parent(const atf_tc_t *, pid_t);
66 static atf_error_t fork_body(const atf_tc_t *, const atf_fs_path_t *,
67                              atf_tcr_t *);
68 static atf_error_t fork_cleanup(const atf_tc_t *, const atf_fs_path_t *);
69 static atf_error_t get_tc_result(const atf_fs_path_t *, atf_tcr_t *);
70 static atf_error_t program_timeout(pid_t, const atf_tc_t *,
71                                    struct timeout_data *);
72 static void unprogram_timeout(struct timeout_data *);
73 static void sigalrm_handler(int);
74 
75 /* Child-only stuff. */
76 static void body_child(const atf_tc_t *, const atf_fs_path_t *)
77             ATF_DEFS_ATTRIBUTE_NORETURN;
78 static atf_error_t check_arch(const char *, void *);
79 static atf_error_t check_config(const char *, void *);
80 static atf_error_t check_machine(const char *, void *);
81 static atf_error_t check_prog(const char *, void *);
82 static atf_error_t check_prog_in_dir(const char *, void *);
83 static atf_error_t check_requirements(const atf_tc_t *);
84 static void cleanup_child(const atf_tc_t *, const atf_fs_path_t *)
85             ATF_DEFS_ATTRIBUTE_NORETURN;
86 static void fail_internal(const char *, int, const char *, const char *,
87                           const char *, va_list,
88                           void (*)(const char *, ...));
89 static void fatal_atf_error(const char *, atf_error_t)
90             ATF_DEFS_ATTRIBUTE_NORETURN;
91 static void fatal_libc_error(const char *, int)
92             ATF_DEFS_ATTRIBUTE_NORETURN;
93 static atf_error_t prepare_child(const atf_tc_t *, const atf_fs_path_t *);
94 static void write_tcr(const atf_tcr_t *);
95 
96 /* ---------------------------------------------------------------------
97  * The "atf_tc" type.
98  * --------------------------------------------------------------------- */
99 
100 /*
101  * Constructors/destructors.
102  */
103 
104 atf_error_t
105 atf_tc_init(atf_tc_t *tc, const char *ident, atf_tc_head_t head,
106             atf_tc_body_t body, atf_tc_cleanup_t cleanup,
107             const atf_map_t *config)
108 {
109     atf_error_t err;
110 
111     atf_object_init(&tc->m_object);
112 
113     tc->m_ident = ident;
114     tc->m_head = head;
115     tc->m_body = body;
116     tc->m_cleanup = cleanup;
117     tc->m_config = config;
118 
119     err = atf_map_init(&tc->m_vars);
120     if (atf_is_error(err))
121         goto err_object;
122 
123     err = atf_tc_set_md_var(tc, "ident", ident);
124     if (atf_is_error(err))
125         goto err_map;
126 
127     err = atf_tc_set_md_var(tc, "timeout", "300");
128     if (atf_is_error(err))
129         goto err_map;
130 
131     /* XXX Should the head be able to return error codes? */
132     tc->m_head(tc);
133 
134     if (strcmp(atf_tc_get_md_var(tc, "ident"), ident) != 0)
135         atf_tc_fail("Test case head modified the read-only 'ident' "
136                     "property");
137 
138     INV(!atf_is_error(err));
139     return err;
140 
141 err_map:
142     atf_map_fini(&tc->m_vars);
143 err_object:
144     atf_object_fini(&tc->m_object);
145 
146     return err;
147 }
148 
149 atf_error_t
150 atf_tc_init_pack(atf_tc_t *tc, const atf_tc_pack_t *pack,
151                  const atf_map_t *config)
152 {
153     return atf_tc_init(tc, pack->m_ident, pack->m_head, pack->m_body,
154                        pack->m_cleanup, config);
155 }
156 
157 void
158 atf_tc_fini(atf_tc_t *tc)
159 {
160     atf_map_fini(&tc->m_vars);
161 
162     atf_object_fini(&tc->m_object);
163 }
164 
165 /*
166  * Getters.
167  */
168 
169 const char *
170 atf_tc_get_ident(const atf_tc_t *tc)
171 {
172     return tc->m_ident;
173 }
174 
175 const char *
176 atf_tc_get_config_var(const atf_tc_t *tc, const char *name)
177 {
178     const char *val;
179     atf_map_citer_t iter;
180 
181     PRE(atf_tc_has_config_var(tc, name));
182     iter = atf_map_find_c(tc->m_config, name);
183     val = atf_map_citer_data(iter);
184     INV(val != NULL);
185 
186     return val;
187 }
188 
189 const char *
190 atf_tc_get_config_var_wd(const atf_tc_t *tc, const char *name,
191                          const char *defval)
192 {
193     const char *val;
194 
195     if (!atf_tc_has_config_var(tc, name))
196         val = defval;
197     else
198         val = atf_tc_get_config_var(tc, name);
199 
200     return val;
201 }
202 
203 const char *
204 atf_tc_get_md_var(const atf_tc_t *tc, const char *name)
205 {
206     const char *val;
207     atf_map_citer_t iter;
208 
209     PRE(atf_tc_has_md_var(tc, name));
210     iter = atf_map_find_c(&tc->m_vars, name);
211     val = atf_map_citer_data(iter);
212     INV(val != NULL);
213 
214     return val;
215 }
216 
217 bool
218 atf_tc_has_config_var(const atf_tc_t *tc, const char *name)
219 {
220     bool found;
221     atf_map_citer_t end, iter;
222 
223     if (tc->m_config == NULL)
224         found = false;
225     else {
226         iter = atf_map_find_c(tc->m_config, name);
227         end = atf_map_end_c(tc->m_config);
228         found = !atf_equal_map_citer_map_citer(iter, end);
229     }
230 
231     return found;
232 }
233 
234 bool
235 atf_tc_has_md_var(const atf_tc_t *tc, const char *name)
236 {
237     atf_map_citer_t end, iter;
238 
239     iter = atf_map_find_c(&tc->m_vars, name);
240     end = atf_map_end_c(&tc->m_vars);
241     return !atf_equal_map_citer_map_citer(iter, end);
242 }
243 
244 /*
245  * Modifiers.
246  */
247 
248 atf_error_t
249 atf_tc_set_md_var(atf_tc_t *tc, const char *name, const char *fmt, ...)
250 {
251     atf_error_t err;
252     char *value;
253     va_list ap;
254 
255     va_start(ap, fmt);
256     err = atf_text_format_ap(&value, fmt, ap);
257     va_end(ap);
258 
259     if (!atf_is_error(err))
260         err = atf_map_insert(&tc->m_vars, name, value, true);
261     else
262         free(value);
263 
264     return err;
265 }
266 
267 /* ---------------------------------------------------------------------
268  * Free functions.
269  * --------------------------------------------------------------------- */
270 
271 atf_error_t
272 atf_tc_run(const atf_tc_t *tc, atf_tcr_t *tcr,
273            const atf_fs_path_t *workdirbase)
274 {
275     atf_error_t err, cleanuperr;
276     atf_fs_path_t workdir;
277 
278     err = atf_fs_path_copy(&workdir, workdirbase);
279     if (atf_is_error(err))
280         goto out;
281 
282     err = atf_fs_path_append_fmt(&workdir, "atf.XXXXXX");
283     if (atf_is_error(err))
284         goto out_workdir;
285 
286     err = atf_fs_mkdtemp(&workdir);
287     if (atf_is_error(err))
288         goto out_workdir;
289 
290     err = fork_body(tc, &workdir, tcr);
291     cleanuperr = fork_cleanup(tc, &workdir);
292     if (!atf_is_error(cleanuperr))
293         (void)atf_fs_cleanup(&workdir);
294     if (!atf_is_error(err))
295         err = cleanuperr;
296     else if (atf_is_error(cleanuperr))
297         atf_error_free(cleanuperr);
298 
299 out_workdir:
300     atf_fs_path_fini(&workdir);
301 out:
302     return err;
303 }
304 
305 /*
306  * Parent-only stuff.
307  */
308 
309 static bool sigalrm_killed = false;
310 static pid_t sigalrm_pid = -1;
311 
312 static
313 void
314 sigalrm_handler(int signo)
315 {
316     INV(signo == SIGALRM);
317 
318     if (sigalrm_pid != -1) {
319         killpg(sigalrm_pid, SIGTERM);
320         sigalrm_killed = true;
321     }
322 }
323 
324 struct timeout_data {
325     bool m_programmed;
326     atf_signal_programmer_t m_sigalrm;
327 };
328 
329 static
330 atf_error_t
331 program_timeout(pid_t pid, const atf_tc_t *tc, struct timeout_data *td)
332 {
333     atf_error_t err;
334     long timeout;
335 
336     err = atf_text_to_long(atf_tc_get_md_var(tc, "timeout"), &timeout);
337     if (atf_is_error(err))
338         goto out;
339 
340     if (timeout != 0) {
341         sigalrm_pid = pid;
342         sigalrm_killed = false;
343 
344         err = atf_signal_programmer_init(&td->m_sigalrm, SIGALRM,
345                                          sigalrm_handler);
346         if (atf_is_error(err))
347             goto out;
348 
349         struct itimerval itv;
350         timerclear(&itv.it_interval);
351         timerclear(&itv.it_value);
352         itv.it_value.tv_sec = timeout;
353         if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
354             atf_signal_programmer_fini(&td->m_sigalrm);
355             err = atf_libc_error(errno, "Failed to program timeout "
356                                  "with %ld seconds", timeout);
357         }
358 
359         td->m_programmed = !atf_is_error(err);
360     } else
361         td->m_programmed = false;
362 
363 out:
364     return err;
365 }
366 
367 static
368 void
369 unprogram_timeout(struct timeout_data *td)
370 {
371     if (td->m_programmed) {
372         atf_signal_programmer_fini(&td->m_sigalrm);
373         sigalrm_pid = -1;
374         sigalrm_killed = false;
375     }
376 }
377 
378 static
379 atf_error_t
380 body_parent(const atf_tc_t *tc, const atf_fs_path_t *workdir, pid_t pid,
381             atf_tcr_t *tcr)
382 {
383     atf_error_t err;
384     int state;
385     struct timeout_data td;
386 
387     err = program_timeout(pid, tc, &td);
388     if (atf_is_error(err)) {
389         char buf[4096];
390 
391         atf_error_format(err, buf, sizeof(buf));
392         fprintf(stderr, "Error programming test case's timeout: %s", buf);
393         atf_error_free(err);
394         killpg(pid, SIGKILL);
395     }
396 
397     if (waitpid(pid, &state, 0) == -1) {
398         if (errno == EINTR && sigalrm_killed)
399             err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state,
400                                           "Test case timed out after %s "
401                                           "seconds",
402                                           atf_tc_get_md_var(tc, "timeout"));
403         else
404             err = atf_libc_error(errno, "Error waiting for child process "
405                                  "%d", pid);
406     } else {
407         if (!WIFEXITED(state) || WEXITSTATUS(state) != EXIT_SUCCESS)
408             err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state,
409                                           "Test case did not exit cleanly; "
410                                           "state was %d", state);
411         else
412             err = get_tc_result(workdir, tcr);
413     }
414 
415     unprogram_timeout(&td);
416 
417     return err;
418 }
419 
420 static
421 atf_error_t
422 cleanup_parent(const atf_tc_t *tc, pid_t pid)
423 {
424     atf_error_t err;
425     int state;
426 
427     if (waitpid(pid, &state, 0) == -1) {
428         err = atf_libc_error(errno, "Error waiting for child process "
429                              "%d", pid);
430         goto out;
431     }
432 
433     if (!WIFEXITED(state) || WEXITSTATUS(state) != EXIT_SUCCESS)
434         /* XXX Not really a libc error. */
435         err = atf_libc_error(EINVAL, "Child process did not exit cleanly");
436     else
437         err = atf_no_error();
438 
439 out:
440     return err;
441 }
442 
443 static
444 atf_error_t
445 fork_body(const atf_tc_t *tc, const atf_fs_path_t *workdir, atf_tcr_t *tcr)
446 {
447     atf_error_t err;
448     pid_t pid;
449 
450     err = atf_process_fork(&pid);
451     if (atf_is_error(err))
452         goto out;
453 
454     if (pid == 0) {
455         body_child(tc, workdir);
456         UNREACHABLE;
457         abort();
458     } else {
459         err = body_parent(tc, workdir, pid, tcr);
460     }
461 
462 out:
463     return err;
464 }
465 
466 static
467 atf_error_t
468 fork_cleanup(const atf_tc_t *tc, const atf_fs_path_t *workdir)
469 {
470     atf_error_t err;
471     pid_t pid;
472 
473     if (tc->m_cleanup == NULL)
474         err = atf_no_error();
475     else {
476         err = atf_process_fork(&pid);
477         if (atf_is_error(err))
478             goto out;
479 
480         if (pid == 0) {
481             cleanup_child(tc, workdir);
482             UNREACHABLE;
483             abort();
484         } else {
485             err = cleanup_parent(tc, pid);
486         }
487     }
488 
489 out:
490     return err;
491 }
492 
493 static
494 atf_error_t
495 get_tc_result(const atf_fs_path_t *workdir, atf_tcr_t *tcr)
496 {
497     atf_error_t err;
498     int fd;
499     atf_fs_path_t tcrfile;
500 
501     err = atf_fs_path_copy(&tcrfile, workdir);
502     if (atf_is_error(err))
503         goto out;
504 
505     err = atf_fs_path_append_fmt(&tcrfile, "tc-result");
506     if (atf_is_error(err))
507         goto out_tcrfile;
508 
509     fd = open(atf_fs_path_cstring(&tcrfile), O_RDONLY);
510     if (fd == -1) {
511         err = atf_libc_error(errno, "Cannot retrieve test case result");
512         goto out_tcrfile;
513     }
514 
515     err = atf_tcr_deserialize(tcr, fd);
516 
517     close(fd);
518 out_tcrfile:
519     atf_fs_path_fini(&tcrfile);
520 out:
521     return err;
522 }
523 
524 /*
525  * Child-only stuff.
526  */
527 
528 static const atf_tc_t *current_tc = NULL;
529 static const atf_fs_path_t *current_workdir = NULL;
530 static size_t current_tc_fail_count = 0;
531 
532 static
533 atf_error_t
534 prepare_child(const atf_tc_t *tc, const atf_fs_path_t *workdir)
535 {
536     atf_error_t err;
537     int i, ret;
538 
539     current_tc = tc;
540     current_workdir = workdir;
541     current_tc_fail_count = 0;
542 
543     ret = setpgid(getpid(), 0);
544     INV(ret != -1);
545 
546     umask(S_IWGRP | S_IWOTH);
547 
548     for (i = 1; i <= atf_signals_last_signo; i++)
549         atf_signal_reset(i);
550 
551     err = atf_env_set("HOME", atf_fs_path_cstring(workdir));
552     if (atf_is_error(err))
553         goto out;
554 
555     err = atf_env_unset("LANG");
556     if (atf_is_error(err))
557         goto out;
558 
559     err = atf_env_unset("LC_ALL");
560     if (atf_is_error(err))
561         goto out;
562 
563     err = atf_env_unset("LC_COLLATE");
564     if (atf_is_error(err))
565         goto out;
566 
567     err = atf_env_unset("LC_CTYPE");
568     if (atf_is_error(err))
569         goto out;
570 
571     err = atf_env_unset("LC_MESSAGES");
572     if (atf_is_error(err))
573         goto out;
574 
575     err = atf_env_unset("LC_MONETARY");
576     if (atf_is_error(err))
577         goto out;
578 
579     err = atf_env_unset("LC_NUMERIC");
580     if (atf_is_error(err))
581         goto out;
582 
583     err = atf_env_unset("LC_TIME");
584     if (atf_is_error(err))
585         goto out;
586 
587     err = atf_env_unset("TZ");
588     if (atf_is_error(err))
589         goto out;
590 
591     if (chdir(atf_fs_path_cstring(workdir)) == -1) {
592         err = atf_libc_error(errno, "Cannot enter work directory '%s'",
593                              atf_fs_path_cstring(workdir));
594         goto out;
595     }
596 
597     err = atf_no_error();
598 
599 out:
600     return err;
601 }
602 
603 static
604 void
605 body_child(const atf_tc_t *tc, const atf_fs_path_t *workdir)
606 {
607     atf_error_t err;
608 
609     atf_disable_exit_checks();
610 
611     err = prepare_child(tc, workdir);
612     if (atf_is_error(err))
613         goto print_err;
614     err = check_requirements(tc);
615     if (atf_is_error(err))
616         goto print_err;
617     tc->m_body(tc);
618 
619     if (current_tc_fail_count == 0)
620         atf_tc_pass();
621     else
622         atf_tc_fail("%d checks failed; see output for more details",
623                     current_tc_fail_count);
624 
625     UNREACHABLE;
626     abort();
627 
628 print_err:
629     INV(atf_is_error(err));
630     {
631         char buf[4096];
632 
633         atf_error_format(err, buf, sizeof(buf));
634         atf_error_free(err);
635 
636         atf_tc_fail("Error while preparing child process: %s", buf);
637     }
638 
639     UNREACHABLE;
640     abort();
641 }
642 
643 static
644 atf_error_t
645 check_arch(const char *arch, void *data)
646 {
647     bool *found = data;
648 
649     if (strcmp(arch, atf_config_get("atf_arch")) == 0)
650         *found = true;
651 
652     return atf_no_error();
653 }
654 
655 static
656 atf_error_t
657 check_config(const char *var, void *data)
658 {
659     if (!atf_tc_has_config_var(current_tc, var))
660         atf_tc_skip("Required configuration variable %s not defined", var);
661 
662     return atf_no_error();
663 }
664 
665 static
666 atf_error_t
667 check_machine(const char *machine, void *data)
668 {
669     bool *found = data;
670 
671     if (strcmp(machine, atf_config_get("atf_machine")) == 0)
672         *found = true;
673 
674     return atf_no_error();
675 }
676 
677 struct prog_found_pair {
678     const char *prog;
679     bool found;
680 };
681 
682 static
683 atf_error_t
684 check_prog(const char *prog, void *data)
685 {
686     atf_error_t err;
687     atf_fs_path_t p;
688 
689     err = atf_fs_path_init_fmt(&p, "%s", prog);
690     if (atf_is_error(err))
691         goto out;
692 
693     if (atf_fs_path_is_absolute(&p)) {
694         if (atf_is_error(atf_fs_eaccess(&p, atf_fs_access_x)))
695             atf_tc_skip("The required program %s could not be found", prog);
696     } else {
697         const char *path = atf_env_get("PATH");
698         struct prog_found_pair pf;
699         atf_fs_path_t bp;
700 
701         err = atf_fs_path_branch_path(&p, &bp);
702         if (atf_is_error(err))
703             goto out_p;
704 
705         if (strcmp(atf_fs_path_cstring(&bp), ".") != 0)
706             atf_tc_fail("Relative paths are not allowed when searching for "
707                         "a program (%s)", prog);
708 
709         pf.prog = prog;
710         pf.found = false;
711         err = atf_text_for_each_word(path, ":", check_prog_in_dir, &pf);
712         if (atf_is_error(err))
713             goto out_bp;
714 
715         if (!pf.found)
716             atf_tc_skip("The required program %s could not be found in "
717                         "the PATH", prog);
718 
719 out_bp:
720         atf_fs_path_fini(&bp);
721     }
722 
723 out_p:
724     atf_fs_path_fini(&p);
725 out:
726     return err;
727 }
728 
729 static
730 atf_error_t
731 check_prog_in_dir(const char *dir, void *data)
732 {
733     struct prog_found_pair *pf = data;
734     atf_error_t err;
735 
736     if (pf->found)
737         err = atf_no_error();
738     else {
739         atf_fs_path_t p;
740 
741         err = atf_fs_path_init_fmt(&p, "%s/%s", dir, pf->prog);
742         if (atf_is_error(err))
743             goto out_p;
744 
745         if (!atf_is_error(atf_fs_eaccess(&p, atf_fs_access_x)))
746             pf->found = true;
747 
748 out_p:
749         atf_fs_path_fini(&p);
750     }
751 
752     return err;
753 }
754 
755 static
756 atf_error_t
757 check_requirements(const atf_tc_t *tc)
758 {
759     atf_error_t err;
760 
761     err = atf_no_error();
762 
763     if (atf_tc_has_md_var(tc, "require.arch")) {
764         const char *arches = atf_tc_get_md_var(tc, "require.arch");
765         bool found = false;
766 
767         if (strlen(arches) == 0)
768             atf_tc_fail("Invalid value in the require.arch property");
769         else {
770             err = atf_text_for_each_word(arches, " ", check_arch, &found);
771             if (atf_is_error(err))
772                 goto out;
773 
774             if (!found)
775                 atf_tc_skip("Requires one of the '%s' architectures",
776                             arches);
777         }
778     }
779 
780     if (atf_tc_has_md_var(tc, "require.config")) {
781         const char *vars = atf_tc_get_md_var(tc, "require.config");
782 
783         if (strlen(vars) == 0)
784             atf_tc_fail("Invalid value in the require.config property");
785         else {
786             err = atf_text_for_each_word(vars, " ", check_config, NULL);
787             if (atf_is_error(err))
788                 goto out;
789         }
790     }
791 
792     if (atf_tc_has_md_var(tc, "require.machine")) {
793         const char *machines = atf_tc_get_md_var(tc, "require.machine");
794         bool found = false;
795 
796         if (strlen(machines) == 0)
797             atf_tc_fail("Invalid value in the require.machine property");
798         else {
799             err = atf_text_for_each_word(machines, " ", check_machine,
800                                          &found);
801             if (atf_is_error(err))
802                 goto out;
803 
804             if (!found)
805                 atf_tc_skip("Requires one of the '%s' machine types",
806                             machines);
807         }
808     }
809 
810     if (atf_tc_has_md_var(tc, "require.progs")) {
811         const char *progs = atf_tc_get_md_var(tc, "require.progs");
812 
813         if (strlen(progs) == 0)
814             atf_tc_fail("Invalid value in the require.progs property");
815         else {
816             err = atf_text_for_each_word(progs, " ", check_prog, NULL);
817             if (atf_is_error(err))
818                 goto out;
819         }
820     }
821 
822     if (atf_tc_has_md_var(tc, "require.user")) {
823         const char *u = atf_tc_get_md_var(tc, "require.user");
824 
825         if (strcmp(u, "root") == 0) {
826             if (!atf_user_is_root())
827                 atf_tc_skip("Requires root privileges");
828         } else if (strcmp(u, "unprivileged") == 0) {
829             if (atf_user_is_root())
830                 atf_tc_skip("Requires an unprivileged user");
831         } else
832             atf_tc_fail("Invalid value in the require.user property");
833     }
834 
835     INV(!atf_is_error(err));
836 out:
837     return err;
838 }
839 
840 static
841 void
842 cleanup_child(const atf_tc_t *tc, const atf_fs_path_t *workdir)
843 {
844     atf_error_t err;
845 
846     atf_disable_exit_checks();
847 
848     err = prepare_child(tc, workdir);
849     if (atf_is_error(err))
850         exit(EXIT_FAILURE);
851     else {
852         tc->m_cleanup(tc);
853         exit(EXIT_SUCCESS);
854     }
855 
856     UNREACHABLE;
857     abort();
858 }
859 
860 static
861 void
862 fatal_atf_error(const char *prefix, atf_error_t err)
863 {
864     char buf[1024];
865 
866     INV(atf_is_error(err));
867 
868     atf_error_format(err, buf, sizeof(buf));
869     atf_error_free(err);
870 
871     fprintf(stderr, "%s: %s", prefix, buf);
872 
873     abort();
874 }
875 
876 static
877 void
878 fatal_libc_error(const char *prefix, int err)
879 {
880     fprintf(stderr, "%s: %s", prefix, strerror(err));
881 
882     abort();
883 }
884 
885 static
886 void
887 write_tcr(const atf_tcr_t *tcr)
888 {
889     atf_error_t err;
890     int fd;
891     atf_fs_path_t tcrfile;
892 
893     err = atf_fs_path_copy(&tcrfile, current_workdir);
894     if (atf_is_error(err))
895         fatal_atf_error("Cannot write test case results", err);
896 
897     err = atf_fs_path_append_fmt(&tcrfile, "tc-result");
898     if (atf_is_error(err))
899         fatal_atf_error("Cannot write test case results", err);
900 
901     fd = open(atf_fs_path_cstring(&tcrfile),
902               O_WRONLY | O_CREAT | O_TRUNC, 0755);
903     if (fd == -1)
904         fatal_libc_error("Cannot write test case results", errno);
905 
906     err = atf_tcr_serialize(tcr, fd);
907     if (atf_is_error(err))
908         fatal_atf_error("Cannot write test case results", err);
909 
910     close(fd);
911 }
912 
913 void
914 atf_tc_fail(const char *fmt, ...)
915 {
916     va_list ap;
917     atf_tcr_t tcr;
918     atf_error_t err;
919 
920     PRE(current_tc != NULL);
921 
922     va_start(ap, fmt);
923     err = atf_tcr_init_reason_ap(&tcr, atf_tcr_failed_state, fmt, ap);
924     va_end(ap);
925     if (atf_is_error(err))
926         abort();
927 
928     write_tcr(&tcr);
929 
930     atf_tcr_fini(&tcr);
931 
932     exit(EXIT_SUCCESS);
933 }
934 
935 void
936 atf_tc_fail_nonfatal(const char *fmt, ...)
937 {
938     va_list ap;
939 
940     va_start(ap, fmt);
941     vfprintf(stderr, fmt, ap);
942     va_end(ap);
943     fprintf(stderr, "\n");
944 
945     current_tc_fail_count++;
946 }
947 
948 void
949 atf_tc_fail_check(const char *file, int line, const char *fmt, ...)
950 {
951     va_list ap;
952 
953     va_start(ap, fmt);
954     fail_internal(file, line, "Check failed", "*** ", fmt, ap,
955                   atf_tc_fail_nonfatal);
956     va_end(ap);
957 }
958 
959 void
960 atf_tc_fail_requirement(const char *file, int line, const char *fmt, ...)
961 {
962     va_list ap;
963 
964     va_start(ap, fmt);
965     fail_internal(file, line, "Requirement failed", "", fmt, ap,
966                   atf_tc_fail);
967     va_end(ap);
968 
969     UNREACHABLE;
970     abort();
971 }
972 
973 static
974 void
975 fail_internal(const char *file, int line, const char *reason,
976               const char *prefix, const char *fmt, va_list ap,
977               void (*failfunc)(const char *, ...))
978 {
979     va_list ap2;
980     atf_error_t err;
981     atf_dynstr_t msg;
982 
983     err = atf_dynstr_init_fmt(&msg, "%s%s:%d: %s: ", prefix, file, line,
984                               reason);
985     if (atf_is_error(err))
986         goto backup;
987 
988     va_copy(ap2, ap);
989     err = atf_dynstr_append_ap(&msg, fmt, ap2);
990     va_end(ap2);
991     if (atf_is_error(err)) {
992         atf_dynstr_fini(&msg);
993         goto backup;
994     }
995 
996     va_copy(ap2, ap);
997     failfunc("%s", atf_dynstr_cstring(&msg));
998     atf_dynstr_fini(&msg);
999     return;
1000 
1001 backup:
1002     va_copy(ap2, ap);
1003     failfunc(fmt, ap2);
1004     va_end(ap2);
1005 }
1006 
1007 void
1008 atf_tc_pass(void)
1009 {
1010     atf_tcr_t tcr;
1011     atf_error_t err;
1012 
1013     PRE(current_tc != NULL);
1014 
1015     err = atf_tcr_init(&tcr, atf_tcr_passed_state);
1016     if (atf_is_error(err))
1017         abort();
1018 
1019     write_tcr(&tcr);
1020 
1021     atf_tcr_fini(&tcr);
1022 
1023     exit(EXIT_SUCCESS);
1024 }
1025 
1026 void
1027 atf_tc_require_prog(const char *prog)
1028 {
1029     atf_error_t err;
1030 
1031     err = check_prog(prog, NULL);
1032     if (atf_is_error(err))
1033         atf_tc_fail("atf_tc_require_prog failed"); /* XXX Correct? */
1034 }
1035 
1036 void
1037 atf_tc_skip(const char *fmt, ...)
1038 {
1039     va_list ap;
1040     atf_tcr_t tcr;
1041     atf_error_t err;
1042 
1043     PRE(current_tc != NULL);
1044 
1045     va_start(ap, fmt);
1046     err = atf_tcr_init_reason_ap(&tcr, atf_tcr_skipped_state, fmt, ap);
1047     va_end(ap);
1048     if (atf_is_error(err))
1049         abort();
1050 
1051     write_tcr(&tcr);
1052 
1053     atf_tcr_fini(&tcr);
1054 
1055     exit(EXIT_SUCCESS);
1056 }
1057