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 52 static 53 atf_error_t 54 create_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)); 70 out: 71 return err; 72 } 73 74 static 75 void 76 cleanup_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 105 static 106 int 107 const_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 114 static 115 atf_error_t 116 init_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 128 static 129 atf_error_t 130 init_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 145 out: 146 return err; 147 } 148 149 struct exec_data { 150 const char *const *m_argv; 151 }; 152 153 static void exec_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN; 154 155 static 156 void 157 exec_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 166 static 167 atf_error_t 168 fork_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 186 out_sbs: 187 atf_process_stream_fini(&errsb); 188 atf_process_stream_fini(&outsb); 189 out: 190 return err; 191 } 192 193 static 194 void 195 update_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 222 static 223 atf_error_t 224 array_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 246 out: 247 return err; 248 } 249 250 static void 251 print_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 261 static 262 atf_error_t 263 check_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)); 278 out: 279 return err; 280 } 281 282 /* --------------------------------------------------------------------- 283 * The "atf_check_result" type. 284 * --------------------------------------------------------------------- */ 285 286 struct 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 294 static 295 atf_error_t 296 atf_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 326 err_stdout: 327 atf_fs_path_fini(&r->pimpl->m_stdout); 328 err_dir: 329 atf_fs_path_fini(&r->pimpl->m_dir); 330 err_argv: 331 atf_list_fini(&r->pimpl->m_argv); 332 out: 333 return err; 334 } 335 336 void 337 atf_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 352 const char * 353 atf_check_result_stdout(const atf_check_result_t *r) 354 { 355 return atf_fs_path_cstring(&r->pimpl->m_stdout); 356 } 357 358 const char * 359 atf_check_result_stderr(const atf_check_result_t *r) 360 { 361 return atf_fs_path_cstring(&r->pimpl->m_stderr); 362 } 363 364 bool 365 atf_check_result_exited(const atf_check_result_t *r) 366 { 367 return atf_process_status_exited(&r->pimpl->m_status); 368 } 369 370 int 371 atf_check_result_exitcode(const atf_check_result_t *r) 372 { 373 return atf_process_status_exitstatus(&r->pimpl->m_status); 374 } 375 376 bool 377 atf_check_result_signaled(const atf_check_result_t *r) 378 { 379 return atf_process_status_signaled(&r->pimpl->m_status); 380 } 381 382 int 383 atf_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). */ 395 atf_error_t 396 atf_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); 411 out: 412 return err; 413 } 414 415 atf_error_t 416 atf_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); 431 out: 432 return err; 433 } 434 435 atf_error_t 436 atf_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); 451 out: 452 return err; 453 } 454 455 atf_error_t 456 atf_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); 482 out: 483 return err; 484 } 485