xref: /netbsd-src/external/bsd/atf/dist/atf-c/tc.c (revision de4fa6c51a9708fc05f88b618fa6fad87c9508ec)
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             if (WIFEXITED(state))
409                 err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state,
410                                              "Test case did not exit cleanly; "
411                                              "exit status was %d",
412                                              WEXITSTATUS(state));
413             else if (WIFSIGNALED(state))
414                 err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state,
415                                              "Test case did not exit cleanly: "
416                                              "%s%s",
417                                              strsignal(WTERMSIG(state)),
418                                              WCOREDUMP(state) ?
419                                                " (core dumped)" : "");
420             else
421                 err = atf_tcr_init_reason_fmt(tcr, atf_tcr_failed_state,
422                                              "Test case did not exit cleanly; "
423                                              "state was %d", state);
424         } else
425             err = get_tc_result(workdir, tcr);
426     }
427 
428     unprogram_timeout(&td);
429 
430     return err;
431 }
432 
433 static
434 atf_error_t
435 cleanup_parent(const atf_tc_t *tc, pid_t pid)
436 {
437     atf_error_t err;
438     int state;
439 
440     if (waitpid(pid, &state, 0) == -1) {
441         err = atf_libc_error(errno, "Error waiting for child process "
442                              "%d", pid);
443         goto out;
444     }
445 
446     if (!WIFEXITED(state) || WEXITSTATUS(state) != EXIT_SUCCESS)
447         /* XXX Not really a libc error. */
448         err = atf_libc_error(EINVAL, "Child process did not exit cleanly");
449     else
450         err = atf_no_error();
451 
452 out:
453     return err;
454 }
455 
456 static
457 atf_error_t
458 fork_body(const atf_tc_t *tc, const atf_fs_path_t *workdir, atf_tcr_t *tcr)
459 {
460     atf_error_t err;
461     pid_t pid;
462 
463     err = atf_process_fork(&pid);
464     if (atf_is_error(err))
465         goto out;
466 
467     if (pid == 0) {
468         body_child(tc, workdir);
469         UNREACHABLE;
470         abort();
471     } else {
472         err = body_parent(tc, workdir, pid, tcr);
473     }
474 
475 out:
476     return err;
477 }
478 
479 static
480 atf_error_t
481 fork_cleanup(const atf_tc_t *tc, const atf_fs_path_t *workdir)
482 {
483     atf_error_t err;
484     pid_t pid;
485 
486     if (tc->m_cleanup == NULL)
487         err = atf_no_error();
488     else {
489         err = atf_process_fork(&pid);
490         if (atf_is_error(err))
491             goto out;
492 
493         if (pid == 0) {
494             cleanup_child(tc, workdir);
495             UNREACHABLE;
496             abort();
497         } else {
498             err = cleanup_parent(tc, pid);
499         }
500     }
501 
502 out:
503     return err;
504 }
505 
506 static
507 atf_error_t
508 get_tc_result(const atf_fs_path_t *workdir, atf_tcr_t *tcr)
509 {
510     atf_error_t err;
511     int fd;
512     atf_fs_path_t tcrfile;
513 
514     err = atf_fs_path_copy(&tcrfile, workdir);
515     if (atf_is_error(err))
516         goto out;
517 
518     err = atf_fs_path_append_fmt(&tcrfile, "tc-result");
519     if (atf_is_error(err))
520         goto out_tcrfile;
521 
522     fd = open(atf_fs_path_cstring(&tcrfile), O_RDONLY);
523     if (fd == -1) {
524         err = atf_libc_error(errno, "Cannot retrieve test case result");
525         goto out_tcrfile;
526     }
527 
528     err = atf_tcr_deserialize(tcr, fd);
529 
530     close(fd);
531 out_tcrfile:
532     atf_fs_path_fini(&tcrfile);
533 out:
534     return err;
535 }
536 
537 /*
538  * Child-only stuff.
539  */
540 
541 static const atf_tc_t *current_tc = NULL;
542 static const atf_fs_path_t *current_workdir = NULL;
543 static size_t current_tc_fail_count = 0;
544 
545 static
546 atf_error_t
547 prepare_child(const atf_tc_t *tc, const atf_fs_path_t *workdir)
548 {
549     atf_error_t err;
550     int i, ret;
551 
552     current_tc = tc;
553     current_workdir = workdir;
554     current_tc_fail_count = 0;
555 
556     ret = setpgid(getpid(), 0);
557     INV(ret != -1);
558 
559     umask(S_IWGRP | S_IWOTH);
560 
561     for (i = 1; i <= atf_signals_last_signo; i++)
562         atf_signal_reset(i);
563 
564     err = atf_env_set("HOME", atf_fs_path_cstring(workdir));
565     if (atf_is_error(err))
566         goto out;
567 
568     err = atf_env_unset("LANG");
569     if (atf_is_error(err))
570         goto out;
571 
572     err = atf_env_unset("LC_ALL");
573     if (atf_is_error(err))
574         goto out;
575 
576     err = atf_env_unset("LC_COLLATE");
577     if (atf_is_error(err))
578         goto out;
579 
580     err = atf_env_unset("LC_CTYPE");
581     if (atf_is_error(err))
582         goto out;
583 
584     err = atf_env_unset("LC_MESSAGES");
585     if (atf_is_error(err))
586         goto out;
587 
588     err = atf_env_unset("LC_MONETARY");
589     if (atf_is_error(err))
590         goto out;
591 
592     err = atf_env_unset("LC_NUMERIC");
593     if (atf_is_error(err))
594         goto out;
595 
596     err = atf_env_unset("LC_TIME");
597     if (atf_is_error(err))
598         goto out;
599 
600     err = atf_env_unset("TZ");
601     if (atf_is_error(err))
602         goto out;
603 
604     if (chdir(atf_fs_path_cstring(workdir)) == -1) {
605         err = atf_libc_error(errno, "Cannot enter work directory '%s'",
606                              atf_fs_path_cstring(workdir));
607         goto out;
608     }
609 
610     err = atf_no_error();
611 
612 out:
613     return err;
614 }
615 
616 static
617 void
618 body_child(const atf_tc_t *tc, const atf_fs_path_t *workdir)
619 {
620     atf_error_t err;
621 
622     atf_disable_exit_checks();
623 
624     err = prepare_child(tc, workdir);
625     if (atf_is_error(err))
626         goto print_err;
627     err = check_requirements(tc);
628     if (atf_is_error(err))
629         goto print_err;
630     tc->m_body(tc);
631 
632     if (current_tc_fail_count == 0)
633         atf_tc_pass();
634     else
635         atf_tc_fail("%d checks failed; see output for more details",
636                     current_tc_fail_count);
637 
638     UNREACHABLE;
639     abort();
640 
641 print_err:
642     INV(atf_is_error(err));
643     {
644         char buf[4096];
645 
646         atf_error_format(err, buf, sizeof(buf));
647         atf_error_free(err);
648 
649         atf_tc_fail("Error while preparing child process: %s", buf);
650     }
651 
652     UNREACHABLE;
653     abort();
654 }
655 
656 static
657 atf_error_t
658 check_arch(const char *arch, void *data)
659 {
660     bool *found = data;
661 
662     if (strcmp(arch, atf_config_get("atf_arch")) == 0)
663         *found = true;
664 
665     return atf_no_error();
666 }
667 
668 static
669 atf_error_t
670 check_config(const char *var, void *data)
671 {
672     if (!atf_tc_has_config_var(current_tc, var))
673         atf_tc_skip("Required configuration variable %s not defined", var);
674 
675     return atf_no_error();
676 }
677 
678 static
679 atf_error_t
680 check_machine(const char *machine, void *data)
681 {
682     bool *found = data;
683 
684     if (strcmp(machine, atf_config_get("atf_machine")) == 0)
685         *found = true;
686 
687     return atf_no_error();
688 }
689 
690 struct prog_found_pair {
691     const char *prog;
692     bool found;
693 };
694 
695 static
696 atf_error_t
697 check_prog(const char *prog, void *data)
698 {
699     atf_error_t err;
700     atf_fs_path_t p;
701 
702     err = atf_fs_path_init_fmt(&p, "%s", prog);
703     if (atf_is_error(err))
704         goto out;
705 
706     if (atf_fs_path_is_absolute(&p)) {
707         if (atf_is_error(atf_fs_eaccess(&p, atf_fs_access_x)))
708             atf_tc_skip("The required program %s could not be found", prog);
709     } else {
710         const char *path = atf_env_get("PATH");
711         struct prog_found_pair pf;
712         atf_fs_path_t bp;
713 
714         err = atf_fs_path_branch_path(&p, &bp);
715         if (atf_is_error(err))
716             goto out_p;
717 
718         if (strcmp(atf_fs_path_cstring(&bp), ".") != 0)
719             atf_tc_fail("Relative paths are not allowed when searching for "
720                         "a program (%s)", prog);
721 
722         pf.prog = prog;
723         pf.found = false;
724         err = atf_text_for_each_word(path, ":", check_prog_in_dir, &pf);
725         if (atf_is_error(err))
726             goto out_bp;
727 
728         if (!pf.found)
729             atf_tc_skip("The required program %s could not be found in "
730                         "the PATH", prog);
731 
732 out_bp:
733         atf_fs_path_fini(&bp);
734     }
735 
736 out_p:
737     atf_fs_path_fini(&p);
738 out:
739     return err;
740 }
741 
742 static
743 atf_error_t
744 check_prog_in_dir(const char *dir, void *data)
745 {
746     struct prog_found_pair *pf = data;
747     atf_error_t err;
748 
749     if (pf->found)
750         err = atf_no_error();
751     else {
752         atf_fs_path_t p;
753 
754         err = atf_fs_path_init_fmt(&p, "%s/%s", dir, pf->prog);
755         if (atf_is_error(err))
756             goto out_p;
757 
758         if (!atf_is_error(atf_fs_eaccess(&p, atf_fs_access_x)))
759             pf->found = true;
760 
761 out_p:
762         atf_fs_path_fini(&p);
763     }
764 
765     return err;
766 }
767 
768 static
769 atf_error_t
770 check_requirements(const atf_tc_t *tc)
771 {
772     atf_error_t err;
773 
774     err = atf_no_error();
775 
776     if (atf_tc_has_md_var(tc, "require.arch")) {
777         const char *arches = atf_tc_get_md_var(tc, "require.arch");
778         bool found = false;
779 
780         if (strlen(arches) == 0)
781             atf_tc_fail("Invalid value in the require.arch property");
782         else {
783             err = atf_text_for_each_word(arches, " ", check_arch, &found);
784             if (atf_is_error(err))
785                 goto out;
786 
787             if (!found)
788                 atf_tc_skip("Requires one of the '%s' architectures",
789                             arches);
790         }
791     }
792 
793     if (atf_tc_has_md_var(tc, "require.config")) {
794         const char *vars = atf_tc_get_md_var(tc, "require.config");
795 
796         if (strlen(vars) == 0)
797             atf_tc_fail("Invalid value in the require.config property");
798         else {
799             err = atf_text_for_each_word(vars, " ", check_config, NULL);
800             if (atf_is_error(err))
801                 goto out;
802         }
803     }
804 
805     if (atf_tc_has_md_var(tc, "require.machine")) {
806         const char *machines = atf_tc_get_md_var(tc, "require.machine");
807         bool found = false;
808 
809         if (strlen(machines) == 0)
810             atf_tc_fail("Invalid value in the require.machine property");
811         else {
812             err = atf_text_for_each_word(machines, " ", check_machine,
813                                          &found);
814             if (atf_is_error(err))
815                 goto out;
816 
817             if (!found)
818                 atf_tc_skip("Requires one of the '%s' machine types",
819                             machines);
820         }
821     }
822 
823     if (atf_tc_has_md_var(tc, "require.progs")) {
824         const char *progs = atf_tc_get_md_var(tc, "require.progs");
825 
826         if (strlen(progs) == 0)
827             atf_tc_fail("Invalid value in the require.progs property");
828         else {
829             err = atf_text_for_each_word(progs, " ", check_prog, NULL);
830             if (atf_is_error(err))
831                 goto out;
832         }
833     }
834 
835     if (atf_tc_has_md_var(tc, "require.user")) {
836         const char *u = atf_tc_get_md_var(tc, "require.user");
837 
838         if (strcmp(u, "root") == 0) {
839             if (!atf_user_is_root())
840                 atf_tc_skip("Requires root privileges");
841         } else if (strcmp(u, "unprivileged") == 0) {
842             if (atf_user_is_root())
843                 atf_tc_skip("Requires an unprivileged user");
844         } else
845             atf_tc_fail("Invalid value in the require.user property");
846     }
847 
848     INV(!atf_is_error(err));
849 out:
850     return err;
851 }
852 
853 static
854 void
855 cleanup_child(const atf_tc_t *tc, const atf_fs_path_t *workdir)
856 {
857     atf_error_t err;
858 
859     atf_disable_exit_checks();
860 
861     err = prepare_child(tc, workdir);
862     if (atf_is_error(err))
863         exit(EXIT_FAILURE);
864     else {
865         tc->m_cleanup(tc);
866         exit(EXIT_SUCCESS);
867     }
868 
869     UNREACHABLE;
870     abort();
871 }
872 
873 static
874 void
875 fatal_atf_error(const char *prefix, atf_error_t err)
876 {
877     char buf[1024];
878 
879     INV(atf_is_error(err));
880 
881     atf_error_format(err, buf, sizeof(buf));
882     atf_error_free(err);
883 
884     fprintf(stderr, "%s: %s", prefix, buf);
885 
886     abort();
887 }
888 
889 static
890 void
891 fatal_libc_error(const char *prefix, int err)
892 {
893     fprintf(stderr, "%s: %s", prefix, strerror(err));
894 
895     abort();
896 }
897 
898 static
899 void
900 write_tcr(const atf_tcr_t *tcr)
901 {
902     atf_error_t err;
903     int fd;
904     atf_fs_path_t tcrfile;
905 
906     err = atf_fs_path_copy(&tcrfile, current_workdir);
907     if (atf_is_error(err))
908         fatal_atf_error("Cannot write test case results", err);
909 
910     err = atf_fs_path_append_fmt(&tcrfile, "tc-result");
911     if (atf_is_error(err))
912         fatal_atf_error("Cannot write test case results", err);
913 
914     fd = open(atf_fs_path_cstring(&tcrfile),
915               O_WRONLY | O_CREAT | O_TRUNC, 0755);
916     if (fd == -1)
917         fatal_libc_error("Cannot write test case results", errno);
918 
919     err = atf_tcr_serialize(tcr, fd);
920     if (atf_is_error(err))
921         fatal_atf_error("Cannot write test case results", err);
922 
923     close(fd);
924 }
925 
926 void
927 atf_tc_fail(const char *fmt, ...)
928 {
929     va_list ap;
930     atf_tcr_t tcr;
931     atf_error_t err;
932 
933     PRE(current_tc != NULL);
934 
935     va_start(ap, fmt);
936     err = atf_tcr_init_reason_ap(&tcr, atf_tcr_failed_state, fmt, ap);
937     va_end(ap);
938     if (atf_is_error(err))
939         abort();
940 
941     write_tcr(&tcr);
942 
943     atf_tcr_fini(&tcr);
944 
945     exit(EXIT_SUCCESS);
946 }
947 
948 void
949 atf_tc_fail_nonfatal(const char *fmt, ...)
950 {
951     va_list ap;
952 
953     va_start(ap, fmt);
954     vfprintf(stderr, fmt, ap);
955     va_end(ap);
956     fprintf(stderr, "\n");
957 
958     current_tc_fail_count++;
959 }
960 
961 void
962 atf_tc_fail_check(const char *file, int line, const char *fmt, ...)
963 {
964     va_list ap;
965 
966     va_start(ap, fmt);
967     fail_internal(file, line, "Check failed", "*** ", fmt, ap,
968                   atf_tc_fail_nonfatal);
969     va_end(ap);
970 }
971 
972 void
973 atf_tc_fail_requirement(const char *file, int line, const char *fmt, ...)
974 {
975     va_list ap;
976 
977     va_start(ap, fmt);
978     fail_internal(file, line, "Requirement failed", "", fmt, ap,
979                   atf_tc_fail);
980     va_end(ap);
981 
982     UNREACHABLE;
983     abort();
984 }
985 
986 static
987 void
988 fail_internal(const char *file, int line, const char *reason,
989               const char *prefix, const char *fmt, va_list ap,
990               void (*failfunc)(const char *, ...))
991 {
992     va_list ap2;
993     atf_error_t err;
994     atf_dynstr_t msg;
995 
996     err = atf_dynstr_init_fmt(&msg, "%s%s:%d: %s: ", prefix, file, line,
997                               reason);
998     if (atf_is_error(err))
999         goto backup;
1000 
1001     va_copy(ap2, ap);
1002     err = atf_dynstr_append_ap(&msg, fmt, ap2);
1003     va_end(ap2);
1004     if (atf_is_error(err)) {
1005         atf_dynstr_fini(&msg);
1006         goto backup;
1007     }
1008 
1009     va_copy(ap2, ap);
1010     failfunc("%s", atf_dynstr_cstring(&msg));
1011     atf_dynstr_fini(&msg);
1012     return;
1013 
1014 backup:
1015     va_copy(ap2, ap);
1016     failfunc(fmt, ap2);
1017     va_end(ap2);
1018 }
1019 
1020 void
1021 atf_tc_pass(void)
1022 {
1023     atf_tcr_t tcr;
1024     atf_error_t err;
1025 
1026     PRE(current_tc != NULL);
1027 
1028     err = atf_tcr_init(&tcr, atf_tcr_passed_state);
1029     if (atf_is_error(err))
1030         abort();
1031 
1032     write_tcr(&tcr);
1033 
1034     atf_tcr_fini(&tcr);
1035 
1036     exit(EXIT_SUCCESS);
1037 }
1038 
1039 void
1040 atf_tc_require_prog(const char *prog)
1041 {
1042     atf_error_t err;
1043 
1044     err = check_prog(prog, NULL);
1045     if (atf_is_error(err))
1046         atf_tc_fail("atf_tc_require_prog failed"); /* XXX Correct? */
1047 }
1048 
1049 void
1050 atf_tc_skip(const char *fmt, ...)
1051 {
1052     va_list ap;
1053     atf_tcr_t tcr;
1054     atf_error_t err;
1055 
1056     PRE(current_tc != NULL);
1057 
1058     va_start(ap, fmt);
1059     err = atf_tcr_init_reason_ap(&tcr, atf_tcr_skipped_state, fmt, ap);
1060     va_end(ap);
1061     if (atf_is_error(err))
1062         abort();
1063 
1064     write_tcr(&tcr);
1065 
1066     atf_tcr_fini(&tcr);
1067 
1068     exit(EXIT_SUCCESS);
1069 }
1070