check.c revision 275988
1/* Copyright (c) 2008 The NetBSD Foundation, Inc. 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 14 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 15 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 20 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 22 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 25 26#include "atf-c/check.h" 27 28#include <sys/wait.h> 29 30#include <errno.h> 31#include <fcntl.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <unistd.h> 36 37#include "atf-c/build.h" 38#include "atf-c/defs.h" 39#include "atf-c/detail/dynstr.h" 40#include "atf-c/detail/env.h" 41#include "atf-c/detail/fs.h" 42#include "atf-c/detail/list.h" 43#include "atf-c/detail/process.h" 44#include "atf-c/detail/sanity.h" 45#include "atf-c/error.h" 46#include "atf-c/utils.h" 47 48/* --------------------------------------------------------------------- 49 * Auxiliary functions. 50 * --------------------------------------------------------------------- */ 51 52static 53atf_error_t 54create_tmpdir(atf_fs_path_t *dir) 55{ 56 atf_error_t err; 57 58 err = atf_fs_path_init_fmt(dir, "%s/check.XXXXXX", 59 atf_env_get_with_default("TMPDIR", "/tmp")); 60 if (atf_is_error(err)) 61 goto out; 62 63 err = atf_fs_mkdtemp(dir); 64 if (atf_is_error(err)) { 65 atf_fs_path_fini(dir); 66 goto out; 67 } 68 69 INV(!atf_is_error(err)); 70out: 71 return err; 72} 73 74static 75void 76cleanup_tmpdir(const atf_fs_path_t *dir, const atf_fs_path_t *outfile, 77 const atf_fs_path_t *errfile) 78{ 79 { 80 atf_error_t err = atf_fs_unlink(outfile); 81 if (atf_is_error(err)) { 82 INV(atf_error_is(err, "libc") && 83 atf_libc_error_code(err) == ENOENT); 84 atf_error_free(err); 85 } else 86 INV(!atf_is_error(err)); 87 } 88 89 { 90 atf_error_t err = atf_fs_unlink(errfile); 91 if (atf_is_error(err)) { 92 INV(atf_error_is(err, "libc") && 93 atf_libc_error_code(err) == ENOENT); 94 atf_error_free(err); 95 } else 96 INV(!atf_is_error(err)); 97 } 98 99 { 100 atf_error_t err = atf_fs_rmdir(dir); 101 INV(!atf_is_error(err)); 102 } 103} 104 105static 106int 107const_execvp(const char *file, const char *const *argv) 108{ 109#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) 110 return execvp(file, UNCONST(argv)); 111#undef UNCONST 112} 113 114static 115atf_error_t 116init_sb(const atf_fs_path_t *path, atf_process_stream_t *sb) 117{ 118 atf_error_t err; 119 120 if (path == NULL) 121 err = atf_process_stream_init_inherit(sb); 122 else 123 err = atf_process_stream_init_redirect_path(sb, path); 124 125 return err; 126} 127 128static 129atf_error_t 130init_sbs(const atf_fs_path_t *outfile, atf_process_stream_t *outsb, 131 const atf_fs_path_t *errfile, atf_process_stream_t *errsb) 132{ 133 atf_error_t err; 134 135 err = init_sb(outfile, outsb); 136 if (atf_is_error(err)) 137 goto out; 138 139 err = init_sb(errfile, errsb); 140 if (atf_is_error(err)) { 141 atf_process_stream_fini(outsb); 142 goto out; 143 } 144 145out: 146 return err; 147} 148 149struct exec_data { 150 const char *const *m_argv; 151}; 152 153static void exec_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN; 154 155static 156void 157exec_child(void *v) 158{ 159 struct exec_data *ea = v; 160 161 const_execvp(ea->m_argv[0], ea->m_argv); 162 fprintf(stderr, "execvp(%s) failed: %s\n", ea->m_argv[0], strerror(errno)); 163 exit(127); 164} 165 166static 167atf_error_t 168fork_and_wait(const char *const *argv, const atf_fs_path_t *outfile, 169 const atf_fs_path_t *errfile, atf_process_status_t *status) 170{ 171 atf_error_t err; 172 atf_process_child_t child; 173 atf_process_stream_t outsb, errsb; 174 struct exec_data ea = { argv }; 175 176 err = init_sbs(outfile, &outsb, errfile, &errsb); 177 if (atf_is_error(err)) 178 goto out; 179 180 err = atf_process_fork(&child, exec_child, &outsb, &errsb, &ea); 181 if (atf_is_error(err)) 182 goto out_sbs; 183 184 err = atf_process_child_wait(&child, status); 185 186out_sbs: 187 atf_process_stream_fini(&errsb); 188 atf_process_stream_fini(&outsb); 189out: 190 return err; 191} 192 193static 194void 195update_success_from_status(const char *progname, 196 const atf_process_status_t *status, bool *success) 197{ 198 bool s = atf_process_status_exited(status) && 199 atf_process_status_exitstatus(status) == EXIT_SUCCESS; 200 201 if (atf_process_status_exited(status)) { 202 if (atf_process_status_exitstatus(status) == EXIT_SUCCESS) 203 INV(s); 204 else { 205 INV(!s); 206 fprintf(stderr, "%s failed with exit code %d\n", progname, 207 atf_process_status_exitstatus(status)); 208 } 209 } else if (atf_process_status_signaled(status)) { 210 INV(!s); 211 fprintf(stderr, "%s failed due to signal %d%s\n", progname, 212 atf_process_status_termsig(status), 213 atf_process_status_coredump(status) ? " (core dumped)" : ""); 214 } else { 215 INV(!s); 216 fprintf(stderr, "%s failed due to unknown reason\n", progname); 217 } 218 219 *success = s; 220} 221 222static 223atf_error_t 224array_to_list(const char *const *a, atf_list_t *l) 225{ 226 atf_error_t err; 227 228 err = atf_list_init(l); 229 if (atf_is_error(err)) 230 goto out; 231 232 while (*a != NULL) { 233 char *item = strdup(*a); 234 if (item == NULL) { 235 err = atf_no_memory_error(); 236 goto out; 237 } 238 239 err = atf_list_append(l, item, true); 240 if (atf_is_error(err)) 241 goto out; 242 243 a++; 244 } 245 246out: 247 return err; 248} 249 250static void 251print_array(const char *const *array, const char *pfx) 252{ 253 const char *const *ptr; 254 255 printf("%s", pfx); 256 for (ptr = array; *ptr != NULL; ptr++) 257 printf(" %s", *ptr); 258 printf("\n"); 259} 260 261static 262atf_error_t 263check_build_run(const char *const *argv, bool *success) 264{ 265 atf_error_t err; 266 atf_process_status_t status; 267 268 print_array(argv, ">"); 269 270 err = fork_and_wait(argv, NULL, NULL, &status); 271 if (atf_is_error(err)) 272 goto out; 273 274 update_success_from_status(argv[0], &status, success); 275 atf_process_status_fini(&status); 276 277 INV(!atf_is_error(err)); 278out: 279 return err; 280} 281 282/* --------------------------------------------------------------------- 283 * The "atf_check_result" type. 284 * --------------------------------------------------------------------- */ 285 286struct atf_check_result_impl { 287 atf_list_t m_argv; 288 atf_fs_path_t m_dir; 289 atf_fs_path_t m_stdout; 290 atf_fs_path_t m_stderr; 291 atf_process_status_t m_status; 292}; 293 294static 295atf_error_t 296atf_check_result_init(atf_check_result_t *r, const char *const *argv, 297 const atf_fs_path_t *dir) 298{ 299 atf_error_t err; 300 301 r->pimpl = malloc(sizeof(struct atf_check_result_impl)); 302 if (r->pimpl == NULL) 303 return atf_no_memory_error(); 304 305 err = array_to_list(argv, &r->pimpl->m_argv); 306 if (atf_is_error(err)) 307 goto out; 308 309 err = atf_fs_path_copy(&r->pimpl->m_dir, dir); 310 if (atf_is_error(err)) 311 goto err_argv; 312 313 err = atf_fs_path_init_fmt(&r->pimpl->m_stdout, "%s/stdout", 314 atf_fs_path_cstring(dir)); 315 if (atf_is_error(err)) 316 goto err_dir; 317 318 err = atf_fs_path_init_fmt(&r->pimpl->m_stderr, "%s/stderr", 319 atf_fs_path_cstring(dir)); 320 if (atf_is_error(err)) 321 goto err_stdout; 322 323 INV(!atf_is_error(err)); 324 goto out; 325 326err_stdout: 327 atf_fs_path_fini(&r->pimpl->m_stdout); 328err_dir: 329 atf_fs_path_fini(&r->pimpl->m_dir); 330err_argv: 331 atf_list_fini(&r->pimpl->m_argv); 332out: 333 return err; 334} 335 336void 337atf_check_result_fini(atf_check_result_t *r) 338{ 339 atf_process_status_fini(&r->pimpl->m_status); 340 341 cleanup_tmpdir(&r->pimpl->m_dir, &r->pimpl->m_stdout, 342 &r->pimpl->m_stderr); 343 atf_fs_path_fini(&r->pimpl->m_stdout); 344 atf_fs_path_fini(&r->pimpl->m_stderr); 345 atf_fs_path_fini(&r->pimpl->m_dir); 346 347 atf_list_fini(&r->pimpl->m_argv); 348 349 free(r->pimpl); 350} 351 352const char * 353atf_check_result_stdout(const atf_check_result_t *r) 354{ 355 return atf_fs_path_cstring(&r->pimpl->m_stdout); 356} 357 358const char * 359atf_check_result_stderr(const atf_check_result_t *r) 360{ 361 return atf_fs_path_cstring(&r->pimpl->m_stderr); 362} 363 364bool 365atf_check_result_exited(const atf_check_result_t *r) 366{ 367 return atf_process_status_exited(&r->pimpl->m_status); 368} 369 370int 371atf_check_result_exitcode(const atf_check_result_t *r) 372{ 373 return atf_process_status_exitstatus(&r->pimpl->m_status); 374} 375 376bool 377atf_check_result_signaled(const atf_check_result_t *r) 378{ 379 return atf_process_status_signaled(&r->pimpl->m_status); 380} 381 382int 383atf_check_result_termsig(const atf_check_result_t *r) 384{ 385 return atf_process_status_termsig(&r->pimpl->m_status); 386} 387 388/* --------------------------------------------------------------------- 389 * Free functions. 390 * --------------------------------------------------------------------- */ 391 392/* XXX: This function shouldn't be in this module. It messes with stdout 393 * and stderr, and it provides a very high-end interface. This belongs, 394 * probably, somewhere related to test cases (such as in the tc module). */ 395atf_error_t 396atf_check_build_c_o(const char *sfile, 397 const char *ofile, 398 const char *const optargs[], 399 bool *success) 400{ 401 atf_error_t err; 402 char **argv; 403 404 err = atf_build_c_o(sfile, ofile, optargs, &argv); 405 if (atf_is_error(err)) 406 goto out; 407 408 err = check_build_run((const char *const *)argv, success); 409 410 atf_utils_free_charpp(argv); 411out: 412 return err; 413} 414 415atf_error_t 416atf_check_build_cpp(const char *sfile, 417 const char *ofile, 418 const char *const optargs[], 419 bool *success) 420{ 421 atf_error_t err; 422 char **argv; 423 424 err = atf_build_cpp(sfile, ofile, optargs, &argv); 425 if (atf_is_error(err)) 426 goto out; 427 428 err = check_build_run((const char *const *)argv, success); 429 430 atf_utils_free_charpp(argv); 431out: 432 return err; 433} 434 435atf_error_t 436atf_check_build_cxx_o(const char *sfile, 437 const char *ofile, 438 const char *const optargs[], 439 bool *success) 440{ 441 atf_error_t err; 442 char **argv; 443 444 err = atf_build_cxx_o(sfile, ofile, optargs, &argv); 445 if (atf_is_error(err)) 446 goto out; 447 448 err = check_build_run((const char *const *)argv, success); 449 450 atf_utils_free_charpp(argv); 451out: 452 return err; 453} 454 455atf_error_t 456atf_check_exec_array(const char *const *argv, atf_check_result_t *r) 457{ 458 atf_error_t err; 459 atf_fs_path_t dir; 460 461 err = create_tmpdir(&dir); 462 if (atf_is_error(err)) 463 goto out; 464 465 err = atf_check_result_init(r, argv, &dir); 466 if (atf_is_error(err)) { 467 atf_error_t err2 = atf_fs_rmdir(&dir); 468 INV(!atf_is_error(err2)); 469 goto out; 470 } 471 472 err = fork_and_wait(argv, &r->pimpl->m_stdout, &r->pimpl->m_stderr, 473 &r->pimpl->m_status); 474 if (atf_is_error(err)) { 475 atf_check_result_fini(r); 476 goto out; 477 } 478 479 INV(!atf_is_error(err)); 480 481 atf_fs_path_fini(&dir); 482out: 483 return err; 484} 485