]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/process/detail/posix/executor.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / process / detail / posix / executor.hpp
CommitLineData
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
29namespace boost { namespace process { namespace detail { namespace posix {
30
b32b8144
FG
31template<typename Executor>
32struct 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
44template<typename Executor>
45struct 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
57template<typename Executor>
58struct 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
68template<typename Executor>
69struct 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
83template<typename Executor>
84struct 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
97template<typename Executor>
98struct 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
111template<typename Executor>
112struct 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
124template<typename Executor> on_setup_t <Executor> call_on_setup (Executor & exec) {return exec;}
92f5a8d4 125template<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}
129template<typename Executor> on_success_t<Executor> call_on_success(Executor & exec) {return exec;}
130
92f5a8d4 131template<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
137template<typename Executor> on_exec_setup_t <Executor> call_on_exec_setup (Executor & exec) {return exec;}
138template<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
144template<typename Sequence>
145class 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;
301public:
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
330template<typename Sequence>
331child 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
362template<typename Sequence>
363child 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
457template<typename Sequence>
458child 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
485template<typename Sequence>
486child 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
547template<typename Char, typename Tup>
548inline executor<Tup> make_executor(Tup & tup)
549{
550 return executor<Tup>(tup);
551}
552
553}}}}
554
555#endif