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