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