Ticket #13590: executor.hpp

File executor.hpp, 16.1 KB (added by Elmar Daegele <elmar.daegele@…>, 4 years ago)
Line 
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
31namespace boost { namespace process { namespace detail { namespace posix {
32
33inline 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
66template<typename Executor>
67struct 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
79template<typename Executor>
80struct 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
92template<typename Executor>
93struct 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
103template<typename Executor>
104struct 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
118template<typename Executor>
119struct 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
132template<typename Executor>
133struct 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
146template<typename Executor>
147struct 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
159template<typename Executor> on_setup_t <Executor> call_on_setup (Executor & exec) {return exec;}
160template<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}
164template<typename Executor> on_success_t<Executor> call_on_success(Executor & exec) {return exec;}
165
166template<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
172template<typename Executor> on_exec_setup_t <Executor> call_on_exec_setup (Executor & exec) {return exec;}
173template<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
179template<typename Sequence>
180class executor
181{
182 template<typename HasHandler, typename UseVFork>
183 void internal_error_handle(const std::error_code &ec, const char* msg, 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 throw process_error(_ec, _msg);
237 }
238
239 typedef typename ::boost::process::detail::has_error_handler<Sequence>::type has_error_handler;
240 typedef typename ::boost::process::detail::has_ignore_error <Sequence>::type has_ignore_error;
241 typedef typename ::boost::process::detail::posix::shall_use_vfork<Sequence>::type shall_use_vfork;
242
243 inline child invoke(boost::mpl::true_ , boost::mpl::true_ );
244 inline child invoke(boost::mpl::false_, boost::mpl::true_ );
245 inline child invoke(boost::mpl::true_ , boost::mpl::false_ );
246 inline child invoke(boost::mpl::false_, boost::mpl::false_ );
247 void _write_error(int sink)
248 {
249 int data[2] = {_ec.value(),static_cast<int>(_msg.size())};
250
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 // BEGIN PATCH by Elmar Daegele, Siemens AG
274 int data[2] = { 0 };
275 // END PATCH by Elmar Daegele, Siemens AG
276
277 _ec.clear();
278 int count = 0;
279
280 // BEGIN PATCH by Elmar Daegele, Siemens AG
281
282 int remaining = sizeof(data);
283 char *writeptr = reinterpret_cast<char *>(&data[0]);
284 do
285 {
286 while ((count = ::read(source, writeptr, remaining ) ) == -1)
287 {
288 //actually, this should block until it's read.
289 auto err = errno;
290 if ((err != EAGAIN ) && (err != EINTR))
291 {
292 set_error(std::error_code(err, std::system_category()), "Error read pipe (ec)");
293 return;
294 }
295 }
296
297 if (count == 0)
298 return ;
299
300 remaining -= count;
301 writeptr += count;
302 }
303 while(remaining > 0);
304
305 // END PATCH by Elmar Daegele, Siemens AG
306
307 std::error_code ec(data[0], std::system_category());
308 std::string msg(data[1], ' ');
309
310 // BEGIN PATCH by Elmar Daegele, Siemens AG
311 remaining = msg.size();
312 writeptr = &msg.front();
313 do
314 {
315 while ((count = ::read(source, writeptr, remaining ) ) == -1)
316 {
317 //actually, this should block until it's read.
318 auto err = errno;
319 if ((err == EBADF) || (err == EPERM))//that should occur on success, therefore return.
320 return;
321 //EAGAIN not yet forked, EINTR interrupted, i.e. try again
322 else if ((err != EAGAIN ) && (err != EINTR))
323 {
324 set_error(std::error_code(err, std::system_category()), "Error read pipe (msg)");
325 return;
326 }
327 }
328
329 if (count == 0)
330 return ;
331
332 remaining -= count;
333 writeptr += count;
334 }
335 while(remaining > 0);
336
337 set_error(ec, std::move(msg));
338 }
339
340
341 std::error_code _ec;
342 std::string _msg;
343public:
344 executor(Sequence & seq) : seq(seq)
345 {
346 }
347
348 child operator()()
349 {
350 return invoke(has_ignore_error(), shall_use_vfork());
351 }
352
353
354 Sequence & seq;
355 const char * exe = nullptr;
356 char *const* cmd_line = nullptr;
357 bool cmd_style = false;
358 char **env = ::environ;
359 pid_t pid = -1;
360 std::shared_ptr<std::atomic<int>> exit_status = std::make_shared<std::atomic<int>>(still_active);
361
362 const std::error_code & error() const {return _ec;}
363
364 void set_error(const std::error_code &ec, const char* msg)
365 {
366 internal_error_handle(ec, msg, has_error_handler(), has_ignore_error(), shall_use_vfork());
367 }
368 void set_error(const std::error_code &ec, const std::string &msg) {set_error(ec, msg.c_str());};
369
370};
371
372template<typename Sequence>
373child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::false_) //ignore errors
374{
375 boost::fusion::for_each(seq, call_on_setup(*this));
376 if (_ec)
377 return child();
378
379 this->pid = ::fork();
380 if (pid == -1)
381 {
382 auto ec = boost::process::detail::get_last_error();
383 boost::fusion::for_each(seq, call_on_fork_error(*this, ec));
384 return child();
385 }
386 else if (pid == 0)
387 {
388 boost::fusion::for_each(seq, call_on_exec_setup(*this));
389 if (cmd_style)
390 ::boost::process::detail::posix::execvpe(exe, cmd_line, env);
391 else
392 ::execve(exe, cmd_line, env);
393 auto ec = boost::process::detail::get_last_error();
394 boost::fusion::for_each(seq, call_on_exec_error(*this, ec));
395 _exit(EXIT_FAILURE);
396 }
397
398 child c(child_handle(pid), exit_status);
399
400 boost::fusion::for_each(seq, call_on_success(*this));
401
402 return c;
403}
404
405template<typename Sequence>
406child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::false_)
407{
408 int p[2];
409 if (::pipe(p) == -1)
410 {
411 set_error(::boost::process::detail::get_last_error(), "pipe(2) failed");
412 return child();
413 }
414 if (::fcntl(p[1], F_SETFD, FD_CLOEXEC) == -1)
415 {
416 set_error(::boost::process::detail::get_last_error(), "fcntl(2) failed");
417 return child();
418 }
419 _ec.clear();
420 boost::fusion::for_each(seq, call_on_setup(*this));
421
422 if (_ec)
423 {
424 boost::fusion::for_each(seq, call_on_error(*this, _ec));
425 return child();
426 }
427
428 this->pid = ::fork();
429 if (pid == -1)
430 {
431 _ec = boost::process::detail::get_last_error();
432 _msg = "fork() failed";
433 boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
434 boost::fusion::for_each(seq, call_on_error(*this, _ec));
435
436 return child();
437 }
438 else if (pid == 0)
439 {
440 _pipe_sink = p[1];
441 ::close(p[0]);
442
443 boost::fusion::for_each(seq, call_on_exec_setup(*this));
444 if (cmd_style)
445 ::boost::process::detail::posix::execvpe(exe, cmd_line, env);
446 else
447 ::execve(exe, cmd_line, env);
448 _ec = boost::process::detail::get_last_error();
449 _msg = "execve failed";
450
451 boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
452
453 _write_error(p[1]);
454
455 _exit(EXIT_FAILURE);
456 return child();
457 }
458
459 child c(child_handle(pid), exit_status);
460
461 ::close(p[1]);
462 _read_error(p[0]);
463 ::close(p[0]);
464
465 if (_ec)
466 {
467 boost::fusion::for_each(seq, call_on_error(*this, _ec));
468 return child();
469 }
470 else
471 boost::fusion::for_each(seq, call_on_success(*this));
472
473 if (_ec)
474 {
475 boost::fusion::for_each(seq, call_on_error(*this, _ec));
476 return child();
477 }
478
479 return c;
480}
481
482#if BOOST_POSIX_HAS_VFORK
483
484
485template<typename Sequence>
486child executor<Sequence>::invoke(boost::mpl::true_, boost::mpl::true_) //ignore errors
487{
488 boost::fusion::for_each(seq, call_on_setup(*this));
489 if (_ec)
490 return child();
491 this->pid = ::vfork();
492 if (pid == -1)
493 {
494 auto ec = boost::process::detail::get_last_error();
495 boost::fusion::for_each(seq, call_on_fork_error(*this, ec));
496 return child();
497 }
498 else if (pid == 0)
499 {
500 boost::fusion::for_each(seq, call_on_exec_setup(*this));
501 ::execve(exe, cmd_line, env);
502 auto ec = boost::process::detail::get_last_error();
503 boost::fusion::for_each(seq, call_on_exec_error(*this, ec));
504 _exit(EXIT_FAILURE);
505 }
506 child c(child_handle(pid), exit_status);
507
508 boost::fusion::for_each(seq, call_on_success(*this));
509
510 return c;
511}
512
513template<typename Sequence>
514child executor<Sequence>::invoke(boost::mpl::false_, boost::mpl::true_)
515{
516 boost::fusion::for_each(seq, call_on_setup(*this));
517
518 if (_ec)
519 {
520 boost::fusion::for_each(seq, call_on_error(*this, _ec));
521 return child();
522 }
523 _ec.clear();
524
525 this->pid = ::vfork();
526 if (pid == -1)
527 {
528 _ec = boost::process::detail::get_last_error();
529 _msg = "fork() failed";
530 boost::fusion::for_each(seq, call_on_fork_error(*this, _ec));
531 boost::fusion::for_each(seq, call_on_error(*this, _ec));
532
533 return child();
534 }
535 else if (pid == 0)
536 {
537 boost::fusion::for_each(seq, call_on_exec_setup(*this));
538
539 if (cmd_style)
540 ::boost::process::detail::posix::execvpe(exe, cmd_line, env);
541 else
542 ::execve(exe, cmd_line, env);
543
544 _ec = boost::process::detail::get_last_error();
545 _msg = "execve failed";
546 boost::fusion::for_each(seq, call_on_exec_error(*this, _ec));
547
548 _exit(EXIT_FAILURE);
549 return child();
550 }
551 child c(child_handle(pid), exit_status);
552
553 check_error(has_error_handler());
554
555
556
557 if (_ec)
558 {
559 boost::fusion::for_each(seq, call_on_error(*this, _ec));
560 return child();
561 }
562 else
563 boost::fusion::for_each(seq, call_on_success(*this));
564
565 if (_ec)
566 {
567 boost::fusion::for_each(seq, call_on_error(*this, _ec));
568 return child();
569 }
570
571 return c;
572}
573
574#endif
575
576template<typename Char, typename Tup>
577inline executor<Tup> make_executor(Tup & tup)
578{
579 return executor<Tup>(tup);
580}
581
582}}}}
583
584#endif