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 #if !defined(TOOLS_PROCESS_HPP) 31 #define TOOLS_PROCESS_HPP 32 33 extern "C" { 34 #include <sys/types.h> 35 36 #include <unistd.h> 37 } 38 39 #include <cerrno> 40 #include <cstdlib> 41 #include <iostream> 42 #include <string> 43 #include <vector> 44 45 #include "auto_array.hpp" 46 #include "exceptions.hpp" 47 #include "fs.hpp" 48 49 namespace tools { 50 namespace process { 51 52 class child; 53 class status; 54 55 // ------------------------------------------------------------------------ 56 // The "argv_array" type. 57 // ------------------------------------------------------------------------ 58 59 class argv_array { 60 typedef std::vector< std::string > args_vector; 61 args_vector m_args; 62 63 // TODO: This is immutable, so we should be able to use 64 // std::tr1::shared_array instead when it becomes widely available. 65 // The reason would be to remove all copy constructors and assignment 66 // operators from this class. 67 auto_array< const char* > m_exec_argv; 68 void ctor_init_exec_argv(void); 69 70 public: 71 typedef args_vector::const_iterator const_iterator; 72 typedef args_vector::size_type size_type; 73 74 argv_array(void); 75 argv_array(const char*, ...); 76 explicit argv_array(const char* const*); 77 template< class C > explicit argv_array(const C&); 78 argv_array(const argv_array&); 79 80 const char* const* exec_argv(void) const; 81 size_type size(void) const; 82 const char* operator[](int) const; 83 84 const_iterator begin(void) const; 85 const_iterator end(void) const; 86 87 argv_array& operator=(const argv_array&); 88 }; 89 90 template< class C > 91 argv_array::argv_array(const C& c) 92 { 93 for (typename C::const_iterator iter = c.begin(); iter != c.end(); 94 iter++) 95 m_args.push_back(*iter); 96 ctor_init_exec_argv(); 97 } 98 99 // ------------------------------------------------------------------------ 100 // The "stream" types. 101 // ------------------------------------------------------------------------ 102 103 class stream_capture { 104 int m_pipefds[2]; 105 106 // Allow access to the getters. 107 template< class OutStream, class ErrStream > friend 108 child fork(void (*)(void*), OutStream, ErrStream, void*); 109 template< class OutStream, class ErrStream > friend 110 status exec(const tools::fs::path&, const argv_array&, 111 const OutStream&, const ErrStream&, void (*)(void)); 112 113 void prepare(void); 114 int connect_parent(void); 115 void connect_child(const int); 116 117 public: 118 stream_capture(void); 119 ~stream_capture(void); 120 }; 121 122 class stream_connect { 123 int m_src_fd; 124 int m_tgt_fd; 125 126 // Allow access to the getters. 127 template< class OutStream, class ErrStream > friend 128 child fork(void (*)(void*), OutStream, ErrStream, void*); 129 template< class OutStream, class ErrStream > friend 130 status exec(const tools::fs::path&, const argv_array&, 131 const OutStream&, const ErrStream&, void (*)(void)); 132 133 void prepare(void); 134 int connect_parent(void); 135 void connect_child(const int); 136 137 public: 138 stream_connect(const int, const int); 139 }; 140 141 class stream_inherit { 142 // Allow access to the getters. 143 template< class OutStream, class ErrStream > friend 144 child fork(void (*)(void*), OutStream, ErrStream, void*); 145 template< class OutStream, class ErrStream > friend 146 status exec(const tools::fs::path&, const argv_array&, 147 const OutStream&, const ErrStream&, void (*)(void)); 148 149 void prepare(void); 150 int connect_parent(void); 151 void connect_child(const int); 152 153 public: 154 stream_inherit(void); 155 }; 156 157 class stream_redirect_fd { 158 int m_fd; 159 160 // Allow access to the getters. 161 template< class OutStream, class ErrStream > friend 162 child fork(void (*)(void*), OutStream, ErrStream, void*); 163 template< class OutStream, class ErrStream > friend 164 status exec(const tools::fs::path&, const argv_array&, 165 const OutStream&, const ErrStream&, void (*)(void)); 166 167 void prepare(void); 168 int connect_parent(void); 169 void connect_child(const int); 170 171 public: 172 stream_redirect_fd(const int); 173 }; 174 175 class stream_redirect_path { 176 const tools::fs::path m_path; 177 178 // Allow access to the getters. 179 template< class OutStream, class ErrStream > friend 180 child fork(void (*)(void*), OutStream, ErrStream, void*); 181 template< class OutStream, class ErrStream > friend 182 status exec(const tools::fs::path&, const argv_array&, 183 const OutStream&, const ErrStream&, void (*)(void)); 184 185 void prepare(void); 186 int connect_parent(void); 187 void connect_child(const int); 188 189 public: 190 stream_redirect_path(const tools::fs::path&); 191 }; 192 193 // ------------------------------------------------------------------------ 194 // The "status" type. 195 // ------------------------------------------------------------------------ 196 197 class status { 198 int m_status; 199 200 friend class child; 201 template< class OutStream, class ErrStream > friend 202 status exec(const tools::fs::path&, const argv_array&, 203 const OutStream&, const ErrStream&, void (*)(void)); 204 205 status(int); 206 207 public: 208 ~status(void); 209 210 std::string str(void) const; 211 212 bool exited(void) const; 213 int exitstatus(void) const; 214 215 bool signaled(void) const; 216 int termsig(void) const; 217 bool coredump(void) const; 218 }; 219 220 // ------------------------------------------------------------------------ 221 // The "child" type. 222 // ------------------------------------------------------------------------ 223 224 class child { 225 pid_t m_pid; 226 227 int m_stdout; 228 int m_stderr; 229 230 bool m_waited; 231 232 template< class OutStream, class ErrStream > friend 233 child fork(void (*)(void*), OutStream, ErrStream, void*); 234 235 child(const pid_t, const int, const int); 236 237 public: 238 ~child(void); 239 240 status wait(void); 241 242 pid_t pid(void) const; 243 int stdout_fd(void); 244 int stderr_fd(void); 245 }; 246 247 // ------------------------------------------------------------------------ 248 // Free functions. 249 // ------------------------------------------------------------------------ 250 251 namespace detail { 252 void flush_streams(void); 253 254 struct exec_args { 255 const tools::fs::path m_prog; 256 const argv_array& m_argv; 257 void (*m_prehook)(void); 258 }; 259 260 void do_exec(void *); 261 } // namespace detail 262 263 // TODO: The void* cookie can probably be templatized, thus also allowing 264 // const data structures. 265 template< class OutStream, class ErrStream > 266 child 267 fork(void (*start)(void*), OutStream outsb, ErrStream errsb, void* v) 268 { 269 detail::flush_streams(); 270 271 outsb.prepare(); 272 errsb.prepare(); 273 274 pid_t pid = ::fork(); 275 if (pid == -1) { 276 throw system_error("tools::process::child::fork", 277 "Failed to fork", errno); 278 } else if (pid == 0) { 279 try { 280 outsb.connect_child(STDOUT_FILENO); 281 errsb.connect_child(STDERR_FILENO); 282 start(v); 283 std::abort(); 284 } catch (...) { 285 std::cerr << "Unhandled error while running subprocess\n"; 286 std::exit(EXIT_FAILURE); 287 } 288 } else { 289 const int stdout_fd = outsb.connect_parent(); 290 const int stderr_fd = errsb.connect_parent(); 291 return child(pid, stdout_fd, stderr_fd); 292 } 293 } 294 295 template< class OutStream, class ErrStream > 296 status 297 exec(const tools::fs::path& prog, const argv_array& argv, 298 const OutStream& outsb, const ErrStream& errsb, 299 void (*prehook)(void)) 300 { 301 struct detail::exec_args ea = { prog, argv, prehook }; 302 child c = fork(detail::do_exec, outsb, errsb, &ea); 303 304 again: 305 try { 306 return c.wait(); 307 } catch (const system_error& e) { 308 if (e.code() == EINTR) 309 goto again; 310 else 311 throw e; 312 } 313 } 314 315 template< class OutStream, class ErrStream > 316 status 317 exec(const tools::fs::path& prog, const argv_array& argv, 318 const OutStream& outsb, const ErrStream& errsb) 319 { 320 return exec(prog, argv, outsb, errsb, NULL); 321 } 322 323 } // namespace process 324 } // namespace tools 325 326 #endif // !defined(TOOLS_PROCESS_HPP) 327