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
status_check__anon7e9de9140111::status_check79 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
output_check__anon7e9de9140111::output_check103 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:
temp_file(const char * pattern)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
~temp_file(void)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&
get_path(void) const154 get_path(void) const
155 {
156 return *m_path;
157 }
158
159 void
write(const std::string & text)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
close(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
get_monotonic_useconds(void)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
timo_expired(useconds_t timeout)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
parse_exit_code(const std::string & str)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
signal_name_to_number(const std::string & str)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
parse_signal(const std::string & str)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
parse_status_check_arg(const std::string & arg)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
parse_output_check_arg(const std::string & arg)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
parse_repeat_check_arg(const std::string & arg,useconds_t * m_timo,useconds_t * m_interval)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
flatten_argv(char * const * argv)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 >
execute(const char * const * argv)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 >
execute_with_shell(char * const * argv)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
cat_file(const atf::fs::path & path)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
grep_file(const atf::fs::path & path,const std::string & regexp)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
file_empty(const atf::fs::path & p)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
compare_files(const atf::fs::path & p1,const atf::fs::path & p2)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
print_diff(const atf::fs::path & p1,const atf::fs::path & p2)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
decode(const std::string & s)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
run_status_check(const status_check & sc,const atf::check::check_result & cr)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
run_status_checks(const std::vector<status_check> & checks,const atf::check::check_result & result)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
run_output_check(const output_check & oc,const atf::fs::path & path,const std::string & stdxxx)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
run_output_checks(const std::vector<output_check> & checks,const atf::fs::path & path,const std::string & stdxxx)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
atf_check(void)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
run_output_checks(const atf::check::check_result & r,const std::string & stdxxx) const835 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
specific_args(void) const852 atf_check::specific_args(void)
853 const
854 {
855 return "<command>";
856 }
857
858 atf_check::options_set
specific_options(void) const859 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
process_option(int ch,const char * arg)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
main(void)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
main(int argc,char * const * argv)953 main(int argc, char* const* argv)
954 {
955 return atf_check().run(argc, argv);
956 }
957