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 extern "C" { 27 #include <sys/types.h> 28 #include <sys/wait.h> 29 30 #include <limits.h> 31 #include <signal.h> 32 #include <stdint.h> 33 #include <unistd.h> 34 } 35 36 #include <cerrno> 37 #include <cstdlib> 38 #include <cstring> 39 #include <fstream> 40 #include <ios> 41 #include <iostream> 42 #include <iterator> 43 #include <list> 44 #include <memory> 45 #include <utility> 46 47 #include "atf-c++/check.hpp" 48 #include "atf-c++/detail/application.hpp" 49 #include "atf-c++/detail/auto_array.hpp" 50 #include "atf-c++/detail/env.hpp" 51 #include "atf-c++/detail/exceptions.hpp" 52 #include "atf-c++/detail/fs.hpp" 53 #include "atf-c++/detail/process.hpp" 54 #include "atf-c++/detail/sanity.hpp" 55 #include "atf-c++/detail/text.hpp" 56 57 static const useconds_t seconds_in_useconds = (1000 * 1000); 58 static const useconds_t mseconds_in_useconds = 1000; 59 static const useconds_t useconds_in_nseconds = 1000; 60 61 // ------------------------------------------------------------------------ 62 // Auxiliary functions. 63 // ------------------------------------------------------------------------ 64 65 namespace { 66 67 enum status_check_t { 68 sc_exit, 69 sc_ignore, 70 sc_signal, 71 }; 72 73 struct status_check { 74 status_check_t type; 75 bool negated; 76 int value; 77 78 status_check(const status_check_t& p_type, const bool p_negated, 79 const int p_value) : 80 type(p_type), 81 negated(p_negated), 82 value(p_value) 83 { 84 } 85 }; 86 87 enum output_check_t { 88 oc_ignore, 89 oc_inline, 90 oc_file, 91 oc_empty, 92 oc_match, 93 oc_save 94 }; 95 96 struct output_check { 97 output_check_t type; 98 bool negated; 99 std::string value; 100 101 output_check(const output_check_t& p_type, const bool p_negated, 102 const std::string& p_value) : 103 type(p_type), 104 negated(p_negated), 105 value(p_value) 106 { 107 } 108 }; 109 110 class temp_file : public std::ostream { 111 std::auto_ptr< atf::fs::path > m_path; 112 int m_fd; 113 114 public: 115 temp_file(const char* pattern) : 116 std::ostream(NULL), 117 m_fd(-1) 118 { 119 const atf::fs::path file = atf::fs::path( 120 atf::env::get("TMPDIR", "/tmp")) / pattern; 121 122 atf::auto_array< char > buf(new char[file.str().length() + 1]); 123 std::strcpy(buf.get(), file.c_str()); 124 125 m_fd = ::mkstemp(buf.get()); 126 if (m_fd == -1) 127 throw atf::system_error("atf_check::temp_file::temp_file(" + 128 file.str() + ")", "mkstemp(3) failed", 129 errno); 130 131 m_path.reset(new atf::fs::path(buf.get())); 132 } 133 134 ~temp_file(void) 135 { 136 close(); 137 try { 138 remove(*m_path); 139 } catch (const atf::system_error&) { 140 // Ignore deletion errors. 141 } 142 } 143 144 const atf::fs::path& 145 get_path(void) const 146 { 147 return *m_path; 148 } 149 150 void 151 write(const std::string& text) 152 { 153 if (::write(m_fd, text.c_str(), text.size()) == -1) 154 throw atf::system_error("atf_check", "write(2) failed", errno); 155 } 156 157 void 158 close(void) 159 { 160 if (m_fd != -1) { 161 flush(); 162 ::close(m_fd); 163 m_fd = -1; 164 } 165 } 166 }; 167 168 } // anonymous namespace 169 170 static useconds_t 171 get_monotonic_useconds(void) 172 { 173 struct timespec ts; 174 useconds_t res; 175 int rc; 176 177 rc = clock_gettime(CLOCK_MONOTONIC, &ts); 178 if (rc != 0) 179 throw std::runtime_error("clock_gettime: " + 180 std::string(strerror(errno))); 181 182 res = ts.tv_sec * seconds_in_useconds; 183 res += ts.tv_nsec / useconds_in_nseconds; 184 return res; 185 } 186 187 static bool 188 timo_expired(useconds_t timeout) 189 { 190 191 if (get_monotonic_useconds() >= timeout) 192 return true; 193 return false; 194 } 195 196 197 static int 198 parse_exit_code(const std::string& str) 199 { 200 try { 201 const int value = atf::text::to_type< int >(str); 202 if (value < 0 || value > 255) 203 throw std::runtime_error("Unused reason"); 204 return value; 205 } catch (const std::runtime_error&) { 206 throw atf::application::usage_error("Invalid exit code for -s option; " 207 "must be an integer in range 0-255"); 208 } 209 } 210 211 static struct name_number { 212 const char *name; 213 int signo; 214 } signal_names_to_numbers[] = { 215 { "hup", SIGHUP }, 216 { "int", SIGINT }, 217 { "quit", SIGQUIT }, 218 { "trap", SIGTRAP }, 219 { "abrt", SIGABRT }, 220 { "kill", SIGKILL }, 221 { "segv", SIGSEGV }, 222 { "pipe", SIGPIPE }, 223 { "alrm", SIGALRM }, 224 { "term", SIGTERM }, 225 { "usr1", SIGUSR1 }, 226 { "usr2", SIGUSR2 }, 227 { NULL, INT_MIN }, 228 }; 229 230 static int 231 signal_name_to_number(const std::string& str) 232 { 233 struct name_number* iter = signal_names_to_numbers; 234 int signo = INT_MIN; 235 while (signo == INT_MIN && iter->name != NULL) { 236 if (str == iter->name || str == std::string("sig") + iter->name) 237 signo = iter->signo; 238 else 239 iter++; 240 } 241 return signo; 242 } 243 244 static int 245 parse_signal(const std::string& str) 246 { 247 const int signo = signal_name_to_number(str); 248 if (signo == INT_MIN) { 249 try { 250 return atf::text::to_type< int >(str); 251 } catch (const std::runtime_error&) { 252 throw atf::application::usage_error("Invalid signal name or number " 253 "in -s option"); 254 } 255 } 256 INV(signo != INT_MIN); 257 return signo; 258 } 259 260 static status_check 261 parse_status_check_arg(const std::string& arg) 262 { 263 const std::string::size_type delimiter = arg.find(':'); 264 bool negated = (arg.compare(0, 4, "not-") == 0); 265 const std::string action_str = arg.substr(0, delimiter); 266 const std::string action = negated ? action_str.substr(4) : action_str; 267 const std::string value_str = ( 268 delimiter == std::string::npos ? "" : arg.substr(delimiter + 1)); 269 int value; 270 271 status_check_t type; 272 if (action == "eq") { 273 // Deprecated; use exit instead. TODO: Remove after 0.10. 274 type = sc_exit; 275 if (negated) 276 throw atf::application::usage_error("Cannot negate eq checker"); 277 negated = false; 278 value = parse_exit_code(value_str); 279 } else if (action == "exit") { 280 type = sc_exit; 281 if (value_str.empty()) 282 value = INT_MIN; 283 else 284 value = parse_exit_code(value_str); 285 } else if (action == "ignore") { 286 if (negated) 287 throw atf::application::usage_error("Cannot negate ignore checker"); 288 type = sc_ignore; 289 value = INT_MIN; 290 } else if (action == "ne") { 291 // Deprecated; use not-exit instead. TODO: Remove after 0.10. 292 type = sc_exit; 293 if (negated) 294 throw atf::application::usage_error("Cannot negate ne checker"); 295 negated = true; 296 value = parse_exit_code(value_str); 297 } else if (action == "signal") { 298 type = sc_signal; 299 if (value_str.empty()) 300 value = INT_MIN; 301 else 302 value = parse_signal(value_str); 303 } else 304 throw atf::application::usage_error("Invalid status checker"); 305 306 return status_check(type, negated, value); 307 } 308 309 static 310 output_check 311 parse_output_check_arg(const std::string& arg) 312 { 313 const std::string::size_type delimiter = arg.find(':'); 314 const bool negated = (arg.compare(0, 4, "not-") == 0); 315 const std::string action_str = arg.substr(0, delimiter); 316 const std::string action = negated ? action_str.substr(4) : action_str; 317 318 output_check_t type; 319 if (action == "empty") 320 type = oc_empty; 321 else if (action == "file") 322 type = oc_file; 323 else if (action == "ignore") { 324 if (negated) 325 throw atf::application::usage_error("Cannot negate ignore checker"); 326 type = oc_ignore; 327 } else if (action == "inline") 328 type = oc_inline; 329 else if (action == "match") 330 type = oc_match; 331 else if (action == "save") { 332 if (negated) 333 throw atf::application::usage_error("Cannot negate save checker"); 334 type = oc_save; 335 } else 336 throw atf::application::usage_error("Invalid output checker"); 337 338 return output_check(type, negated, arg.substr(delimiter + 1)); 339 } 340 341 static void 342 parse_repeat_check_arg(const std::string& arg, useconds_t *m_timo, 343 useconds_t *m_interval) 344 { 345 const std::string::size_type delimiter = arg.find(':'); 346 const bool has_interval = (delimiter != std::string::npos); 347 const std::string timo_str = arg.substr(0, delimiter); 348 349 long l; 350 char *end; 351 352 // There is no reason this couldn't be a non-integer number of seconds, 353 // this was just easy to do for now. 354 errno = 0; 355 l = strtol(timo_str.c_str(), &end, 10); 356 if (errno == ERANGE) 357 throw atf::application::usage_error("Bogus timeout in seconds"); 358 else if (errno != 0) 359 throw atf::application::usage_error("Timeout must be a number"); 360 361 if (*end != 0) 362 throw atf::application::usage_error("Timeout must be a number"); 363 364 *m_timo = get_monotonic_useconds() + (l * seconds_in_useconds); 365 // 50 milliseconds is chosen arbitrarily. There is a tradeoff between 366 // longer and shorter poll times. A shorter poll time makes for faster 367 // tests. A longer poll time makes for lower CPU overhead for the polled 368 // operation. 50ms is chosen with these tradeoffs in mind: on 369 // microcontrollers, the hope is that we can still avoid meaningful CPU use 370 // with a small test every 50ms. And on typical fast x86 hardware, our 371 // tests can be much more precise with time wasted than they typically are 372 // without this feature. 373 *m_interval = 50 * mseconds_in_useconds; 374 375 if (!has_interval) 376 return; 377 378 const std::string intv_str = arg.substr(delimiter + 1, std::string::npos); 379 380 // Same -- this could be non-integer milliseconds. 381 errno = 0; 382 l = strtol(intv_str.c_str(), &end, 10); 383 if (errno == ERANGE) 384 throw atf::application::usage_error( 385 "Bogus repeat interval in milliseconds"); 386 else if (errno != 0) 387 throw atf::application::usage_error( 388 "Repeat interval must be a number"); 389 390 if (*end != 0) 391 throw atf::application::usage_error( 392 "Repeat interval must be a number"); 393 394 *m_interval = l * mseconds_in_useconds; 395 } 396 397 static 398 std::string 399 flatten_argv(char* const* argv) 400 { 401 std::string cmdline; 402 403 char* const* arg = &argv[0]; 404 while (*arg != NULL) { 405 if (arg != &argv[0]) 406 cmdline += ' '; 407 408 cmdline += *arg; 409 410 arg++; 411 } 412 413 return cmdline; 414 } 415 416 static 417 std::auto_ptr< atf::check::check_result > 418 execute(const char* const* argv) 419 { 420 // TODO: This should go to stderr... but fixing it now may be hard as test 421 // cases out there might be relying on stderr being silent. 422 std::cout << "Executing command [ "; 423 for (int i = 0; argv[i] != NULL; ++i) 424 std::cout << argv[i] << " "; 425 std::cout << "]\n"; 426 std::cout.flush(); 427 428 atf::process::argv_array argva(argv); 429 return atf::check::exec(argva); 430 } 431 432 static 433 std::auto_ptr< atf::check::check_result > 434 execute_with_shell(char* const* argv) 435 { 436 const std::string cmd = flatten_argv(argv); 437 const std::string shell = atf::env::get("ATF_SHELL", ATF_SHELL); 438 439 const char* sh_argv[4]; 440 sh_argv[0] = shell.c_str(); 441 sh_argv[1] = "-c"; 442 sh_argv[2] = cmd.c_str(); 443 sh_argv[3] = NULL; 444 return execute(sh_argv); 445 } 446 447 static 448 void 449 cat_file(const atf::fs::path& path) 450 { 451 std::ifstream stream(path.c_str()); 452 if (!stream) 453 throw std::runtime_error("Failed to open " + path.str()); 454 455 stream >> std::noskipws; 456 std::istream_iterator< char > begin(stream), end; 457 std::ostream_iterator< char > out(std::cerr); 458 std::copy(begin, end, out); 459 460 stream.close(); 461 } 462 463 static 464 bool 465 grep_file(const atf::fs::path& path, const std::string& regexp) 466 { 467 std::ifstream stream(path.c_str()); 468 if (!stream) 469 throw std::runtime_error("Failed to open " + path.str()); 470 471 bool found = false; 472 473 std::string line; 474 while (!found && !std::getline(stream, line).fail()) { 475 if (atf::text::match(line, regexp)) 476 found = true; 477 } 478 479 stream.close(); 480 481 return found; 482 } 483 484 static 485 bool 486 file_empty(const atf::fs::path& p) 487 { 488 atf::fs::file_info f(p); 489 490 return (f.get_size() == 0); 491 } 492 493 static bool 494 compare_files(const atf::fs::path& p1, const atf::fs::path& p2) 495 { 496 bool equal = false; 497 498 std::ifstream f1(p1.c_str()); 499 if (!f1) 500 throw std::runtime_error("Failed to open " + p1.str()); 501 502 std::ifstream f2(p2.c_str()); 503 if (!f2) 504 throw std::runtime_error("Failed to open " + p2.str()); 505 506 for (;;) { 507 char buf1[512], buf2[512]; 508 509 f1.read(buf1, sizeof(buf1)); 510 if (f1.bad()) 511 throw std::runtime_error("Failed to read from " + p1.str()); 512 513 f2.read(buf2, sizeof(buf2)); 514 if (f2.bad()) 515 throw std::runtime_error("Failed to read from " + p2.str()); 516 517 if ((f1.gcount() == 0) && (f2.gcount() == 0)) { 518 equal = true; 519 break; 520 } 521 522 if ((f1.gcount() != f2.gcount()) || 523 (std::memcmp(buf1, buf2, f1.gcount()) != 0)) { 524 break; 525 } 526 } 527 528 return equal; 529 } 530 531 static 532 void 533 print_diff(const atf::fs::path& p1, const atf::fs::path& p2) 534 { 535 const atf::process::status s = 536 atf::process::exec(atf::fs::path("diff"), 537 atf::process::argv_array("diff", "-u", p1.c_str(), 538 p2.c_str(), NULL), 539 atf::process::stream_connect(STDOUT_FILENO, 540 STDERR_FILENO), 541 atf::process::stream_inherit()); 542 543 if (!s.exited()) 544 std::cerr << "Failed to run diff(3)\n"; 545 546 if (s.exitstatus() != 1) 547 std::cerr << "Error while running diff(3)\n"; 548 } 549 550 static 551 std::string 552 decode(const std::string& s) 553 { 554 size_t i; 555 std::string res; 556 557 res.reserve(s.length()); 558 559 i = 0; 560 while (i < s.length()) { 561 char c = s[i++]; 562 563 if (c == '\\') { 564 switch (s[i++]) { 565 case 'a': c = '\a'; break; 566 case 'b': c = '\b'; break; 567 case 'c': break; 568 case 'e': c = 033; break; 569 case 'f': c = '\f'; break; 570 case 'n': c = '\n'; break; 571 case 'r': c = '\r'; break; 572 case 't': c = '\t'; break; 573 case 'v': c = '\v'; break; 574 case '\\': break; 575 case '0': 576 { 577 int count = 3; 578 c = 0; 579 while (--count >= 0 && (unsigned)(s[i] - '0') < 8) 580 c = (c << 3) + (s[i++] - '0'); 581 break; 582 } 583 default: 584 --i; 585 break; 586 } 587 } 588 589 res.push_back(c); 590 } 591 592 return res; 593 } 594 595 static 596 bool 597 run_status_check(const status_check& sc, const atf::check::check_result& cr) 598 { 599 bool result; 600 601 if (sc.type == sc_exit) { 602 if (cr.exited() && sc.value != INT_MIN) { 603 const int status = cr.exitcode(); 604 605 if (!sc.negated && sc.value != status) { 606 std::cerr << "Fail: incorrect exit status: " 607 << status << ", expected: " 608 << sc.value << "\n"; 609 result = false; 610 } else if (sc.negated && sc.value == status) { 611 std::cerr << "Fail: incorrect exit status: " 612 << status << ", expected: " 613 << "anything else\n"; 614 result = false; 615 } else 616 result = true; 617 } else if (cr.exited() && sc.value == INT_MIN) { 618 result = true; 619 } else { 620 std::cerr << "Fail: program did not exit cleanly\n"; 621 result = false; 622 } 623 } else if (sc.type == sc_ignore) { 624 result = true; 625 } else if (sc.type == sc_signal) { 626 if (cr.signaled() && sc.value != INT_MIN) { 627 const int status = cr.termsig(); 628 629 if (!sc.negated && sc.value != status) { 630 std::cerr << "Fail: incorrect signal received: " 631 << status << ", expected: " << sc.value << "\n"; 632 result = false; 633 } else if (sc.negated && sc.value == status) { 634 std::cerr << "Fail: incorrect signal received: " 635 << status << ", expected: " 636 << "anything else\n"; 637 result = false; 638 } else 639 result = true; 640 } else if (cr.signaled() && sc.value == INT_MIN) { 641 result = true; 642 } else { 643 std::cerr << "Fail: program did not receive a signal\n"; 644 result = false; 645 } 646 } else { 647 UNREACHABLE; 648 result = false; 649 } 650 651 if (result == false) { 652 std::cerr << "stdout:\n"; 653 cat_file(atf::fs::path(cr.stdout_path())); 654 std::cerr << "\n"; 655 656 std::cerr << "stderr:\n"; 657 cat_file(atf::fs::path(cr.stderr_path())); 658 std::cerr << "\n"; 659 } 660 661 return result; 662 } 663 664 static 665 bool 666 run_status_checks(const std::vector< status_check >& checks, 667 const atf::check::check_result& result) 668 { 669 bool ok = false; 670 671 for (std::vector< status_check >::const_iterator iter = checks.begin(); 672 !ok && iter != checks.end(); iter++) { 673 ok |= run_status_check(*iter, result); 674 } 675 676 return ok; 677 } 678 679 static 680 bool 681 run_output_check(const output_check oc, const atf::fs::path& path, 682 const std::string& stdxxx) 683 { 684 bool result; 685 686 if (oc.type == oc_empty) { 687 const bool is_empty = file_empty(path); 688 if (!oc.negated && !is_empty) { 689 std::cerr << "Fail: " << stdxxx << " not empty\n"; 690 print_diff(atf::fs::path("/dev/null"), path); 691 result = false; 692 } else if (oc.negated && is_empty) { 693 std::cerr << "Fail: " << stdxxx << " is empty\n"; 694 result = false; 695 } else 696 result = true; 697 } else if (oc.type == oc_file) { 698 const bool equals = compare_files(path, atf::fs::path(oc.value)); 699 if (!oc.negated && !equals) { 700 std::cerr << "Fail: " << stdxxx << " does not match golden " 701 "output\n"; 702 print_diff(atf::fs::path(oc.value), path); 703 result = false; 704 } else if (oc.negated && equals) { 705 std::cerr << "Fail: " << stdxxx << " matches golden output\n"; 706 cat_file(atf::fs::path(oc.value)); 707 result = false; 708 } else 709 result = true; 710 } else if (oc.type == oc_ignore) { 711 result = true; 712 } else if (oc.type == oc_inline) { 713 temp_file temp("atf-check.XXXXXX"); 714 temp.write(decode(oc.value)); 715 temp.close(); 716 717 const bool equals = compare_files(path, temp.get_path()); 718 if (!oc.negated && !equals) { 719 std::cerr << "Fail: " << stdxxx << " does not match expected " 720 "value\n"; 721 print_diff(temp.get_path(), path); 722 result = false; 723 } else if (oc.negated && equals) { 724 std::cerr << "Fail: " << stdxxx << " matches expected value\n"; 725 cat_file(temp.get_path()); 726 result = false; 727 } else 728 result = true; 729 } else if (oc.type == oc_match) { 730 const bool matches = grep_file(path, oc.value); 731 if (!oc.negated && !matches) { 732 std::cerr << "Fail: regexp " + oc.value + " not in " << stdxxx 733 << "\n"; 734 cat_file(path); 735 result = false; 736 } else if (oc.negated && matches) { 737 std::cerr << "Fail: regexp " + oc.value + " is in " << stdxxx 738 << "\n"; 739 cat_file(path); 740 result = false; 741 } else 742 result = true; 743 } else if (oc.type == oc_save) { 744 INV(!oc.negated); 745 std::ifstream ifs(path.c_str(), std::fstream::binary); 746 ifs >> std::noskipws; 747 std::istream_iterator< char > begin(ifs), end; 748 749 std::ofstream ofs(oc.value.c_str(), std::fstream::binary 750 | std::fstream::trunc); 751 std::ostream_iterator <char> obegin(ofs); 752 753 std::copy(begin, end, obegin); 754 result = true; 755 } else { 756 UNREACHABLE; 757 result = false; 758 } 759 760 return result; 761 } 762 763 static 764 bool 765 run_output_checks(const std::vector< output_check >& checks, 766 const atf::fs::path& path, const std::string& stdxxx) 767 { 768 bool ok = true; 769 770 for (std::vector< output_check >::const_iterator iter = checks.begin(); 771 iter != checks.end(); iter++) { 772 ok &= run_output_check(*iter, path, stdxxx); 773 } 774 775 return ok; 776 } 777 778 // ------------------------------------------------------------------------ 779 // The "atf_check" application. 780 // ------------------------------------------------------------------------ 781 782 namespace { 783 784 class atf_check : public atf::application::app { 785 bool m_rflag; 786 bool m_xflag; 787 788 useconds_t m_timo; 789 useconds_t m_interval; 790 791 std::vector< status_check > m_status_checks; 792 std::vector< output_check > m_stdout_checks; 793 std::vector< output_check > m_stderr_checks; 794 795 static const char* m_description; 796 797 bool run_output_checks(const atf::check::check_result&, 798 const std::string&) const; 799 800 std::string specific_args(void) const; 801 options_set specific_options(void) const; 802 void process_option(int, const char*); 803 void process_option_s(const std::string&); 804 805 public: 806 atf_check(void); 807 int main(void); 808 }; 809 810 } // anonymous namespace 811 812 const char* atf_check::m_description = 813 "atf-check executes given command and analyzes its results."; 814 815 atf_check::atf_check(void) : 816 app(m_description, "atf-check(1)"), 817 m_rflag(false), 818 m_xflag(false) 819 { 820 } 821 822 bool 823 atf_check::run_output_checks(const atf::check::check_result& r, 824 const std::string& stdxxx) 825 const 826 { 827 if (stdxxx == "stdout") { 828 return ::run_output_checks(m_stdout_checks, 829 atf::fs::path(r.stdout_path()), "stdout"); 830 } else if (stdxxx == "stderr") { 831 return ::run_output_checks(m_stderr_checks, 832 atf::fs::path(r.stderr_path()), "stderr"); 833 } else { 834 UNREACHABLE; 835 return false; 836 } 837 } 838 839 std::string 840 atf_check::specific_args(void) 841 const 842 { 843 return "<command>"; 844 } 845 846 atf_check::options_set 847 atf_check::specific_options(void) 848 const 849 { 850 using atf::application::option; 851 options_set opts; 852 853 opts.insert(option('s', "qual:value", "Handle status. Qualifier " 854 "must be one of: ignore exit:<num> signal:<name|num>")); 855 opts.insert(option('o', "action:arg", "Handle stdout. Action must be " 856 "one of: empty ignore file:<path> inline:<val> match:regexp " 857 "save:<path>")); 858 opts.insert(option('e', "action:arg", "Handle stderr. Action must be " 859 "one of: empty ignore file:<path> inline:<val> match:regexp " 860 "save:<path>")); 861 opts.insert(option('r', "timeout[:interval]", "Repeat failed check until " 862 "the timeout expires.")); 863 opts.insert(option('x', "", "Execute command as a shell command")); 864 865 return opts; 866 } 867 868 void 869 atf_check::process_option(int ch, const char* arg) 870 { 871 switch (ch) { 872 case 's': 873 m_status_checks.push_back(parse_status_check_arg(arg)); 874 break; 875 876 case 'o': 877 m_stdout_checks.push_back(parse_output_check_arg(arg)); 878 break; 879 880 case 'e': 881 m_stderr_checks.push_back(parse_output_check_arg(arg)); 882 break; 883 884 case 'r': 885 m_rflag = true; 886 parse_repeat_check_arg(arg, &m_timo, &m_interval); 887 break; 888 889 case 'x': 890 m_xflag = true; 891 break; 892 893 default: 894 UNREACHABLE; 895 } 896 } 897 898 int 899 atf_check::main(void) 900 { 901 if (m_argc < 1) 902 throw atf::application::usage_error("No command specified"); 903 904 int status = EXIT_FAILURE; 905 906 if (m_status_checks.empty()) 907 m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS)); 908 else if (m_status_checks.size() > 1) { 909 // TODO: Remove this restriction. 910 throw atf::application::usage_error("Cannot specify -s more than once"); 911 } 912 913 if (m_stdout_checks.empty()) 914 m_stdout_checks.push_back(output_check(oc_empty, false, "")); 915 if (m_stderr_checks.empty()) 916 m_stderr_checks.push_back(output_check(oc_empty, false, "")); 917 918 do { 919 std::auto_ptr< atf::check::check_result > r = 920 m_xflag ? execute_with_shell(m_argv) : execute(m_argv); 921 922 if ((run_status_checks(m_status_checks, *r) == false) || 923 (run_output_checks(*r, "stderr") == false) || 924 (run_output_checks(*r, "stdout") == false)) 925 status = EXIT_FAILURE; 926 else 927 status = EXIT_SUCCESS; 928 929 if (m_rflag && status == EXIT_FAILURE) { 930 if (timo_expired(m_timo)) 931 break; 932 usleep(m_interval); 933 } 934 } while (m_rflag && status == EXIT_FAILURE); 935 936 return status; 937 } 938 939 int 940 main(int argc, char* const* argv) 941 { 942 return atf_check().run(argc, argv); 943 } 944