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 #include <boost/algorithm/string/predicate.hpp>
26 #include <boost/algorithm/string/split.hpp>
27 #include <boost/algorithm/string/classification.hpp>
28 
29 namespace boost { namespace process { namespace detail { namespace posix {
30 
31 template<typename Executor>
32 struct on_setup_t
33 {
34     Executor & exec;
on_setup_tboost::process::detail::posix::on_setup_t35     on_setup_t(Executor & exec) : exec(exec) {};
36     template<typename T>
operator ()boost::process::detail::posix::on_setup_t37     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;
on_error_tboost::process::detail::posix::on_error_t49     on_error_t(Executor & exec, const std::error_code & error) : exec(exec), error(error) {};
50     template<typename T>
operator ()boost::process::detail::posix::on_error_t51     void operator()(T & t) const
52     {
53         t.on_error(exec, error);
54     }
55 };
56 
57 template<typename Executor>
58 struct on_success_t
59 {
60     Executor & exec;
on_success_tboost::process::detail::posix::on_success_t61     on_success_t(Executor & exec) : exec(exec) {};
62     template<typename T>
operator ()boost::process::detail::posix::on_success_t63     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;
on_fork_error_tboost::process::detail::posix::on_fork_error_t73     on_fork_error_t(Executor & exec, const std::error_code & error) : exec(exec), error(error) {};
74 
75     template<typename T>
operator ()boost::process::detail::posix::on_fork_error_t76     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;
on_exec_setup_tboost::process::detail::posix::on_exec_setup_t87     on_exec_setup_t(Executor & exec) : exec(exec) {};
88 
89     template<typename T>
operator ()boost::process::detail::posix::on_exec_setup_t90     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;
on_exec_error_tboost::process::detail::posix::on_exec_error_t102     on_exec_error_t(Executor & exec, const std::error_code & error) : exec(exec), ec(error) {};
103 
104     template<typename T>
operator ()boost::process::detail::posix::on_exec_error_t105     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;
on_fork_success_tboost::process::detail::posix::on_fork_success_t115     on_fork_success_t(Executor & exec) : exec(exec) {};
116 
117     template<typename T>
operator ()boost::process::detail::posix::on_fork_success_t118     void operator()(T & t) const
119     {
120         t.on_fork_success(exec);
121     }
122 };
123 
call_on_setup(Executor & exec)124 template<typename Executor> on_setup_t  <Executor> call_on_setup  (Executor & exec) {return exec;}
call_on_error(Executor & exec,const std::error_code & ec)125 template<typename Executor> on_error_t  <Executor> call_on_error  (Executor & exec, const std::error_code & ec)
126 {
127     return on_error_t<Executor> (exec, ec);
128 }
call_on_success(Executor & exec)129 template<typename Executor> on_success_t<Executor> call_on_success(Executor & exec) {return exec;}
130 
call_on_fork_error(Executor & exec,const std::error_code & ec)131 template<typename Executor> on_fork_error_t  <Executor> call_on_fork_error  (Executor & exec, const std::error_code & ec)
132 {
133     return on_fork_error_t<Executor> (exec, ec);
134 }
135 
136 
call_on_exec_setup(Executor & exec)137 template<typename Executor> on_exec_setup_t  <Executor> call_on_exec_setup  (Executor & exec) {return exec;}
call_on_exec_error(Executor & exec,const std::error_code & ec)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>
internal_error_handle(const std::error_code &,const char *,HasHandler,boost::mpl::true_,UseVFork)148     void internal_error_handle(const std::error_code&, const char*, HasHandler, boost::mpl::true_, UseVFork) {}
149 
150     int _pipe_sink = -1;
151 
write_error(const std::error_code & ec,const char * msg)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 
internal_error_handle(const std::error_code & ec,const char * msg,boost::mpl::true_,boost::mpl::false_,boost::mpl::false_)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     }
internal_error_handle(const std::error_code & ec,const char * msg,boost::mpl::false_,boost::mpl::false_,boost::mpl::false_)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 
internal_error_handle(const std::error_code & ec,const char * msg,boost::mpl::true_,boost::mpl::false_,boost::mpl::true_)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     }
internal_error_handle(const std::error_code & ec,const char * msg,boost::mpl::false_,boost::mpl::false_,boost::mpl::true_)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 
check_error(boost::mpl::true_)198     void check_error(boost::mpl::true_) {};
check_error(boost::mpl::false_)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_ );
_write_error(int sink)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 
_read_error(int source)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;
261                 //EAGAIN not yet forked, EINTR interrupted, i.e. try again
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 
268     std::string prepare_cmd_style_fn; //buffer
269 
prepare_cmd_style()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     }
298 
299     std::error_code _ec;
300     std::string _msg;
301 public:
executor(Sequence & seq)302     executor(Sequence & seq) : seq(seq)
303     {
304     }
305 
operator ()()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 
error() const320     const std::error_code & error() const {return _ec;}
321 
set_error(const std::error_code & ec,const char * msg)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     }
set_error(const std::error_code & ec,const std::string & msg)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>
invoke(boost::mpl::true_,boost::mpl::false_)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();
336     if (cmd_style)
337         prepare_cmd_style();
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));
349         ::execve(exe, cmd_line, env);
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>
invoke(boost::mpl::false_,boost::mpl::false_)363 child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
364 {
365     {
366         struct pipe_guard
367         {
368             int p[2];
369             pipe_guard() : p{-1,-1} {}
370 
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{};
379 
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));
393 
394         if (_ec)
395         {
396             boost::fusion::for_each(seq, call_on_error(*this, _ec));
397             return child();
398         }
399 
400         if (cmd_style)
401             prepare_cmd_style();
402 
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]);
416 
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));
422 
423             _write_error(_pipe_sink);
424             ::close(p.p[1]);
425 
426             _exit(EXIT_FAILURE);
427             return child();
428         }
429 
430         ::close(p.p[1]);
431         p.p[1] = -1;
432         _read_error(p.p[0]);
433 
434     }
435     if (_ec)
436     {
437         boost::fusion::for_each(seq, call_on_error(*this, _ec));
438         return child();
439     }
440 
441     child c(child_handle(pid), exit_status);
442 
443     boost::fusion::for_each(seq, call_on_success(*this));
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>
invoke(boost::mpl::true_,boost::mpl::true_)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>
invoke(boost::mpl::false_,boost::mpl::true_)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();
496     if (cmd_style)
497         this->prepare_cmd_style();
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 
513         ::execve(exe, cmd_line, env);
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>
make_executor(Tup & tup)548 inline executor<Tup> make_executor(Tup & tup)
549 {
550     return executor<Tup>(tup);
551 }
552 
553 }}}}
554 
555 #endif
556