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