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
status_check__anon7e9de9140111::status_check78 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
output_check__anon7e9de9140111::output_check101 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:
temp_file(const char * pattern)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
~temp_file(void)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&
get_path(void) const145 get_path(void) const
146 {
147 return *m_path;
148 }
149
150 void
write(const std::string & text)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
close(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
get_monotonic_useconds(void)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
timo_expired(useconds_t timeout)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
parse_exit_code(const std::string & str)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
signal_name_to_number(const std::string & str)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
parse_signal(const std::string & str)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
parse_status_check_arg(const std::string & arg)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
parse_output_check_arg(const std::string & arg)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
parse_repeat_check_arg(const std::string & arg,useconds_t * m_timo,useconds_t * m_interval)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
flatten_argv(char * const * argv)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 >
execute(const char * const * argv)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 >
execute_with_shell(char * const * argv)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
cat_file(const atf::fs::path & path)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
grep_file(const atf::fs::path & path,const std::string & regexp)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
file_empty(const atf::fs::path & p)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
compare_files(const atf::fs::path & p1,const atf::fs::path & p2)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
print_diff(const atf::fs::path & p1,const atf::fs::path & p2)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
decode(const std::string & s)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
run_status_check(const status_check & sc,const atf::check::check_result & cr)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
run_status_checks(const std::vector<status_check> & checks,const atf::check::check_result & result)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
run_output_check(const output_check oc,const atf::fs::path & path,const std::string & stdxxx)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
run_output_checks(const std::vector<output_check> & checks,const atf::fs::path & path,const std::string & stdxxx)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
atf_check(void)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
run_output_checks(const atf::check::check_result & r,const std::string & stdxxx) const823 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
specific_args(void) const840 atf_check::specific_args(void)
841 const
842 {
843 return "<command>";
844 }
845
846 atf_check::options_set
specific_options(void) const847 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
process_option(int ch,const char * arg)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
main(void)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
main(int argc,char * const * argv)940 main(int argc, char* const* argv)
941 {
942 return atf_check().run(argc, argv);
943 }
944