11553Srgrimes/* Copyright (c) 2008 The NetBSD Foundation, Inc.
21553Srgrimes * All rights reserved.
31553Srgrimes *
41553Srgrimes * Redistribution and use in source and binary forms, with or without
51553Srgrimes * modification, are permitted provided that the following conditions
61553Srgrimes * are met:
71553Srgrimes * 1. Redistributions of source code must retain the above copyright
81553Srgrimes *    notice, this list of conditions and the following disclaimer.
91553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
101553Srgrimes *    notice, this list of conditions and the following disclaimer in the
111553Srgrimes *    documentation and/or other materials provided with the distribution.
121553Srgrimes *
131553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
141553Srgrimes * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
151553Srgrimes * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
161553Srgrimes * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
171553Srgrimes * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
181553Srgrimes * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
201553Srgrimes * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
211553Srgrimes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
221553Srgrimes * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
231553Srgrimes * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
241553Srgrimes * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  */
251553Srgrimes
261553Srgrimes#include "atf-c/check.h"
271553Srgrimes
281553Srgrimes#include <sys/wait.h>
291553Srgrimes
301553Srgrimes#include <errno.h>
311553Srgrimes#include <fcntl.h>
321553Srgrimes#include <stdint.h>
331553Srgrimes#include <stdio.h>
341553Srgrimes#include <stdlib.h>
3530027Scharnier#include <string.h>
361553Srgrimes#include <unistd.h>
3730027Scharnier
3830027Scharnier#include "atf-c/build.h"
3950479Speter#include "atf-c/defs.h"
401553Srgrimes#include "atf-c/detail/dynstr.h"
411553Srgrimes#include "atf-c/detail/env.h"
421553Srgrimes#include "atf-c/detail/fs.h"
431553Srgrimes#include "atf-c/detail/list.h"
4430027Scharnier#include "atf-c/detail/process.h"
4530027Scharnier#include "atf-c/detail/sanity.h"
4630027Scharnier#include "atf-c/error.h"
471553Srgrimes#include "atf-c/utils.h"
481553Srgrimes
491553Srgrimes/* ---------------------------------------------------------------------
5044303Swollman * Auxiliary functions.
5130027Scharnier * --------------------------------------------------------------------- */
5244303Swollman
5344303Swollmanstatic
5444303Swollmanatf_error_t
5544303Swollmancreate_tmpdir(atf_fs_path_t *dir)
5644303Swollman{
5744303Swollman    atf_error_t err;
5844303Swollman
591553Srgrimes    err = atf_fs_path_init_fmt(dir, "%s/check.XXXXXX",
6030027Scharnier                               atf_env_get_with_default("TMPDIR", "/tmp"));
6130027Scharnier    if (atf_is_error(err))
621553Srgrimes        goto out;
6342561Sjkoshy
641553Srgrimes    err = atf_fs_mkdtemp(dir);
651553Srgrimes    if (atf_is_error(err)) {
661553Srgrimes        atf_fs_path_fini(dir);
671553Srgrimes        goto out;
681553Srgrimes    }
691553Srgrimes
702860Srgrimes    INV(!atf_is_error(err));
712860Srgrimesout:
722860Srgrimes    return err;
7336670Speter}
741553Srgrimes
7530027Scharnierstatic
761553Srgrimesvoid
771553Srgrimescleanup_tmpdir(const atf_fs_path_t *dir, const atf_fs_path_t *outfile,
781553Srgrimes               const atf_fs_path_t *errfile)
791553Srgrimes{
8054375Sjoe    {
811553Srgrimes        atf_error_t err = atf_fs_unlink(outfile);
821553Srgrimes        if (atf_is_error(err)) {
832860Srgrimes            INV(atf_error_is(err, "libc") &&
8454375Sjoe                atf_libc_error_code(err) == ENOENT);
8554375Sjoe            atf_error_free(err);
862860Srgrimes        } else
871553Srgrimes            INV(!atf_is_error(err));
881553Srgrimes    }
891553Srgrimes
901553Srgrimes    {
911553Srgrimes        atf_error_t err = atf_fs_unlink(errfile);
921553Srgrimes        if (atf_is_error(err)) {
931553Srgrimes            INV(atf_error_is(err, "libc") &&
941553Srgrimes                atf_libc_error_code(err) == ENOENT);
952860Srgrimes            atf_error_free(err);
961553Srgrimes        } else
971553Srgrimes            INV(!atf_is_error(err));
981553Srgrimes    }
991553Srgrimes
1001553Srgrimes    {
1011553Srgrimes        atf_error_t err = atf_fs_rmdir(dir);
1021553Srgrimes        INV(!atf_is_error(err));
1031553Srgrimes    }
1041553Srgrimes}
1051553Srgrimes
10630027Scharnierstatic
1072860Srgrimesint
1082860Srgrimesconst_execvp(const char *file, const char *const *argv)
1092860Srgrimes{
11060418Swollman#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
11160418Swollman    return execvp(file, UNCONST(argv));
11260418Swollman#undef UNCONST
11360418Swollman}
1141553Srgrimes
1151553Srgrimesstatic
1162860Srgrimesatf_error_t
1172860Srgrimesinit_sb(const atf_fs_path_t *path, atf_process_stream_t *sb)
1182860Srgrimes{
1192860Srgrimes    atf_error_t err;
12054375Sjoe
1212860Srgrimes    if (path == NULL)
1221553Srgrimes        err = atf_process_stream_init_inherit(sb);
1231553Srgrimes    else
1242860Srgrimes        err = atf_process_stream_init_redirect_path(sb, path);
1252860Srgrimes
1262860Srgrimes    return err;
1272860Srgrimes}
1282860Srgrimes
1291553Srgrimesstatic
1301553Srgrimesatf_error_t
1311553Srgrimesinit_sbs(const atf_fs_path_t *outfile, atf_process_stream_t *outsb,
1321553Srgrimes         const atf_fs_path_t *errfile, atf_process_stream_t *errsb)
13330027Scharnier{
1341553Srgrimes    atf_error_t err;
1351553Srgrimes
1361553Srgrimes    err = init_sb(outfile, outsb);
1372860Srgrimes    if (atf_is_error(err))
1381553Srgrimes        goto out;
1398857Srgrimes
1401553Srgrimes    err = init_sb(errfile, errsb);
1412860Srgrimes    if (atf_is_error(err)) {
1421553Srgrimes        atf_process_stream_fini(outsb);
1431553Srgrimes        goto out;
14430027Scharnier    }
1451553Srgrimes
1461553Srgrimesout:
1471553Srgrimes    return err;
1482860Srgrimes}
1492860Srgrimes
1501553Srgrimesstruct exec_data {
1511553Srgrimes    const char *const *m_argv;
1521553Srgrimes};
1531553Srgrimes
1541553Srgrimesstatic void exec_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
1552860Srgrimes
15661749Sjoestatic
15742561Sjkoshyvoid
1581553Srgrimesexec_child(void *v)
15942561Sjkoshy{
16042561Sjkoshy    struct exec_data *ea = v;
16142561Sjkoshy
16242787Sjkoshy    const_execvp(ea->m_argv[0], ea->m_argv);
16342561Sjkoshy    fprintf(stderr, "execvp(%s) failed: %s\n", ea->m_argv[0], strerror(errno));
1642860Srgrimes    exit(127);
16542561Sjkoshy}
1661553Srgrimes
16742561Sjkoshystatic
16842561Sjkoshyatf_error_t
16942561Sjkoshyfork_and_wait(const char *const *argv, const atf_fs_path_t *outfile,
1701553Srgrimes              const atf_fs_path_t *errfile, atf_process_status_t *status)
1712860Srgrimes{
1722860Srgrimes    atf_error_t err;
1731553Srgrimes    atf_process_child_t child;
1742860Srgrimes    atf_process_stream_t outsb, errsb;
1751553Srgrimes    struct exec_data ea = { argv };
1762860Srgrimes
1772860Srgrimes    err = init_sbs(outfile, &outsb, errfile, &errsb);
1782860Srgrimes    if (atf_is_error(err))
1792860Srgrimes        goto out;
1802860Srgrimes
1812860Srgrimes    err = atf_process_fork(&child, exec_child, &outsb, &errsb, &ea);
1822860Srgrimes    if (atf_is_error(err))
18330027Scharnier        goto out_sbs;
18430027Scharnier
18530027Scharnier    err = atf_process_child_wait(&child, status);
1862860Srgrimes
1872860Srgrimesout_sbs:
1882860Srgrimes    atf_process_stream_fini(&errsb);
1892860Srgrimes    atf_process_stream_fini(&outsb);
1902860Srgrimesout:
1912860Srgrimes    return err;
1922860Srgrimes}
1932860Srgrimes
1942860Srgrimesstatic
1952860Srgrimesvoid
19630027Scharnierupdate_success_from_status(const char *progname,
19730027Scharnier                           const atf_process_status_t *status, bool *success)
19830027Scharnier{
1992860Srgrimes    bool s = atf_process_status_exited(status) &&
2002860Srgrimes             atf_process_status_exitstatus(status) == EXIT_SUCCESS;
2012860Srgrimes
2022860Srgrimes    if (atf_process_status_exited(status)) {
2032860Srgrimes        if (atf_process_status_exitstatus(status) == EXIT_SUCCESS)
2041553Srgrimes            INV(s);
2052860Srgrimes        else {
2061553Srgrimes            INV(!s);
2072860Srgrimes            fprintf(stderr, "%s failed with exit code %d\n", progname,
2081553Srgrimes                    atf_process_status_exitstatus(status));
2092860Srgrimes        }
2101553Srgrimes    } else if (atf_process_status_signaled(status)) {
2112860Srgrimes        INV(!s);
21218404Snate        fprintf(stderr, "%s failed due to signal %d%s\n", progname,
21318404Snate                atf_process_status_termsig(status),
2141553Srgrimes                atf_process_status_coredump(status) ? " (core dumped)" : "");
2151553Srgrimes    } else {
2161553Srgrimes        INV(!s);
21730027Scharnier        fprintf(stderr, "%s failed due to unknown reason\n", progname);
2181553Srgrimes    }
2192860Srgrimes
2201553Srgrimes    *success = s;
22144303Swollman}
2226286Swollman
22344303Swollmanstatic
2246286Swollmanatf_error_t
22544303Swollmanarray_to_list(const char *const *a, atf_list_t *l)
22644303Swollman{
22730027Scharnier    atf_error_t err;
2286286Swollman
22944303Swollman    err = atf_list_init(l);
2306286Swollman    if (atf_is_error(err))
2316286Swollman        goto out;
23244303Swollman
23344303Swollman    while (*a != NULL) {
23444303Swollman        char *item = strdup(*a);
23544303Swollman        if (item == NULL) {
23644303Swollman            err = atf_no_memory_error();
23744303Swollman            goto out;
23844303Swollman        }
23944303Swollman
24044303Swollman        err = atf_list_append(l, item, true);
24144303Swollman        if (atf_is_error(err))
24244303Swollman            goto out;
24344303Swollman
24444303Swollman        a++;
24544303Swollman    }
24644303Swollman
24744303Swollmanout:
24844303Swollman    return err;
24944303Swollman}
25044303Swollman
25144303Swollmanstatic void
25244303Swollmanprint_array(const char *const *array, const char *pfx)
25344303Swollman{
25444303Swollman    const char *const *ptr;
25544303Swollman
25644303Swollman    printf("%s", pfx);
2571553Srgrimes    for (ptr = array; *ptr != NULL; ptr++)
2581553Srgrimes        printf(" %s", *ptr);
2592860Srgrimes    printf("\n");
26061749Sjoe}
26161749Sjoe
26261749Sjoestatic
26361749Sjoeatf_error_t
26461749Sjoecheck_build_run(const char *const *argv, bool *success)
2651553Srgrimes{
2661553Srgrimes    atf_error_t err;
2671553Srgrimes    atf_process_status_t status;
2681553Srgrimes
2691553Srgrimes    print_array(argv, ">");
2701553Srgrimes
27154375Sjoe    err = fork_and_wait(argv, NULL, NULL, &status);
27254375Sjoe    if (atf_is_error(err))
2731553Srgrimes        goto out;
2741553Srgrimes
27554375Sjoe    update_success_from_status(argv[0], &status, success);
2761553Srgrimes    atf_process_status_fini(&status);
2771553Srgrimes
2781553Srgrimes    INV(!atf_is_error(err));
2791553Srgrimesout:
2801553Srgrimes    return err;
28154375Sjoe}
2821553Srgrimes
2831553Srgrimes/* ---------------------------------------------------------------------
2841553Srgrimes * The "atf_check_result" type.
2851553Srgrimes * --------------------------------------------------------------------- */
2861553Srgrimes
28754375Sjoestruct atf_check_result_impl {
2881553Srgrimes    atf_list_t m_argv;
2891553Srgrimes    atf_fs_path_t m_dir;
2902860Srgrimes    atf_fs_path_t m_stdout;
2912860Srgrimes    atf_fs_path_t m_stderr;
2922860Srgrimes    atf_process_status_t m_status;
29354375Sjoe};
29454375Sjoe
29554375Sjoestatic
29661749Sjoeatf_error_t
2972877Srgrimesatf_check_result_init(atf_check_result_t *r, const char *const *argv,
2981553Srgrimes                      const atf_fs_path_t *dir)
2991553Srgrimes{
3001553Srgrimes    atf_error_t err;
30130027Scharnier
3021553Srgrimes    r->pimpl = malloc(sizeof(struct atf_check_result_impl));
3031553Srgrimes    if (r->pimpl == NULL)
3041553Srgrimes        return atf_no_memory_error();
3051553Srgrimes
3061553Srgrimes    err = array_to_list(argv, &r->pimpl->m_argv);
3071553Srgrimes    if (atf_is_error(err))
30854375Sjoe        goto out;
3091553Srgrimes
31054375Sjoe    err = atf_fs_path_copy(&r->pimpl->m_dir, dir);
3111553Srgrimes    if (atf_is_error(err))
3122860Srgrimes        goto err_argv;
3132860Srgrimes
3142860Srgrimes    err = atf_fs_path_init_fmt(&r->pimpl->m_stdout, "%s/stdout",
3152860Srgrimes                               atf_fs_path_cstring(dir));
3162860Srgrimes    if (atf_is_error(err))
3172860Srgrimes        goto err_dir;
3182860Srgrimes
3192860Srgrimes    err = atf_fs_path_init_fmt(&r->pimpl->m_stderr, "%s/stderr",
3202860Srgrimes                               atf_fs_path_cstring(dir));
3212860Srgrimes    if (atf_is_error(err))
3222860Srgrimes        goto err_stdout;
3232860Srgrimes
3242860Srgrimes    INV(!atf_is_error(err));
3252860Srgrimes    goto out;
3262860Srgrimes
3272860Srgrimeserr_stdout:
32854375Sjoe    atf_fs_path_fini(&r->pimpl->m_stdout);
32954375Sjoeerr_dir:
33054375Sjoe    atf_fs_path_fini(&r->pimpl->m_dir);
33154375Sjoeerr_argv:
33254375Sjoe    atf_list_fini(&r->pimpl->m_argv);
33354375Sjoeout:
33454375Sjoe    return err;
33554375Sjoe}
33654375Sjoe
33754375Sjoevoid
33854375Sjoeatf_check_result_fini(atf_check_result_t *r)
33954375Sjoe{
34054375Sjoe    atf_process_status_fini(&r->pimpl->m_status);
34154375Sjoe
3421553Srgrimes    cleanup_tmpdir(&r->pimpl->m_dir, &r->pimpl->m_stdout,
3431553Srgrimes                   &r->pimpl->m_stderr);
3442860Srgrimes    atf_fs_path_fini(&r->pimpl->m_stdout);
3452860Srgrimes    atf_fs_path_fini(&r->pimpl->m_stderr);
3462877Srgrimes    atf_fs_path_fini(&r->pimpl->m_dir);
3472877Srgrimes
3482860Srgrimes    atf_list_fini(&r->pimpl->m_argv);
3492860Srgrimes
3502860Srgrimes    free(r->pimpl);
3512877Srgrimes}
3522877Srgrimes
3532860Srgrimesconst char *
3542860Srgrimesatf_check_result_stdout(const atf_check_result_t *r)
3551553Srgrimes{
3562860Srgrimes    return atf_fs_path_cstring(&r->pimpl->m_stdout);
35751705Sbillf}
3582860Srgrimes
3592860Srgrimesconst char *
3602860Srgrimesatf_check_result_stderr(const atf_check_result_t *r)
36130027Scharnier{
36230027Scharnier    return atf_fs_path_cstring(&r->pimpl->m_stderr);
36330027Scharnier}
36451705Sbillf
3652860Srgrimesbool
36638020Sbdeatf_check_result_exited(const atf_check_result_t *r)
36751705Sbillf{
3682860Srgrimes    return atf_process_status_exited(&r->pimpl->m_status);
3692860Srgrimes}
3702860Srgrimes
37130027Scharnierint
37230027Scharnieratf_check_result_exitcode(const atf_check_result_t *r)
37330027Scharnier{
37451705Sbillf    return atf_process_status_exitstatus(&r->pimpl->m_status);
3752860Srgrimes}
37638020Sbde
3772860Srgrimesbool
3782860Srgrimesatf_check_result_signaled(const atf_check_result_t *r)
3792860Srgrimes{
3802860Srgrimes    return atf_process_status_signaled(&r->pimpl->m_status);
38161749Sjoe}
38261749Sjoe
38361749Sjoeint
38461749Sjoeatf_check_result_termsig(const atf_check_result_t *r)
38561749Sjoe{
3862860Srgrimes    return atf_process_status_termsig(&r->pimpl->m_status);
3872860Srgrimes}
3882860Srgrimes
3892860Srgrimes/* ---------------------------------------------------------------------
39054375Sjoe * Free functions.
3912860Srgrimes * --------------------------------------------------------------------- */
3921553Srgrimes
3931553Srgrimes/* XXX: This function shouldn't be in this module.  It messes with stdout
3941553Srgrimes * and stderr, and it provides a very high-end interface.  This belongs,
3951553Srgrimes * probably, somewhere related to test cases (such as in the tc module). */
3961553Srgrimesatf_error_t
3971553Srgrimesatf_check_build_c_o(const char *sfile,
3981553Srgrimes                    const char *ofile,
3991553Srgrimes                    const char *const optargs[],
4001553Srgrimes                    bool *success)
4011553Srgrimes{
4021553Srgrimes    atf_error_t err;
4031553Srgrimes    char **argv;
4041553Srgrimes
4051553Srgrimes    err = atf_build_c_o(sfile, ofile, optargs, &argv);
4061553Srgrimes    if (atf_is_error(err))
4071553Srgrimes        goto out;
4081553Srgrimes
4091553Srgrimes    err = check_build_run((const char *const *)argv, success);
4101553Srgrimes
4111553Srgrimes    atf_utils_free_charpp(argv);
4121553Srgrimesout:
4131553Srgrimes    return err;
4141553Srgrimes}
4152860Srgrimes
4161553Srgrimesatf_error_t
4172860Srgrimesatf_check_build_cpp(const char *sfile,
4182860Srgrimes                    const char *ofile,
4191553Srgrimes                    const char *const optargs[],
4201553Srgrimes                    bool *success)
4211553Srgrimes{
4221553Srgrimes    atf_error_t err;
4231553Srgrimes    char **argv;
4241553Srgrimes
4251553Srgrimes    err = atf_build_cpp(sfile, ofile, optargs, &argv);
4261553Srgrimes    if (atf_is_error(err))
4271553Srgrimes        goto out;
4281553Srgrimes
4291553Srgrimes    err = check_build_run((const char *const *)argv, success);
4301553Srgrimes
4311553Srgrimes    atf_utils_free_charpp(argv);
4321553Srgrimesout:
4331553Srgrimes    return err;
4341553Srgrimes}
4352860Srgrimes
4362860Srgrimesatf_error_t
4371553Srgrimesatf_check_build_cxx_o(const char *sfile,
4381553Srgrimes                      const char *ofile,
4391553Srgrimes                      const char *const optargs[],
440                      bool *success)
441{
442    atf_error_t err;
443    char **argv;
444
445    err = atf_build_cxx_o(sfile, ofile, optargs, &argv);
446    if (atf_is_error(err))
447        goto out;
448
449    err = check_build_run((const char *const *)argv, success);
450
451    atf_utils_free_charpp(argv);
452out:
453    return err;
454}
455
456atf_error_t
457atf_check_exec_array(const char *const *argv, atf_check_result_t *r)
458{
459    atf_error_t err;
460    atf_fs_path_t dir;
461
462    err = create_tmpdir(&dir);
463    if (atf_is_error(err))
464        goto out;
465
466    err = atf_check_result_init(r, argv, &dir);
467    if (atf_is_error(err)) {
468        atf_error_t err2 = atf_fs_rmdir(&dir);
469        INV(!atf_is_error(err2));
470        goto out;
471    }
472
473    err = fork_and_wait(argv, &r->pimpl->m_stdout, &r->pimpl->m_stderr,
474                        &r->pimpl->m_status);
475    if (atf_is_error(err)) {
476        atf_check_result_fini(r);
477        goto out;
478    }
479
480    INV(!atf_is_error(err));
481
482    atf_fs_path_fini(&dir);
483out:
484    return err;
485}
486