xref: /netbsd-src/external/bsd/atf/dist/tools/process.cpp (revision 7ea1cb15c21a1df77faf869802a4207e4282f384)
1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2008 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.
15 //
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.
28 //
29 
30 extern "C" {
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 
34 #include <fcntl.h>
35 #include <signal.h>
36 }
37 
38 #include <cassert>
39 #include <cstdarg>
40 #include <cerrno>
41 #include <cstring>
42 #include <iostream>
43 
44 #include "exceptions.hpp"
45 #include "text.hpp"
46 #include "process.hpp"
47 
48 namespace detail = tools::process::detail;
49 namespace impl = tools::process;
50 #define IMPL_NAME "tools::process"
51 
52 // ------------------------------------------------------------------------
53 // Auxiliary functions.
54 // ------------------------------------------------------------------------
55 
56 template< class C >
57 tools::auto_array< const char* >
collection_to_argv(const C & c)58 collection_to_argv(const C& c)
59 {
60     tools::auto_array< const char* > argv(new const char*[c.size() + 1]);
61 
62     std::size_t pos = 0;
63     for (typename C::const_iterator iter = c.begin(); iter != c.end();
64          iter++) {
65         argv[pos] = (*iter).c_str();
66         pos++;
67     }
68     assert(pos == c.size());
69     argv[pos] = NULL;
70 
71     return argv;
72 }
73 
74 template< class C >
75 C
argv_to_collection(const char * const * argv)76 argv_to_collection(const char* const* argv)
77 {
78     C c;
79 
80     for (const char* const* iter = argv; *iter != NULL; iter++)
81         c.push_back(std::string(*iter));
82 
83     return c;
84 }
85 
86 static
87 void
safe_dup(const int oldfd,const int newfd)88 safe_dup(const int oldfd, const int newfd)
89 {
90     if (oldfd != newfd) {
91         if (dup2(oldfd, newfd) == -1) {
92             throw tools::system_error(IMPL_NAME "::safe_dup",
93                                       "Could not allocate file descriptor",
94                                       errno);
95         } else {
96             ::close(oldfd);
97         }
98     }
99 }
100 
101 static
102 int
const_execvp(const char * file,const char * const * argv)103 const_execvp(const char *file, const char *const *argv)
104 {
105     return ::execvp(file, const_cast<char* const*>(argv));
106 #undef UNCONST
107 }
108 
109 void
do_exec(void * v)110 detail::do_exec(void *v)
111 {
112     struct exec_args *ea = reinterpret_cast<struct exec_args *>(v);
113 
114     if (ea->m_prehook != NULL)
115         ea->m_prehook();
116 
117     const int ret = const_execvp(ea->m_prog.c_str(), ea->m_argv.exec_argv());
118     const int errnocopy = errno;
119     assert(ret == -1);
120     std::cerr << "exec(" << ea->m_prog.str() << ") failed: "
121               << std::strerror(errnocopy) << "\n";
122     std::exit(EXIT_FAILURE);
123 }
124 
125 // ------------------------------------------------------------------------
126 // The "argv_array" type.
127 // ------------------------------------------------------------------------
128 
argv_array(void)129 impl::argv_array::argv_array(void) :
130     m_exec_argv(collection_to_argv(m_args))
131 {
132 }
133 
argv_array(const char * arg1,...)134 impl::argv_array::argv_array(const char* arg1, ...)
135 {
136     m_args.push_back(arg1);
137 
138     {
139         va_list ap;
140         const char* nextarg;
141 
142         va_start(ap, arg1);
143         while ((nextarg = va_arg(ap, const char*)) != NULL)
144             m_args.push_back(nextarg);
145         va_end(ap);
146     }
147 
148     ctor_init_exec_argv();
149 }
150 
argv_array(const char * const * ca)151 impl::argv_array::argv_array(const char* const* ca) :
152     m_args(argv_to_collection< args_vector >(ca)),
153     m_exec_argv(collection_to_argv(m_args))
154 {
155 }
156 
argv_array(const argv_array & a)157 impl::argv_array::argv_array(const argv_array& a) :
158     m_args(a.m_args),
159     m_exec_argv(collection_to_argv(m_args))
160 {
161 }
162 
163 void
ctor_init_exec_argv(void)164 impl::argv_array::ctor_init_exec_argv(void)
165 {
166     m_exec_argv = collection_to_argv(m_args);
167 }
168 
169 const char* const*
exec_argv(void) const170 impl::argv_array::exec_argv(void)
171     const
172 {
173     return m_exec_argv.get();
174 }
175 
176 impl::argv_array::size_type
size(void) const177 impl::argv_array::size(void)
178     const
179 {
180     return m_args.size();
181 }
182 
183 const char*
operator [](int idx) const184 impl::argv_array::operator[](int idx)
185     const
186 {
187     return m_args[idx].c_str();
188 }
189 
190 impl::argv_array::const_iterator
begin(void) const191 impl::argv_array::begin(void)
192     const
193 {
194     return m_args.begin();
195 }
196 
197 impl::argv_array::const_iterator
end(void) const198 impl::argv_array::end(void)
199     const
200 {
201     return m_args.end();
202 }
203 
204 impl::argv_array&
operator =(const argv_array & a)205 impl::argv_array::operator=(const argv_array& a)
206 {
207     if (this != &a) {
208         m_args = a.m_args;
209         m_exec_argv = collection_to_argv(m_args);
210     }
211     return *this;
212 }
213 
214 // ------------------------------------------------------------------------
215 // The "stream" types.
216 // ------------------------------------------------------------------------
217 
stream_capture(void)218 impl::stream_capture::stream_capture(void)
219 {
220     for (int i = 0; i < 2; i++)
221         m_pipefds[i] = -1;
222 }
223 
~stream_capture(void)224 impl::stream_capture::~stream_capture(void)
225 {
226     for (int i = 0; i < 2; i++)
227         if (m_pipefds[i] != -1)
228             ::close(m_pipefds[i]);
229 }
230 
231 void
prepare(void)232 impl::stream_capture::prepare(void)
233 {
234     if (pipe(m_pipefds) == -1)
235         throw system_error(IMPL_NAME "::stream_capture::prepare",
236                            "Failed to create pipe", errno);
237 }
238 
239 int
connect_parent(void)240 impl::stream_capture::connect_parent(void)
241 {
242     ::close(m_pipefds[1]); m_pipefds[1] = -1;
243     const int fd = m_pipefds[0];
244     m_pipefds[0] = -1;
245     return fd;
246 }
247 
248 void
connect_child(const int fd)249 impl::stream_capture::connect_child(const int fd)
250 {
251     ::close(m_pipefds[0]); m_pipefds[0] = -1;
252     if (m_pipefds[1] != fd) {
253         safe_dup(m_pipefds[1], fd);
254     }
255     m_pipefds[1] = -1;
256 }
257 
stream_connect(const int src_fd,const int tgt_fd)258 impl::stream_connect::stream_connect(const int src_fd, const int tgt_fd) :
259     m_src_fd(src_fd), m_tgt_fd(tgt_fd)
260 {
261 }
262 
263 void
prepare(void)264 impl::stream_connect::prepare(void)
265 {
266 }
267 
268 int
connect_parent(void)269 impl::stream_connect::connect_parent(void)
270 {
271     return -1;
272 }
273 
274 void
connect_child(const int fd)275 impl::stream_connect::connect_child(const int fd __attribute__((__unused__)))
276 {
277     safe_dup(m_tgt_fd, m_src_fd);
278 }
279 
stream_inherit(void)280 impl::stream_inherit::stream_inherit(void)
281 {
282 }
283 
284 void
prepare(void)285 impl::stream_inherit::prepare(void)
286 {
287 }
288 
289 int
connect_parent(void)290 impl::stream_inherit::connect_parent(void)
291 {
292     return -1;
293 }
294 
295 void
connect_child(const int fd)296 impl::stream_inherit::connect_child(const int fd __attribute__((__unused__)))
297 {
298 }
299 
stream_redirect_fd(const int fd)300 impl::stream_redirect_fd::stream_redirect_fd(const int fd) :
301     m_fd(fd)
302 {
303 }
304 
305 void
prepare(void)306 impl::stream_redirect_fd::prepare(void)
307 {
308 }
309 
310 int
connect_parent(void)311 impl::stream_redirect_fd::connect_parent(void)
312 {
313     return -1;
314 }
315 
316 void
connect_child(const int fd)317 impl::stream_redirect_fd::connect_child(const int fd)
318 {
319     safe_dup(m_fd, fd);
320 }
321 
stream_redirect_path(const tools::fs::path & p)322 impl::stream_redirect_path::stream_redirect_path(const tools::fs::path& p) :
323     m_path(p)
324 {
325 }
326 
327 void
prepare(void)328 impl::stream_redirect_path::prepare(void)
329 {
330 }
331 
332 int
connect_parent(void)333 impl::stream_redirect_path::connect_parent(void)
334 {
335     return -1;
336 }
337 
338 void
connect_child(const int fd)339 impl::stream_redirect_path::connect_child(const int fd)
340 {
341     const int aux = ::open(m_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
342     if (aux == -1)
343         throw system_error(IMPL_NAME "::stream_redirect_path::connect_child",
344                            "Could not create " + m_path.str(), errno);
345     else
346         safe_dup(aux, fd);
347 }
348 
349 // ------------------------------------------------------------------------
350 // The "status" type.
351 // ------------------------------------------------------------------------
352 
status(int s)353 impl::status::status(int s) :
354     m_status(s)
355 {
356 }
357 
~status(void)358 impl::status::~status(void)
359 {
360 }
361 
362 std::string
str(void) const363 impl::status::str(void)
364      const
365 {
366     int mutable_status = m_status;
367     std::stringstream rv;
368     if (WIFEXITED(mutable_status))
369 	rv << "exit("  << WEXITSTATUS(mutable_status);
370     else if (WIFSTOPPED(mutable_status))
371 	rv << "stopped("  << WSTOPSIG(mutable_status);
372     else if (WIFSIGNALED(mutable_status))
373 	rv << "terminated("  << WTERMSIG(mutable_status);
374     if (WCOREDUMP(mutable_status))
375 	rv << "/core)";
376     else
377 	rv << ")";
378     return rv.str();
379 }
380 
381 bool
exited(void) const382 impl::status::exited(void)
383     const
384 {
385     int mutable_status = m_status;
386     return WIFEXITED(mutable_status);
387 }
388 
389 int
exitstatus(void) const390 impl::status::exitstatus(void)
391     const
392 {
393     assert(exited());
394     int mutable_status = m_status;
395     return WEXITSTATUS(mutable_status);
396 }
397 
398 bool
signaled(void) const399 impl::status::signaled(void)
400     const
401 {
402     int mutable_status = m_status;
403     return WIFSIGNALED(mutable_status);
404 }
405 
406 int
termsig(void) const407 impl::status::termsig(void)
408     const
409 {
410     assert(signaled());
411     int mutable_status = m_status;
412     return WTERMSIG(mutable_status);
413 }
414 
415 bool
coredump(void) const416 impl::status::coredump(void)
417     const
418 {
419     assert(signaled());
420     int mutable_status = m_status;
421     return WCOREDUMP(mutable_status);
422 }
423 
424 // ------------------------------------------------------------------------
425 // The "child" type.
426 // ------------------------------------------------------------------------
427 
child(const pid_t pid_arg,const int stdout_fd_arg,const int stderr_fd_arg)428 impl::child::child(const pid_t pid_arg, const int stdout_fd_arg,
429                    const int stderr_fd_arg) :
430     m_pid(pid_arg),
431     m_stdout(stdout_fd_arg),
432     m_stderr(stderr_fd_arg),
433     m_waited(false)
434 {
435 }
436 
~child(void)437 impl::child::~child(void)
438 {
439     if (!m_waited) {
440         ::kill(m_pid, SIGTERM);
441         (void)wait();
442 
443         if (m_stdout != -1) {
444             ::close(m_stdout); m_stdout = -1;
445         }
446         if (m_stderr != -1) {
447             ::close(m_stderr); m_stderr = -1;
448         }
449     }
450 }
451 
452 impl::status
wait(void)453 impl::child::wait(void)
454 {
455     int s;
456 
457     if (::waitpid(m_pid, &s, 0) == -1)
458         throw system_error(IMPL_NAME "::child::wait", "Failed waiting for "
459                            "process " + text::to_string(m_pid), errno);
460 
461     if (m_stdout != -1) {
462         ::close(m_stdout); m_stdout = -1;
463     }
464     if (m_stderr != -1) {
465         ::close(m_stderr); m_stderr = -1;
466     }
467 
468     m_waited = true;
469     return status(s);
470 }
471 
472 pid_t
pid(void) const473 impl::child::pid(void)
474     const
475 {
476     return m_pid;
477 }
478 
479 int
stdout_fd(void)480 impl::child::stdout_fd(void)
481 {
482     return m_stdout;
483 }
484 
485 int
stderr_fd(void)486 impl::child::stderr_fd(void)
487 {
488     return m_stderr;
489 }
490 
491 // ------------------------------------------------------------------------
492 // Free functions.
493 // ------------------------------------------------------------------------
494 
495 void
flush_streams(void)496 detail::flush_streams(void)
497 {
498     // This is a weird hack to ensure that the output of the parent process
499     // is flushed before executing a child which prevents, for example, the
500     // output of the atf-run hooks to appear before the output of atf-run
501     // itself.
502     //
503     // TODO: This should only be executed when inheriting the stdout or
504     // stderr file descriptors.  However, the flushing is specific to the
505     // iostreams, so we cannot do it from the C library where all the process
506     // logic is performed.  Come up with a better design.
507     std::cout.flush();
508     std::cerr.flush();
509 }
510