]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/process/detail/posix/executor.hpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / boost / process / detail / posix / executor.hpp
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;
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 {
53 t.on_error(exec, error);
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;}
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 }
129 template<typename Executor> on_success_t<Executor> call_on_success(Executor & exec) {return exec;}
130
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
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 const auto len = std::strlen(msg);
156 int data[2] = {ec.value(), len + 1};
157
158 ::write(_pipe_sink, &data[0], sizeof(int) * 2);
159 ::write(_pipe_sink, msg, len);
160 }
161
162 void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::true_ , boost::mpl::false_, boost::mpl::false_)
163 {
164 if (this->pid == 0) //on the fork.
165 write_error(ec, msg);
166 else
167 {
168 this->_ec = ec;
169 this->_msg = msg;
170 }
171 }
172 void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::false_, boost::mpl::false_)
173 {
174 if (this->pid == 0)
175 write_error(ec, msg);
176 else
177 throw process_error(ec, msg);
178 }
179
180
181 void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::true_ , boost::mpl::false_, boost::mpl::true_)
182 {
183 this->_ec = ec;
184 this->_msg = msg;
185 }
186 void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::false_, boost::mpl::true_)
187 {
188 if (this->pid == 0)
189 {
190 this->_ec = ec;
191 this->_msg = msg;
192 }
193 else
194 throw process_error(ec, msg);
195 }
196
197 void check_error(boost::mpl::true_) {};
198 void check_error(boost::mpl::false_)
199 {
200 if (_ec)
201 throw process_error(_ec, _msg);
202 }
203
204 typedef typename ::boost::process::detail::has_error_handler<Sequence>::type has_error_handler;
205 typedef typename ::boost::process::detail::has_ignore_error <Sequence>::type has_ignore_error;
206 typedef typename ::boost::process::detail::posix::shall_use_vfork<Sequence>::type shall_use_vfork;
207
208 inline child invoke(boost::mpl::true_ , boost::mpl::true_ );
209 inline child invoke(boost::mpl::false_, boost::mpl::true_ );
210 inline child invoke(boost::mpl::true_ , boost::mpl::false_ );
211 inline child invoke(boost::mpl::false_, boost::mpl::false_ );
212 void _write_error(int sink)
213 {
214 int data[2] = {_ec.value(),static_cast<int>(_msg.size())};
215 while (::write(sink, &data[0], sizeof(int) *2) == -1)
216 {
217 auto err = errno;
218
219 if (err == EBADF)
220 return;
221 else if ((err != EINTR) && (err != EAGAIN))
222 break;
223 }
224 while (::write(sink, &_msg.front(), _msg.size()) == -1)
225 {
226 auto err = errno;
227
228 if (err == EBADF)
229 return;
230 else if ((err != EINTR) && (err != EAGAIN))
231 break;
232 }
233 }
234
235 void _read_error(int source)
236 {
237 int data[2];
238
239 _ec.clear();
240 int count = 0;
241 while ((count = ::read(source, &data[0], sizeof(int) *2 ) ) == -1)
242 {
243 //actually, this should block until it's read.
244 auto err = errno;
245 if ((err != EAGAIN ) && (err != EINTR))
246 set_error(std::error_code(err, std::system_category()), "Error read pipe");
247 }
248 if (count == 0)
249 return ;
250
251 std::error_code ec(data[0], std::system_category());
252 std::string msg(data[1], ' ');
253
254 while (::read(source, &msg.front(), msg.size() ) == -1)
255 {
256 //actually, this should block until it's read.
257 auto err = errno;
258 if ((err == EBADF) || (err == EPERM))//that should occur on success, therefore return.
259 return;
260 //EAGAIN not yet forked, EINTR interrupted, i.e. try again
261 else if ((err != EAGAIN ) && (err != EINTR))
262 set_error(std::error_code(err, std::system_category()), "Error read pipe");
263 }
264 set_error(ec, std::move(msg));
265 }
266
267 std::string prepare_cmd_style_fn; //buffer
268
269 inline void prepare_cmd_style() //this does what execvpe does - but we execute it in the father process, to avoid allocations.
270 {
271 //use my own implementation
272 prepare_cmd_style_fn = exe;
273 if ((prepare_cmd_style_fn.find('/') == std::string::npos) && ::access(prepare_cmd_style_fn.c_str(), X_OK))
274 {
275 const auto * e = ::environ;
276 while ((e != nullptr) && (*e != nullptr) && !boost::starts_with(*e, "PATH="))
277 e++;
278
279 if ((e != nullptr) && (*e != nullptr))
280 {
281 std::vector<std::string> path;
282 //the beginning of the string contains "PATH="
283 boost::split(path, (*e) + 5, 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:
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();
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>
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>
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();
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>
548 inline executor<Tup> make_executor(Tup & tup)
549 {
550 return executor<Tup>(tup);
551 }
552
553 }}}}
554
555 #endif