xref: /minix/external/bsd/atf/dist/tools/process.cpp (revision e1cdaee1)
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 #define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
106     return ::execvp(file, (char* const*)(UNCONST(argv)));
107 #undef UNCONST
108 }
109 
110 void
do_exec(void * v)111 detail::do_exec(void *v)
112 {
113     struct exec_args *ea = reinterpret_cast<struct exec_args *>(v);
114 
115     if (ea->m_prehook != NULL)
116         ea->m_prehook();
117 
118 #if !defined(NDEBUG) && defined(__minix)
119     const int ret =
120 #endif /* !defined(NDEBUG) && defined(__minix) */
121     	const_execvp(ea->m_prog.c_str(), ea->m_argv.exec_argv());
122     const int errnocopy = errno;
123     assert(ret == -1);
124     std::cerr << "exec(" << ea->m_prog.str() << ") failed: "
125               << std::strerror(errnocopy) << "\n";
126     std::exit(EXIT_FAILURE);
127 }
128 
129 // ------------------------------------------------------------------------
130 // The "argv_array" type.
131 // ------------------------------------------------------------------------
132 
argv_array(void)133 impl::argv_array::argv_array(void) :
134     m_exec_argv(collection_to_argv(m_args))
135 {
136 }
137 
argv_array(const char * arg1,...)138 impl::argv_array::argv_array(const char* arg1, ...)
139 {
140     m_args.push_back(arg1);
141 
142     {
143         va_list ap;
144         const char* nextarg;
145 
146         va_start(ap, arg1);
147         while ((nextarg = va_arg(ap, const char*)) != NULL)
148             m_args.push_back(nextarg);
149         va_end(ap);
150     }
151 
152     ctor_init_exec_argv();
153 }
154 
argv_array(const char * const * ca)155 impl::argv_array::argv_array(const char* const* ca) :
156     m_args(argv_to_collection< args_vector >(ca)),
157     m_exec_argv(collection_to_argv(m_args))
158 {
159 }
160 
argv_array(const argv_array & a)161 impl::argv_array::argv_array(const argv_array& a) :
162     m_args(a.m_args),
163     m_exec_argv(collection_to_argv(m_args))
164 {
165 }
166 
167 void
ctor_init_exec_argv(void)168 impl::argv_array::ctor_init_exec_argv(void)
169 {
170     m_exec_argv = collection_to_argv(m_args);
171 }
172 
173 const char* const*
exec_argv(void) const174 impl::argv_array::exec_argv(void)
175     const
176 {
177     return m_exec_argv.get();
178 }
179 
180 impl::argv_array::size_type
size(void) const181 impl::argv_array::size(void)
182     const
183 {
184     return m_args.size();
185 }
186 
187 const char*
operator [](int idx) const188 impl::argv_array::operator[](int idx)
189     const
190 {
191     return m_args[idx].c_str();
192 }
193 
194 impl::argv_array::const_iterator
begin(void) const195 impl::argv_array::begin(void)
196     const
197 {
198     return m_args.begin();
199 }
200 
201 impl::argv_array::const_iterator
end(void) const202 impl::argv_array::end(void)
203     const
204 {
205     return m_args.end();
206 }
207 
208 impl::argv_array&
operator =(const argv_array & a)209 impl::argv_array::operator=(const argv_array& a)
210 {
211     if (this != &a) {
212         m_args = a.m_args;
213         m_exec_argv = collection_to_argv(m_args);
214     }
215     return *this;
216 }
217 
218 // ------------------------------------------------------------------------
219 // The "stream" types.
220 // ------------------------------------------------------------------------
221 
stream_capture(void)222 impl::stream_capture::stream_capture(void)
223 {
224     for (int i = 0; i < 2; i++)
225         m_pipefds[i] = -1;
226 }
227 
~stream_capture(void)228 impl::stream_capture::~stream_capture(void)
229 {
230     for (int i = 0; i < 2; i++)
231         if (m_pipefds[i] != -1)
232             ::close(m_pipefds[i]);
233 }
234 
235 void
prepare(void)236 impl::stream_capture::prepare(void)
237 {
238     if (pipe(m_pipefds) == -1)
239         throw system_error(IMPL_NAME "::stream_capture::prepare",
240                            "Failed to create pipe", errno);
241 }
242 
243 int
connect_parent(void)244 impl::stream_capture::connect_parent(void)
245 {
246     ::close(m_pipefds[1]); m_pipefds[1] = -1;
247     const int fd = m_pipefds[0];
248     m_pipefds[0] = -1;
249     return fd;
250 }
251 
252 void
connect_child(const int fd)253 impl::stream_capture::connect_child(const int fd)
254 {
255     ::close(m_pipefds[0]); m_pipefds[0] = -1;
256     if (m_pipefds[1] != fd) {
257         safe_dup(m_pipefds[1], fd);
258     }
259     m_pipefds[1] = -1;
260 }
261 
stream_connect(const int src_fd,const int tgt_fd)262 impl::stream_connect::stream_connect(const int src_fd, const int tgt_fd) :
263     m_src_fd(src_fd), m_tgt_fd(tgt_fd)
264 {
265 }
266 
267 void
prepare(void)268 impl::stream_connect::prepare(void)
269 {
270 }
271 
272 int
connect_parent(void)273 impl::stream_connect::connect_parent(void)
274 {
275     return -1;
276 }
277 
278 void
connect_child(const int fd)279 impl::stream_connect::connect_child(const int fd __attribute__((__unused__)))
280 {
281     safe_dup(m_tgt_fd, m_src_fd);
282 }
283 
stream_inherit(void)284 impl::stream_inherit::stream_inherit(void)
285 {
286 }
287 
288 void
prepare(void)289 impl::stream_inherit::prepare(void)
290 {
291 }
292 
293 int
connect_parent(void)294 impl::stream_inherit::connect_parent(void)
295 {
296     return -1;
297 }
298 
299 void
connect_child(const int fd)300 impl::stream_inherit::connect_child(const int fd __attribute__((__unused__)))
301 {
302 }
303 
stream_redirect_fd(const int fd)304 impl::stream_redirect_fd::stream_redirect_fd(const int fd) :
305     m_fd(fd)
306 {
307 }
308 
309 void
prepare(void)310 impl::stream_redirect_fd::prepare(void)
311 {
312 }
313 
314 int
connect_parent(void)315 impl::stream_redirect_fd::connect_parent(void)
316 {
317     return -1;
318 }
319 
320 void
connect_child(const int fd)321 impl::stream_redirect_fd::connect_child(const int fd)
322 {
323     safe_dup(m_fd, fd);
324 }
325 
stream_redirect_path(const tools::fs::path & p)326 impl::stream_redirect_path::stream_redirect_path(const tools::fs::path& p) :
327     m_path(p)
328 {
329 }
330 
331 void
prepare(void)332 impl::stream_redirect_path::prepare(void)
333 {
334 }
335 
336 int
connect_parent(void)337 impl::stream_redirect_path::connect_parent(void)
338 {
339     return -1;
340 }
341 
342 void
connect_child(const int fd)343 impl::stream_redirect_path::connect_child(const int fd)
344 {
345     const int aux = ::open(m_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
346     if (aux == -1)
347         throw system_error(IMPL_NAME "::stream_redirect_path::connect_child",
348                            "Could not create " + m_path.str(), errno);
349     else
350         safe_dup(aux, fd);
351 }
352 
353 // ------------------------------------------------------------------------
354 // The "status" type.
355 // ------------------------------------------------------------------------
356 
status(int s)357 impl::status::status(int s) :
358     m_status(s)
359 {
360 }
361 
~status(void)362 impl::status::~status(void)
363 {
364 }
365 
366 bool
exited(void) const367 impl::status::exited(void)
368     const
369 {
370     int mutable_status = m_status;
371     return WIFEXITED(mutable_status);
372 }
373 
374 int
exitstatus(void) const375 impl::status::exitstatus(void)
376     const
377 {
378     assert(exited());
379     int mutable_status = m_status;
380     return WEXITSTATUS(mutable_status);
381 }
382 
383 bool
signaled(void) const384 impl::status::signaled(void)
385     const
386 {
387     int mutable_status = m_status;
388     return WIFSIGNALED(mutable_status);
389 }
390 
391 int
termsig(void) const392 impl::status::termsig(void)
393     const
394 {
395     assert(signaled());
396     int mutable_status = m_status;
397     return WTERMSIG(mutable_status);
398 }
399 
400 bool
coredump(void) const401 impl::status::coredump(void)
402     const
403 {
404     assert(signaled());
405     int mutable_status = m_status;
406     return WCOREDUMP(mutable_status);
407 }
408 
409 // ------------------------------------------------------------------------
410 // The "child" type.
411 // ------------------------------------------------------------------------
412 
child(const pid_t pid_arg,const int stdout_fd_arg,const int stderr_fd_arg)413 impl::child::child(const pid_t pid_arg, const int stdout_fd_arg,
414                    const int stderr_fd_arg) :
415     m_pid(pid_arg),
416     m_stdout(stdout_fd_arg),
417     m_stderr(stderr_fd_arg),
418     m_waited(false)
419 {
420 }
421 
~child(void)422 impl::child::~child(void)
423 {
424     if (!m_waited) {
425         ::kill(m_pid, SIGTERM);
426         (void)wait();
427 
428         if (m_stdout != -1)
429             ::close(m_stdout);
430         if (m_stderr != -1)
431             ::close(m_stderr);
432     }
433 }
434 
435 impl::status
wait(void)436 impl::child::wait(void)
437 {
438     int s;
439 
440     if (::waitpid(m_pid, &s, 0) == -1)
441         throw system_error(IMPL_NAME "::child::wait", "Failed waiting for "
442                            "process " + text::to_string(m_pid), errno);
443 
444     if (m_stdout != -1)
445         ::close(m_stdout); m_stdout = -1;
446     if (m_stderr != -1)
447         ::close(m_stderr); m_stderr = -1;
448 
449     m_waited = true;
450     return status(s);
451 }
452 
453 pid_t
pid(void) const454 impl::child::pid(void)
455     const
456 {
457     return m_pid;
458 }
459 
460 int
stdout_fd(void)461 impl::child::stdout_fd(void)
462 {
463     return m_stdout;
464 }
465 
466 int
stderr_fd(void)467 impl::child::stderr_fd(void)
468 {
469     return m_stderr;
470 }
471 
472 // ------------------------------------------------------------------------
473 // Free functions.
474 // ------------------------------------------------------------------------
475 
476 void
flush_streams(void)477 detail::flush_streams(void)
478 {
479     // This is a weird hack to ensure that the output of the parent process
480     // is flushed before executing a child which prevents, for example, the
481     // output of the atf-run hooks to appear before the output of atf-run
482     // itself.
483     //
484     // TODO: This should only be executed when inheriting the stdout or
485     // stderr file descriptors.  However, the flushing is specific to the
486     // iostreams, so we cannot do it from the C library where all the process
487     // logic is performed.  Come up with a better design.
488     std::cout.flush();
489     std::cerr.flush();
490 }
491