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