1 // 2 // Automated Testing Framework (atf) 3 // 4 // Copyright (c) 2008 The NetBSD Foundation, Inc. 5 // All rights reserved. 6 // 7 // Redistribution and use in source and binary forms, with or without 8 // modification, are permitted provided that the following conditions 9 // are met: 10 // 1. Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // 2. Redistributions in binary form must reproduce the above copyright 13 // notice, this list of conditions and the following disclaimer in the 14 // documentation and/or other materials provided with the distribution. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 // 29 30 extern "C" { 31 #include <sys/types.h> 32 #include <sys/wait.h> 33 34 #include <limits.h> 35 #include <signal.h> 36 #include <unistd.h> 37 } 38 39 #include <cerrno> 40 #include <cstdlib> 41 #include <cstring> 42 #include <fstream> 43 #include <ios> 44 #include <iostream> 45 #include <iterator> 46 #include <list> 47 #include <memory> 48 #include <utility> 49 50 #include "atf-c++/check.hpp" 51 #include "atf-c++/config.hpp" 52 53 #include "atf-c++/detail/application.hpp" 54 #include "atf-c++/detail/auto_array.hpp" 55 #include "atf-c++/detail/exceptions.hpp" 56 #include "atf-c++/detail/fs.hpp" 57 #include "atf-c++/detail/process.hpp" 58 #include "atf-c++/detail/sanity.hpp" 59 #include "atf-c++/detail/text.hpp" 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 atf::fs::path& p) : 116 std::ostream(NULL), 117 m_fd(-1) 118 { 119 atf::auto_array< char > buf(new char[p.str().length() + 1]); 120 std::strcpy(buf.get(), p.c_str()); 121 122 m_fd = ::mkstemp(buf.get()); 123 if (m_fd == -1) 124 throw atf::system_error("atf_check::temp_file::temp_file(" + 125 p.str() + ")", "mkstemp(3) failed", 126 errno); 127 128 m_path.reset(new atf::fs::path(buf.get())); 129 } 130 131 ~temp_file(void) 132 { 133 close(); 134 try { 135 remove(*m_path); 136 } catch (const atf::system_error&) { 137 // Ignore deletion errors. 138 } 139 } 140 141 const atf::fs::path& 142 get_path(void) const 143 { 144 return *m_path; 145 } 146 147 void 148 write(const std::string& text) 149 { 150 if (::write(m_fd, text.c_str(), text.size()) == -1) 151 throw atf::system_error("atf_check", "write(2) failed", errno); 152 } 153 154 void 155 close(void) 156 { 157 if (m_fd != -1) { 158 flush(); 159 ::close(m_fd); 160 m_fd = -1; 161 } 162 } 163 }; 164 165 } // anonymous namespace 166 167 static int 168 parse_exit_code(const std::string& str) 169 { 170 try { 171 const int value = atf::text::to_type< int >(str); 172 if (value < 0 || value > 255) 173 throw std::runtime_error("Unused reason"); 174 return value; 175 } catch (const std::runtime_error&) { 176 throw atf::application::usage_error("Invalid exit code for -s option; " 177 "must be an integer in range 0-255"); 178 } 179 } 180 181 static struct name_number { 182 const char *name; 183 int signo; 184 } signal_names_to_numbers[] = { 185 { "hup", SIGHUP }, 186 { "int", SIGINT }, 187 { "quit", SIGQUIT }, 188 { "trap", SIGTRAP }, 189 { "abrt", SIGABRT }, 190 { "kill", SIGKILL }, 191 { "segv", SIGSEGV }, 192 { "pipe", SIGPIPE }, 193 { "alrm", SIGALRM }, 194 { "term", SIGTERM }, 195 { "usr1", SIGUSR1 }, 196 { "usr2", SIGUSR2 }, 197 { NULL, INT_MIN }, 198 }; 199 200 static int 201 signal_name_to_number(const std::string& str) 202 { 203 struct name_number* iter = signal_names_to_numbers; 204 int signo = INT_MIN; 205 while (signo == INT_MIN && iter->name != NULL) { 206 if (str == iter->name || str == std::string("sig") + iter->name) 207 signo = iter->signo; 208 else 209 iter++; 210 } 211 return signo; 212 } 213 214 static int 215 parse_signal(const std::string& str) 216 { 217 const int signo = signal_name_to_number(str); 218 if (signo == INT_MIN) { 219 try { 220 return atf::text::to_type< int >(str); 221 } catch (std::runtime_error) { 222 throw atf::application::usage_error("Invalid signal name or number " 223 "in -s option"); 224 } 225 } 226 INV(signo != INT_MIN); 227 return signo; 228 } 229 230 static status_check 231 parse_status_check_arg(const std::string& arg) 232 { 233 const std::string::size_type delimiter = arg.find(':'); 234 bool negated = (arg.compare(0, 4, "not-") == 0); 235 const std::string action_str = arg.substr(0, delimiter); 236 const std::string action = negated ? action_str.substr(4) : action_str; 237 const std::string value_str = ( 238 delimiter == std::string::npos ? "" : arg.substr(delimiter + 1)); 239 int value; 240 241 status_check_t type; 242 if (action == "eq") { 243 // Deprecated; use exit instead. TODO: Remove after 0.10. 244 type = sc_exit; 245 if (negated) 246 throw atf::application::usage_error("Cannot negate eq checker"); 247 negated = false; 248 value = parse_exit_code(value_str); 249 } else if (action == "exit") { 250 type = sc_exit; 251 if (value_str.empty()) 252 value = INT_MIN; 253 else 254 value = parse_exit_code(value_str); 255 } else if (action == "ignore") { 256 if (negated) 257 throw atf::application::usage_error("Cannot negate ignore checker"); 258 type = sc_ignore; 259 value = INT_MIN; 260 } else if (action == "ne") { 261 // Deprecated; use not-exit instead. TODO: Remove after 0.10. 262 type = sc_exit; 263 if (negated) 264 throw atf::application::usage_error("Cannot negate ne checker"); 265 negated = true; 266 value = parse_exit_code(value_str); 267 } else if (action == "signal") { 268 type = sc_signal; 269 if (value_str.empty()) 270 value = INT_MIN; 271 else 272 value = parse_signal(value_str); 273 } else 274 throw atf::application::usage_error("Invalid status checker"); 275 276 return status_check(type, negated, value); 277 } 278 279 static 280 output_check 281 parse_output_check_arg(const std::string& arg) 282 { 283 const std::string::size_type delimiter = arg.find(':'); 284 const bool negated = (arg.compare(0, 4, "not-") == 0); 285 const std::string action_str = arg.substr(0, delimiter); 286 const std::string action = negated ? action_str.substr(4) : action_str; 287 288 output_check_t type; 289 if (action == "empty") 290 type = oc_empty; 291 else if (action == "file") 292 type = oc_file; 293 else if (action == "ignore") { 294 if (negated) 295 throw atf::application::usage_error("Cannot negate ignore checker"); 296 type = oc_ignore; 297 } else if (action == "inline") 298 type = oc_inline; 299 else if (action == "match") 300 type = oc_match; 301 else if (action == "save") { 302 if (negated) 303 throw atf::application::usage_error("Cannot negate save checker"); 304 type = oc_save; 305 } else 306 throw atf::application::usage_error("Invalid output checker"); 307 308 return output_check(type, negated, arg.substr(delimiter + 1)); 309 } 310 311 static 312 std::string 313 flatten_argv(char* const* argv) 314 { 315 std::string cmdline; 316 317 char* const* arg = &argv[0]; 318 while (*arg != NULL) { 319 if (arg != &argv[0]) 320 cmdline += ' '; 321 322 cmdline += *arg; 323 324 arg++; 325 } 326 327 return cmdline; 328 } 329 330 static 331 std::auto_ptr< atf::check::check_result > 332 execute(const char* const* argv) 333 { 334 // TODO: This should go to stderr... but fixing it now may be hard as test 335 // cases out there might be relying on stderr being silent. 336 std::cout << "Executing command [ "; 337 for (int i = 0; argv[i] != NULL; ++i) 338 std::cout << argv[i] << " "; 339 std::cout << "]\n"; 340 std::cout.flush(); 341 342 atf::process::argv_array argva(argv); 343 return atf::check::exec(argva); 344 } 345 346 static 347 std::auto_ptr< atf::check::check_result > 348 execute_with_shell(char* const* argv) 349 { 350 const std::string cmd = flatten_argv(argv); 351 352 const char* sh_argv[4]; 353 sh_argv[0] = atf::config::get("atf_shell").c_str(); 354 sh_argv[1] = "-c"; 355 sh_argv[2] = cmd.c_str(); 356 sh_argv[3] = NULL; 357 return execute(sh_argv); 358 } 359 360 static 361 void 362 open_error(const atf::fs::path& path) 363 { 364 throw std::runtime_error("Failed to open " + path.str() + " " 365 + ::strerror(errno)); 366 } 367 368 369 static 370 void 371 cat_file(const atf::fs::path& path) 372 { 373 std::ifstream stream(path.c_str()); 374 if (!stream) 375 open_error(path); 376 377 stream >> std::noskipws; 378 std::istream_iterator< char > begin(stream), end; 379 std::ostream_iterator< char > out(std::cerr); 380 std::copy(begin, end, out); 381 382 stream.close(); 383 } 384 385 static 386 bool 387 grep_file(const atf::fs::path& path, const std::string& regexp) 388 { 389 std::ifstream stream(path.c_str()); 390 if (!stream) 391 open_error(path); 392 393 bool found = false; 394 395 std::string line; 396 while (!found && !std::getline(stream, line).fail()) { 397 if (atf::text::match(line, regexp)) 398 found = true; 399 } 400 401 stream.close(); 402 403 return found; 404 } 405 406 static 407 bool 408 file_empty(const atf::fs::path& p) 409 { 410 atf::fs::file_info f(p); 411 412 return (f.get_size() == 0); 413 } 414 415 static bool 416 compare_files(const atf::fs::path& p1, const atf::fs::path& p2) 417 { 418 bool equal = false; 419 420 std::ifstream f1(p1.c_str()); 421 if (!f1) 422 open_error(p1); 423 424 std::ifstream f2(p2.c_str()); 425 if (!f2) 426 open_error(p2); 427 428 for (;;) { 429 char buf1[512], buf2[512]; 430 431 f1.read(buf1, sizeof(buf1)); 432 if (f1.bad()) 433 throw std::runtime_error("Failed to read from " + p1.str()); 434 435 f2.read(buf2, sizeof(buf2)); 436 if (f2.bad()) 437 throw std::runtime_error("Failed to read from " + p1.str()); 438 439 if ((f1.gcount() == 0) && (f2.gcount() == 0)) { 440 equal = true; 441 break; 442 } 443 444 if ((f1.gcount() != f2.gcount()) || 445 (std::memcmp(buf1, buf2, f1.gcount()) != 0)) { 446 break; 447 } 448 } 449 450 return equal; 451 } 452 453 static 454 void 455 print_diff(const atf::fs::path& p1, const atf::fs::path& p2) 456 { 457 const atf::process::status s = 458 atf::process::exec(atf::fs::path("diff"), 459 atf::process::argv_array("diff", "-u", p1.c_str(), 460 p2.c_str(), NULL), 461 atf::process::stream_connect(STDOUT_FILENO, 462 STDERR_FILENO), 463 atf::process::stream_inherit()); 464 465 if (!s.exited()) 466 std::cerr << "Failed to run diff(3)\n"; 467 468 if (s.exitstatus() != 1) 469 std::cerr << "Error while running diff(3)\n"; 470 } 471 472 static 473 std::string 474 decode(const std::string& s) 475 { 476 size_t i; 477 std::string res; 478 479 res.reserve(s.length()); 480 481 i = 0; 482 while (i < s.length()) { 483 char c = s[i++]; 484 485 if (c == '\\') { 486 switch (s[i++]) { 487 case 'a': c = '\a'; break; 488 case 'b': c = '\b'; break; 489 case 'c': break; 490 case 'e': c = 033; break; 491 case 'f': c = '\f'; break; 492 case 'n': c = '\n'; break; 493 case 'r': c = '\r'; break; 494 case 't': c = '\t'; break; 495 case 'v': c = '\v'; break; 496 case '\\': break; 497 case '0': 498 { 499 int count = 3; 500 c = 0; 501 while (--count >= 0 && (unsigned)(s[i] - '0') < 8) 502 c = (c << 3) + (s[i++] - '0'); 503 break; 504 } 505 default: 506 --i; 507 break; 508 } 509 } 510 511 res.push_back(c); 512 } 513 514 return res; 515 } 516 517 static 518 bool 519 run_status_check(const status_check& sc, const atf::check::check_result& cr) 520 { 521 bool result; 522 523 if (sc.type == sc_exit) { 524 if (cr.exited() && sc.value != INT_MIN) { 525 const int status = cr.exitcode(); 526 527 if (!sc.negated && sc.value != status) { 528 std::cerr << "Fail: incorrect exit status: " 529 << status << ", expected: " 530 << sc.value << "\n"; 531 result = false; 532 } else if (sc.negated && sc.value == status) { 533 std::cerr << "Fail: incorrect exit status: " 534 << status << ", expected: " 535 << "anything else\n"; 536 result = false; 537 } else 538 result = true; 539 } else if (cr.exited() && sc.value == INT_MIN) { 540 result = true; 541 } else { 542 std::cerr << "Fail: program did not exit cleanly\n"; 543 result = false; 544 } 545 } else if (sc.type == sc_ignore) { 546 result = true; 547 } else if (sc.type == sc_signal) { 548 if (cr.signaled() && sc.value != INT_MIN) { 549 const int status = cr.termsig(); 550 551 if (!sc.negated && sc.value != status) { 552 std::cerr << "Fail: incorrect signal received: " 553 << status << ", expected: " << sc.value << "\n"; 554 result = false; 555 } else if (sc.negated && sc.value == status) { 556 std::cerr << "Fail: incorrect signal received: " 557 << status << ", expected: " 558 << "anything else\n"; 559 result = false; 560 } else 561 result = true; 562 } else if (cr.signaled() && sc.value == INT_MIN) { 563 result = true; 564 } else { 565 std::cerr << "Fail: program did not receive a signal\n"; 566 result = false; 567 } 568 } else { 569 UNREACHABLE; 570 result = false; 571 } 572 573 if (result == false) { 574 std::cerr << "stdout:\n"; 575 cat_file(atf::fs::path(cr.stdout_path())); 576 std::cerr << "\n"; 577 578 std::cerr << "stderr:\n"; 579 cat_file(atf::fs::path(cr.stderr_path())); 580 std::cerr << "\n"; 581 } 582 583 return result; 584 } 585 586 static 587 bool 588 run_status_checks(const std::vector< status_check >& checks, 589 const atf::check::check_result& result) 590 { 591 bool ok = false; 592 593 for (std::vector< status_check >::const_iterator iter = checks.begin(); 594 !ok && iter != checks.end(); iter++) { 595 ok |= run_status_check(*iter, result); 596 } 597 598 return ok; 599 } 600 601 static 602 bool 603 run_output_check(const output_check oc, const atf::fs::path& path, 604 const std::string& stdxxx) 605 { 606 bool result; 607 608 if (oc.type == oc_empty) { 609 const bool is_empty = file_empty(path); 610 if (!oc.negated && !is_empty) { 611 std::cerr << "Fail: " << stdxxx << " not empty\n"; 612 print_diff(atf::fs::path("/dev/null"), path); 613 result = false; 614 } else if (oc.negated && is_empty) { 615 std::cerr << "Fail: " << stdxxx << " is empty\n"; 616 result = false; 617 } else 618 result = true; 619 } else if (oc.type == oc_file) { 620 const bool equals = compare_files(path, atf::fs::path(oc.value)); 621 if (!oc.negated && !equals) { 622 std::cerr << "Fail: " << stdxxx << " does not match golden " 623 "output\n"; 624 print_diff(atf::fs::path(oc.value), path); 625 result = false; 626 } else if (oc.negated && equals) { 627 std::cerr << "Fail: " << stdxxx << " matches golden output\n"; 628 cat_file(atf::fs::path(oc.value)); 629 result = false; 630 } else 631 result = true; 632 } else if (oc.type == oc_ignore) { 633 result = true; 634 } else if (oc.type == oc_inline) { 635 atf::fs::path path2 = atf::fs::path(atf::config::get("atf_workdir")) 636 / "inline.XXXXXX"; 637 temp_file temp(path2); 638 temp.write(decode(oc.value)); 639 temp.close(); 640 641 const bool equals = compare_files(path, temp.get_path()); 642 if (!oc.negated && !equals) { 643 std::cerr << "Fail: " << stdxxx << " does not match expected " 644 "value\n"; 645 print_diff(temp.get_path(), path); 646 result = false; 647 } else if (oc.negated && equals) { 648 std::cerr << "Fail: " << stdxxx << " matches expected value\n"; 649 cat_file(temp.get_path()); 650 result = false; 651 } else 652 result = true; 653 } else if (oc.type == oc_match) { 654 const bool matches = grep_file(path, oc.value); 655 if (!oc.negated && !matches) { 656 std::cerr << "Fail: regexp " + oc.value + " not in " << stdxxx 657 << "\n"; 658 cat_file(path); 659 result = false; 660 } else if (oc.negated && matches) { 661 std::cerr << "Fail: regexp " + oc.value + " is in " << stdxxx 662 << "\n"; 663 cat_file(path); 664 result = false; 665 } else 666 result = true; 667 } else if (oc.type == oc_save) { 668 INV(!oc.negated); 669 std::ifstream ifs(path.c_str(), std::fstream::binary); 670 ifs >> std::noskipws; 671 std::istream_iterator< char > begin(ifs), end; 672 673 std::ofstream ofs(oc.value.c_str(), std::fstream::binary 674 | std::fstream::trunc); 675 std::ostream_iterator <char> obegin(ofs); 676 677 std::copy(begin, end, obegin); 678 result = true; 679 } else { 680 UNREACHABLE; 681 result = false; 682 } 683 684 return result; 685 } 686 687 static 688 bool 689 run_output_checks(const std::vector< output_check >& checks, 690 const atf::fs::path& path, const std::string& stdxxx) 691 { 692 bool ok = true; 693 694 for (std::vector< output_check >::const_iterator iter = checks.begin(); 695 iter != checks.end(); iter++) { 696 ok &= run_output_check(*iter, path, stdxxx); 697 } 698 699 return ok; 700 } 701 702 // ------------------------------------------------------------------------ 703 // The "atf_check" application. 704 // ------------------------------------------------------------------------ 705 706 namespace { 707 708 class atf_check : public atf::application::app { 709 bool m_xflag; 710 711 std::vector< status_check > m_status_checks; 712 std::vector< output_check > m_stdout_checks; 713 std::vector< output_check > m_stderr_checks; 714 715 static const char* m_description; 716 717 bool run_output_checks(const atf::check::check_result&, 718 const std::string&) const; 719 720 std::string specific_args(void) const; 721 options_set specific_options(void) const; 722 void process_option(int, const char*); 723 void process_option_s(const std::string&); 724 725 public: 726 atf_check(void); 727 int main(void); 728 }; 729 730 } // anonymous namespace 731 732 const char* atf_check::m_description = 733 "atf-check executes given command and analyzes its results."; 734 735 atf_check::atf_check(void) : 736 app(m_description, "atf-check(1)"), 737 m_xflag(false) 738 { 739 } 740 741 bool 742 atf_check::run_output_checks(const atf::check::check_result& r, 743 const std::string& stdxxx) 744 const 745 { 746 if (stdxxx == "stdout") { 747 return ::run_output_checks(m_stdout_checks, 748 atf::fs::path(r.stdout_path()), "stdout"); 749 } else if (stdxxx == "stderr") { 750 return ::run_output_checks(m_stderr_checks, 751 atf::fs::path(r.stderr_path()), "stderr"); 752 } else { 753 UNREACHABLE; 754 return false; 755 } 756 } 757 758 std::string 759 atf_check::specific_args(void) 760 const 761 { 762 return "<command>"; 763 } 764 765 atf_check::options_set 766 atf_check::specific_options(void) 767 const 768 { 769 using atf::application::option; 770 options_set opts; 771 772 opts.insert(option('s', "qual:value", "Handle status. Qualifier " 773 "must be one of: ignore exit:<num> signal:<name|num>")); 774 opts.insert(option('o', "action:arg", "Handle stdout. Action must be " 775 "one of: empty ignore file:<path> inline:<val> match:regexp " 776 "save:<path>")); 777 opts.insert(option('e', "action:arg", "Handle stderr. Action must be " 778 "one of: empty ignore file:<path> inline:<val> match:regexp " 779 "save:<path>")); 780 opts.insert(option('x', "", "Execute command as a shell command")); 781 782 return opts; 783 } 784 785 void 786 atf_check::process_option(int ch, const char* arg) 787 { 788 switch (ch) { 789 case 's': 790 m_status_checks.push_back(parse_status_check_arg(arg)); 791 break; 792 793 case 'o': 794 m_stdout_checks.push_back(parse_output_check_arg(arg)); 795 break; 796 797 case 'e': 798 m_stderr_checks.push_back(parse_output_check_arg(arg)); 799 break; 800 801 case 'x': 802 m_xflag = true; 803 break; 804 805 default: 806 UNREACHABLE; 807 } 808 } 809 810 int 811 atf_check::main(void) 812 { 813 if (m_argc < 1) 814 throw atf::application::usage_error("No command specified"); 815 816 int status = EXIT_FAILURE; 817 818 std::auto_ptr< atf::check::check_result > r = 819 m_xflag ? execute_with_shell(m_argv) : execute(m_argv); 820 821 if (m_status_checks.empty()) 822 m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS)); 823 else if (m_status_checks.size() > 1) { 824 // TODO: Remove this restriction. 825 throw atf::application::usage_error("Cannot specify -s more than once"); 826 } 827 828 if (m_stdout_checks.empty()) 829 m_stdout_checks.push_back(output_check(oc_empty, false, "")); 830 if (m_stderr_checks.empty()) 831 m_stderr_checks.push_back(output_check(oc_empty, false, "")); 832 833 if ((run_status_checks(m_status_checks, *r) == false) || 834 (run_output_checks(*r, "stderr") == false) || 835 (run_output_checks(*r, "stdout") == false)) 836 status = EXIT_FAILURE; 837 else 838 status = EXIT_SUCCESS; 839 840 return status; 841 } 842 843 int 844 main(int argc, char* const* argv) 845 { 846 return atf_check().run(argc, argv); 847 } 848