Home | History | Annotate | Line # | Download | only in atf-c
tc.c revision 1.1.1.1
      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