]>
Commit | Line | Data |
---|---|---|
b32b8144 FG |
1 | // Copyright (c) 2006, 2007 Julio M. Merino Vidal |
2 | // Copyright (c) 2008 Ilya Sokolov, Boris Schaeling | |
3 | // Copyright (c) 2009 Boris Schaeling | |
4 | // Copyright (c) 2010 Felipe Tanus, Boris Schaeling | |
5 | // Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling | |
6 | // | |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
9 | ||
10 | #ifndef BOOST_PROCESS_DETAIL_POSIX_EXECUTOR_HPP | |
11 | #define BOOST_PROCESS_DETAIL_POSIX_EXECUTOR_HPP | |
12 | ||
13 | #include <boost/process/detail/child_decl.hpp> | |
14 | #include <boost/process/error.hpp> | |
15 | #include <boost/process/pipe.hpp> | |
16 | #include <boost/process/detail/posix/basic_pipe.hpp> | |
17 | #include <boost/process/detail/posix/use_vfork.hpp> | |
18 | #include <boost/fusion/algorithm/iteration/for_each.hpp> | |
19 | #include <cstdlib> | |
20 | #include <sys/types.h> | |
21 | #include <fcntl.h> | |
22 | #include <errno.h> | |
23 | #include <unistd.h> | |
24 | ||
b32b8144 FG |
25 | #include <boost/algorithm/string/predicate.hpp> |
26 | #include <boost/algorithm/string/split.hpp> | |
27 | #include <boost/algorithm/string/classification.hpp> | |
b32b8144 FG |
28 | |
29 | namespace boost { namespace process { namespace detail { namespace posix { | |
30 | ||
b32b8144 FG |
31 | template<typename Executor> |
32 | struct on_setup_t | |
33 | { | |
34 | Executor & exec; | |
35 | on_setup_t(Executor & exec) : exec(exec) {}; | |
36 | template<typename T> | |
37 | void operator()(T & t) const | |
38 | { | |
39 | if (!exec.error()) | |
40 | t.on_setup(exec); | |
41 | } | |
42 | }; | |
43 | ||
44 | template<typename Executor> | |
45 | struct on_error_t | |
46 | { | |
47 | Executor & exec; | |
48 | const std::error_code & error; | |
49 | on_error_t(Executor & exec, const std::error_code & error) : exec(exec), error(error) {}; | |
50 | template<typename T> | |
51 | void operator()(T & t) const | |
52 | { | |
92f5a8d4 | 53 | t.on_error(exec, error); |
b32b8144 FG |
54 | } |
55 | }; | |
56 | ||
57 | template<typename Executor> | |
58 | struct on_success_t | |
59 | { | |
60 | Executor & exec; | |
61 | on_success_t(Executor & exec) : exec(exec) {}; | |
62 | template<typename T> | |
63 | void operator()(T & t) const {t.on_success(exec);} | |
64 | }; | |
65 | ||
66 | ||
67 | ||
68 | template<typename Executor> | |
69 | struct on_fork_error_t | |
70 | { | |
71 | Executor & exec; | |
72 | const std::error_code & error; | |
73 | on_fork_error_t(Executor & exec, const std::error_code & error) : exec(exec), error(error) {}; | |
74 | ||
75 | template<typename T> | |
76 | void operator()(T & t) const | |
77 | { | |
78 | t.on_fork_error(exec, error); | |
79 | } | |
80 | }; | |
81 | ||
82 | ||
83 | template<typename Executor> | |
84 | struct on_exec_setup_t | |
85 | { | |
86 | Executor & exec; | |
87 | on_exec_setup_t(Executor & exec) : exec(exec) {}; | |
88 | ||
89 | template<typename T> | |
90 | void operator()(T & t) const | |
91 | { | |
92 | t.on_exec_setup(exec); | |
93 | } | |
94 | }; | |
95 | ||
96 | ||
97 | template<typename Executor> | |
98 | struct on_exec_error_t | |
99 | { | |
100 | Executor & exec; | |
101 | const std::error_code &ec; | |
102 | on_exec_error_t(Executor & exec, const std::error_code & error) : exec(exec), ec(error) {}; | |
103 | ||
104 | template<typename T> | |
105 | void operator()(T & t) const | |
106 | { | |
107 | t.on_exec_error(exec, ec); | |
108 | } | |
109 | }; | |
110 | ||
111 | template<typename Executor> | |
112 | struct on_fork_success_t | |
113 | { | |
114 | Executor & exec; | |
115 | on_fork_success_t(Executor & exec) : exec(exec) {}; | |
116 | ||
117 | template<typename T> | |
118 | void operator()(T & t) const | |
119 | { | |
120 | t.on_fork_success(exec); | |
121 | } | |
122 | }; | |
123 | ||
124 | template<typename Executor> on_setup_t <Executor> call_on_setup (Executor & exec) {return exec;} | |
92f5a8d4 | 125 | template<typename Executor> on_error_t <Executor> call_on_error (Executor & exec, const std::error_code & ec) |
b32b8144 FG |
126 | { |
127 | return on_error_t<Executor> (exec, ec); | |
128 | } | |
129 | template<typename Executor> on_success_t<Executor> call_on_success(Executor & exec) {return exec;} | |
130 | ||
92f5a8d4 | 131 | template<typename Executor> on_fork_error_t <Executor> call_on_fork_error (Executor & exec, const std::error_code & ec) |
b32b8144 FG |
132 | { |
133 | return on_fork_error_t<Executor> (exec, ec); | |
134 | } | |
135 | ||
136 | ||
137 | template<typename Executor> on_exec_setup_t <Executor> call_on_exec_setup (Executor & exec) {return exec;} | |
138 | template<typename Executor> on_exec_error_t <Executor> call_on_exec_error (Executor & exec, const std::error_code & ec) | |
139 | { | |
140 | return on_exec_error_t<Executor> (exec, ec); | |
141 | } | |
142 | ||
143 | ||
144 | template<typename Sequence> | |
145 | class executor | |
146 | { | |
147 | template<typename HasHandler, typename UseVFork> | |
148 | void internal_error_handle(const std::error_code&, const char*, HasHandler, boost::mpl::true_, UseVFork) {} | |
149 | ||
150 | int _pipe_sink = -1; | |
151 | ||
152 | void write_error(const std::error_code & ec, const char * msg) | |
153 | { | |
154 | //I am the child | |
155 | int len = ec.value(); | |
156 | ::write(_pipe_sink, &len, sizeof(int)); | |
157 | ||
158 | len = std::strlen(msg) + 1; | |
159 | ::write(_pipe_sink, &len, sizeof(int)); | |
160 | ::write(_pipe_sink, msg, len); | |
161 | } | |
162 | ||
163 | void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::true_ , boost::mpl::false_, boost::mpl::false_) | |
164 | { | |
165 | if (this->pid == 0) //on the fork. | |
166 | write_error(ec, msg); | |
167 | else | |
168 | { | |
169 | this->_ec = ec; | |
170 | this->_msg = msg; | |
171 | } | |
172 | } | |
173 | void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::false_, boost::mpl::false_) | |
174 | { | |
175 | if (this->pid == 0) | |
176 | write_error(ec, msg); | |
177 | else | |
178 | throw process_error(ec, msg); | |
179 | } | |
180 | ||
181 | ||
182 | void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::true_ , boost::mpl::false_, boost::mpl::true_) | |
183 | { | |
184 | this->_ec = ec; | |
185 | this->_msg = msg; | |
186 | } | |
187 | void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::false_, boost::mpl::true_) | |
188 | { | |
189 | if (this->pid == 0) | |
190 | { | |
191 | this->_ec = ec; | |
192 | this->_msg = msg; | |
193 | } | |
194 | else | |
195 | throw process_error(ec, msg); | |
196 | } | |
197 | ||
198 | void check_error(boost::mpl::true_) {}; | |
199 | void check_error(boost::mpl::false_) | |
200 | { | |
201 | if (_ec) | |
202 | throw process_error(_ec, _msg); | |
203 | } | |
204 | ||
205 | typedef typename ::boost::process::detail::has_error_handler<Sequence>::type has_error_handler; | |
206 | typedef typename ::boost::process::detail::has_ignore_error <Sequence>::type has_ignore_error; | |
207 | typedef typename ::boost::process::detail::posix::shall_use_vfork<Sequence>::type shall_use_vfork; | |
208 | ||
209 | inline child invoke(boost::mpl::true_ , boost::mpl::true_ ); | |
210 | inline child invoke(boost::mpl::false_, boost::mpl::true_ ); | |
211 | inline child invoke(boost::mpl::true_ , boost::mpl::false_ ); | |
212 | inline child invoke(boost::mpl::false_, boost::mpl::false_ ); | |
213 | void _write_error(int sink) | |
214 | { | |
215 | int data[2] = {_ec.value(),static_cast<int>(_msg.size())}; | |
216 | while (::write(sink, &data[0], sizeof(int) *2) == -1) | |
217 | { | |
218 | auto err = errno; | |
219 | ||
220 | if (err == EBADF) | |
221 | return; | |
222 | else if ((err != EINTR) && (err != EAGAIN)) | |
223 | break; | |
224 | } | |
225 | while (::write(sink, &_msg.front(), _msg.size()) == -1) | |
226 | { | |
227 | auto err = errno; | |
228 | ||
229 | if (err == EBADF) | |
230 | return; | |
231 | else if ((err != EINTR) && (err != EAGAIN)) | |
232 | break; | |
233 | } | |
234 | } | |
235 | ||
236 | void _read_error(int source) | |
237 | { | |
238 | int data[2]; | |
239 | ||
240 | _ec.clear(); | |
241 | int count = 0; | |
242 | while ((count = ::read(source, &data[0], sizeof(int) *2 ) ) == -1) | |
243 | { | |
244 | //actually, this should block until it's read. | |
245 | auto err = errno; | |
246 | if ((err != EAGAIN ) && (err != EINTR)) | |
247 | set_error(std::error_code(err, std::system_category()), "Error read pipe"); | |
248 | } | |
249 | if (count == 0) | |
250 | return ; | |
251 | ||
252 | std::error_code ec(data[0], std::system_category()); | |
253 | std::string msg(data[1], ' '); | |
254 | ||
255 | while (::read(source, &msg.front(), msg.size() ) == -1) | |
256 | { | |
257 | //actually, this should block until it's read. | |
258 | auto err = errno; | |
259 | if ((err == EBADF) || (err == EPERM))//that should occur on success, therefore return. | |
260 | return; | |
92f5a8d4 | 261 | //EAGAIN not yet forked, EINTR interrupted, i.e. try again |
b32b8144 FG |
262 | else if ((err != EAGAIN ) && (err != EINTR)) |
263 | set_error(std::error_code(err, std::system_category()), "Error read pipe"); | |
264 | } | |
265 | set_error(ec, std::move(msg)); | |
266 | } | |
267 | ||
92f5a8d4 TL |
268 | std::string prepare_cmd_style_fn; //buffer |
269 | ||
270 | inline void prepare_cmd_style() //this does what execvpe does - but we execute it in the father process, to avoid allocations. | |
271 | { | |
272 | //use my own implementation | |
273 | prepare_cmd_style_fn = exe; | |
274 | if ((prepare_cmd_style_fn.find('/') == std::string::npos) && ::access(prepare_cmd_style_fn.c_str(), X_OK)) | |
275 | { | |
276 | auto e = ::environ; | |
277 | while ((*e != nullptr) && !boost::starts_with(*e, "PATH=")) | |
278 | e++; | |
279 | ||
280 | if (e != nullptr) | |
281 | { | |
282 | std::vector<std::string> path; | |
283 | boost::split(path, *e, boost::is_any_of(":")); | |
284 | ||
285 | for (const std::string & pp : path) | |
286 | { | |
287 | auto p = pp + "/" + exe; | |
288 | if (!::access(p.c_str(), X_OK)) | |
289 | { | |
290 | prepare_cmd_style_fn = p; | |
291 | break; | |
292 | } | |
293 | } | |
294 | } | |
295 | } | |
296 | exe = prepare_cmd_style_fn.c_str(); | |
297 | } | |
b32b8144 FG |
298 | |
299 | std::error_code _ec; | |
300 | std::string _msg; | |
301 | public: | |
302 | executor(Sequence & seq) : seq(seq) | |
303 | { | |
304 | } | |
305 | ||
306 | child operator()() | |
307 | { | |
308 | return invoke(has_ignore_error(), shall_use_vfork()); | |
309 | } | |
310 | ||
311 | ||
312 | Sequence & seq; | |
313 | const char * exe = nullptr; | |
314 | char *const* cmd_line = nullptr; | |
315 | bool cmd_style = false; | |
316 | char **env = ::environ; | |
317 | pid_t pid = -1; | |
318 | std::shared_ptr<std::atomic<int>> exit_status = std::make_shared<std::atomic<int>>(still_active); | |
319 | ||
320 | const std::error_code & error() const {return _ec;} | |
321 | ||
322 | void set_error(const std::error_code &ec, const char* msg) | |
323 | { | |
324 | internal_error_handle(ec, msg, has_error_handler(), has_ignore_error(), shall_use_vfork()); | |
325 | } | |
326 | void set_error(const std::error_code &ec, const std::string &msg) {set_error(ec, msg.c_str());}; | |
327 | ||
328 | }; | |
329 | ||
330 | template<typename Sequence> | |
331 | child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore errors | |
332 | { | |
333 | boost::fusion::for_each(seq, call_on_setup(*this)); | |
334 | if (_ec) | |
335 | return child(); | |
92f5a8d4 TL |
336 | if (cmd_style) |
337 | prepare_cmd_style(); | |
b32b8144 FG |
338 | |
339 | this->pid = ::fork(); | |
340 | if (pid == -1) | |
341 | { | |
342 | auto ec = boost::process::detail::get_last_error(); | |
343 | boost::fusion::for_each(seq, call_on_fork_error(*this, ec)); | |
344 | return child(); | |
345 | } | |
346 | else if (pid == 0) | |
347 | { | |
348 | boost::fusion::for_each(seq, call_on_exec_setup(*this)); | |
92f5a8d4 | 349 | ::execve(exe, cmd_line, env); |
b32b8144 FG |
350 | auto ec = boost::process::detail::get_last_error(); |
351 | boost::fusion::for_each(seq, call_on_exec_error(*this, ec)); | |
352 | _exit(EXIT_FAILURE); | |
353 | } | |
354 | ||
355 | child c(child_handle(pid), exit_status); | |
356 | ||
357 | boost::fusion::for_each(seq, call_on_success(*this)); | |
358 | ||
359 | return c; | |
360 | } | |
361 | ||
362 | template<typename Sequence> | |
363 | child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_) | |
364 | { | |
b32b8144 | 365 | { |
92f5a8d4 TL |
366 | struct pipe_guard |
367 | { | |
368 | int p[2]; | |
369 | pipe_guard() : p{-1,-1} {} | |
b32b8144 | 370 | |
92f5a8d4 TL |
371 | ~pipe_guard() |
372 | { | |
373 | if (p[0] != -1) | |
374 | ::close(p[0]); | |
375 | if (p[1] != -1) | |
376 | ::close(p[1]); | |
377 | } | |
378 | } p{}; | |
b32b8144 | 379 | |
92f5a8d4 TL |
380 | if (::pipe(p.p) == -1) |
381 | { | |
382 | set_error(::boost::process::detail::get_last_error(), "pipe(2) failed"); | |
383 | return child(); | |
384 | } | |
385 | if (::fcntl(p.p[1], F_SETFD, FD_CLOEXEC) == -1) | |
386 | { | |
387 | auto err = ::boost::process::detail::get_last_error(); | |
388 | set_error(err, "fcntl(2) failed");//this might throw, so we need to be sure our pipe is safe. | |
389 | return child(); | |
390 | } | |
391 | _ec.clear(); | |
392 | boost::fusion::for_each(seq, call_on_setup(*this)); | |
b32b8144 | 393 | |
92f5a8d4 TL |
394 | if (_ec) |
395 | { | |
396 | boost::fusion::for_each(seq, call_on_error(*this, _ec)); | |
397 | return child(); | |
398 | } | |
b32b8144 | 399 | |
b32b8144 | 400 | if (cmd_style) |
92f5a8d4 | 401 | prepare_cmd_style(); |
b32b8144 | 402 | |
92f5a8d4 TL |
403 | this->pid = ::fork(); |
404 | if (pid == -1) | |
405 | { | |
406 | _ec = boost::process::detail::get_last_error(); | |
407 | _msg = "fork() failed"; | |
408 | boost::fusion::for_each(seq, call_on_fork_error(*this, _ec)); | |
409 | boost::fusion::for_each(seq, call_on_error(*this, _ec)); | |
410 | return child(); | |
411 | } | |
412 | else if (pid == 0) | |
413 | { | |
414 | _pipe_sink = p.p[1]; | |
415 | ::close(p.p[0]); | |
b32b8144 | 416 | |
92f5a8d4 TL |
417 | boost::fusion::for_each(seq, call_on_exec_setup(*this)); |
418 | ::execve(exe, cmd_line, env); | |
419 | _ec = boost::process::detail::get_last_error(); | |
420 | _msg = "execve failed"; | |
421 | boost::fusion::for_each(seq, call_on_exec_error(*this, _ec)); | |
b32b8144 | 422 | |
92f5a8d4 TL |
423 | _write_error(_pipe_sink); |
424 | ::close(p.p[1]); | |
b32b8144 | 425 | |
92f5a8d4 TL |
426 | _exit(EXIT_FAILURE); |
427 | return child(); | |
428 | } | |
b32b8144 | 429 | |
92f5a8d4 TL |
430 | ::close(p.p[1]); |
431 | p.p[1] = -1; | |
432 | _read_error(p.p[0]); | |
b32b8144 | 433 | |
92f5a8d4 | 434 | } |
b32b8144 FG |
435 | if (_ec) |
436 | { | |
437 | boost::fusion::for_each(seq, call_on_error(*this, _ec)); | |
438 | return child(); | |
439 | } | |
92f5a8d4 TL |
440 | |
441 | child c(child_handle(pid), exit_status); | |
442 | ||
443 | boost::fusion::for_each(seq, call_on_success(*this)); | |
b32b8144 FG |
444 | |
445 | if (_ec) | |
446 | { | |
447 | boost::fusion::for_each(seq, call_on_error(*this, _ec)); | |
448 | return child(); | |
449 | } | |
450 | ||
451 | return c; | |
452 | } | |
453 | ||
454 | #if BOOST_POSIX_HAS_VFORK | |
455 | ||
456 | ||
457 | template<typename Sequence> | |
458 | child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::true_) //ignore errors | |
459 | { | |
460 | boost::fusion::for_each(seq, call_on_setup(*this)); | |
461 | if (_ec) | |
462 | return child(); | |
463 | this->pid = ::vfork(); | |
464 | if (pid == -1) | |
465 | { | |
466 | auto ec = boost::process::detail::get_last_error(); | |
467 | boost::fusion::for_each(seq, call_on_fork_error(*this, ec)); | |
468 | return child(); | |
469 | } | |
470 | else if (pid == 0) | |
471 | { | |
472 | boost::fusion::for_each(seq, call_on_exec_setup(*this)); | |
473 | ::execve(exe, cmd_line, env); | |
474 | auto ec = boost::process::detail::get_last_error(); | |
475 | boost::fusion::for_each(seq, call_on_exec_error(*this, ec)); | |
476 | _exit(EXIT_FAILURE); | |
477 | } | |
478 | child c(child_handle(pid), exit_status); | |
479 | ||
480 | boost::fusion::for_each(seq, call_on_success(*this)); | |
481 | ||
482 | return c; | |
483 | } | |
484 | ||
485 | template<typename Sequence> | |
486 | child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_) | |
487 | { | |
488 | boost::fusion::for_each(seq, call_on_setup(*this)); | |
489 | ||
490 | if (_ec) | |
491 | { | |
492 | boost::fusion::for_each(seq, call_on_error(*this, _ec)); | |
493 | return child(); | |
494 | } | |
495 | _ec.clear(); | |
92f5a8d4 TL |
496 | if (cmd_style) |
497 | this->prepare_cmd_style(); | |
b32b8144 FG |
498 | |
499 | this->pid = ::vfork(); | |
500 | if (pid == -1) | |
501 | { | |
502 | _ec = boost::process::detail::get_last_error(); | |
503 | _msg = "fork() failed"; | |
504 | boost::fusion::for_each(seq, call_on_fork_error(*this, _ec)); | |
505 | boost::fusion::for_each(seq, call_on_error(*this, _ec)); | |
506 | ||
507 | return child(); | |
508 | } | |
509 | else if (pid == 0) | |
510 | { | |
511 | boost::fusion::for_each(seq, call_on_exec_setup(*this)); | |
512 | ||
92f5a8d4 | 513 | ::execve(exe, cmd_line, env); |
b32b8144 FG |
514 | |
515 | _ec = boost::process::detail::get_last_error(); | |
516 | _msg = "execve failed"; | |
517 | boost::fusion::for_each(seq, call_on_exec_error(*this, _ec)); | |
518 | ||
519 | _exit(EXIT_FAILURE); | |
520 | return child(); | |
521 | } | |
522 | child c(child_handle(pid), exit_status); | |
523 | ||
524 | check_error(has_error_handler()); | |
525 | ||
526 | ||
527 | ||
528 | if (_ec) | |
529 | { | |
530 | boost::fusion::for_each(seq, call_on_error(*this, _ec)); | |
531 | return child(); | |
532 | } | |
533 | else | |
534 | boost::fusion::for_each(seq, call_on_success(*this)); | |
535 | ||
536 | if (_ec) | |
537 | { | |
538 | boost::fusion::for_each(seq, call_on_error(*this, _ec)); | |
539 | return child(); | |
540 | } | |
541 | ||
542 | return c; | |
543 | } | |
544 | ||
545 | #endif | |
546 | ||
547 | template<typename Char, typename Tup> | |
548 | inline executor<Tup> make_executor(Tup & tup) | |
549 | { | |
550 | return executor<Tup>(tup); | |
551 | } | |
552 | ||
553 | }}}} | |
554 | ||
555 | #endif |