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