Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / atf / dist / tools / atf-exec.cpp
blob0ff3eabc837680687a358bfcb838913de6596a1b
1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 extern "C" {
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include <signal.h>
34 #include <unistd.h>
37 #include <cerrno>
38 #include <cstdlib>
39 #include <cstring>
40 #include <fstream>
41 #include <iostream>
43 #include "atf-c++/application.hpp"
44 #include "atf-c++/exceptions.hpp"
45 #include "atf-c++/process.hpp"
46 #include "atf-c++/sanity.hpp"
47 #include "atf-c++/signals.hpp"
48 #include "atf-c++/text.hpp"
50 namespace sigalarm {
51 static bool happened = false;
53 void
54 handler(int signo)
56 happened = true;
60 class atf_exec : public atf::application::app {
61 static const char* m_description;
63 std::string specific_args(void) const;
64 options_set specific_options(void) const;
65 void process_option(int, const char*);
67 void process_option_t(const std::string&);
68 unsigned int m_timeout_secs;
69 std::string m_timeout_file;
71 static void route_do_exec(void *);
72 void do_exec(void) const;
74 static int handle_status(const atf::process::status&);
76 public:
77 atf_exec(void);
79 int main(void);
82 const char* atf_exec::m_description =
83 "atf-exec executes the given command in a controlled manner.";
85 atf_exec::atf_exec(void) :
86 app(m_description, "atf-exec(1)", "atf(7)"),
87 m_timeout_secs(0)
91 std::string
92 atf_exec::specific_args(void)
93 const
95 return "<command>";
98 atf_exec::options_set
99 atf_exec::specific_options(void)
100 const
102 using atf::application::option;
103 options_set opts;
104 opts.insert(option('t', "secs:file", "Kills the command after the "
105 "specified amount of time and "
106 "creates the given file"));
107 return opts;
110 void
111 atf_exec::process_option_t(const std::string& arg)
113 using atf::application::usage_error;
115 std::string::size_type pos = arg.find(':');
116 if (pos == std::string::npos)
117 throw usage_error("Invalid value for -t option; must be of the "
118 "form secs:file");
119 if (pos == 0)
120 throw usage_error("Invalid value for -t option; secs cannot be "
121 "empty");
122 if (pos == arg.length() - 1)
123 throw usage_error("Invalid value for -t option; file cannot be "
124 "empty");
126 m_timeout_secs = atf::text::to_type< unsigned int >(arg.substr(0, pos));
127 m_timeout_file = arg.substr(pos + 1);
130 void
131 atf_exec::process_option(int ch, const char* arg)
133 switch (ch) {
134 case 't':
135 process_option_t(arg);
136 break;
138 default:
139 UNREACHABLE;
143 void
144 atf_exec::route_do_exec(void *v)
146 atf_exec* ae = static_cast< atf_exec* >(v);
147 ae->do_exec();
150 void
151 atf_exec::do_exec(void)
152 const
154 if (::setpgid(::getpid(), 0) == -1)
155 throw atf::system_error("main", "setpgid failed", errno);
157 char** argv = new char*[m_argc + 1];
158 for (int i = 0; i < m_argc; i++)
159 argv[i] = ::strdup(m_argv[i]);
160 argv[m_argc] = NULL;
162 ::execvp(m_argv[0], argv);
163 // TODO: Handle error code from execvp.
164 std::abort();
168 atf_exec::handle_status(const atf::process::status& s)
170 int exitcode = EXIT_FAILURE;
172 if (s.exited())
173 exitcode = s.exitstatus();
174 else if (s.signaled())
175 ::kill(0, s.termsig());
176 else
177 UNREACHABLE;
179 return exitcode;
183 atf_exec::main(void)
185 if (m_argc < 1)
186 throw atf::application::usage_error("No command specified");
188 atf::process::child c =
189 atf::process::fork(route_do_exec,
190 atf::process::stream_inherit(),
191 atf::process::stream_inherit(),
192 this);
194 atf::signals::signal_programmer sp(SIGALRM, sigalarm::handler);
196 if (m_timeout_secs > 0) {
197 struct itimerval itv;
199 timerclear(&itv.it_interval);
200 timerclear(&itv.it_value);
201 itv.it_value.tv_sec = m_timeout_secs;
202 if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
203 throw atf::system_error("main", "setitimer failed", errno);
206 int exitcode;
207 try {
208 const atf::process::status s = c.wait();
209 exitcode = handle_status(s);
210 } catch (const atf::system_error& e) {
211 if (e.code() == EINTR) {
212 if (sigalarm::happened) {
213 INV(m_timeout_secs > 0);
215 ::killpg(c.pid(), SIGTERM);
217 std::ofstream os(m_timeout_file.c_str());
218 os.close();
220 exitcode = EXIT_FAILURE;
221 (void)c.wait();
222 } else {
223 // TODO: Retry wait.
224 abort();
226 } else
227 throw;
229 return exitcode;
233 main(int argc, char* const* argv)
235 return atf_exec().run(argc, argv);