1 // Copyright 2015 The Kyua Authors.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 #include "utils/process/executor.ipp"
30
31 #if defined(HAVE_CONFIG_H)
32 #include "config.h"
33 #endif
34
35 extern "C" {
36 #include <sys/types.h>
37 #include <sys/wait.h>
38
39 #include <signal.h>
40 }
41
42 #include <fstream>
43 #include <map>
44 #include <memory>
45 #include <stdexcept>
46
47 #include "utils/datetime.hpp"
48 #include "utils/format/macros.hpp"
49 #include "utils/fs/auto_cleaners.hpp"
50 #include "utils/fs/exceptions.hpp"
51 #include "utils/fs/operations.hpp"
52 #include "utils/fs/path.hpp"
53 #include "utils/logging/macros.hpp"
54 #include "utils/logging/operations.hpp"
55 #include "utils/noncopyable.hpp"
56 #include "utils/optional.ipp"
57 #include "utils/passwd.hpp"
58 #include "utils/process/child.ipp"
59 #include "utils/process/deadline_killer.hpp"
60 #include "utils/process/isolation.hpp"
61 #include "utils/process/operations.hpp"
62 #include "utils/process/status.hpp"
63 #include "utils/sanity.hpp"
64 #include "utils/signals/interrupts.hpp"
65 #include "utils/signals/timer.hpp"
66
67 namespace datetime = utils::datetime;
68 namespace executor = utils::process::executor;
69 namespace fs = utils::fs;
70 namespace logging = utils::logging;
71 namespace passwd = utils::passwd;
72 namespace process = utils::process;
73 namespace signals = utils::signals;
74
75 using utils::none;
76 using utils::optional;
77
78
79 namespace {
80
81
82 /// Template for temporary directories created by the executor.
83 static const char* work_directory_template = PACKAGE_TARNAME ".XXXXXX";
84
85
86 /// Mapping of active subprocess PIDs to their execution data.
87 typedef std::map< int, executor::exec_handle > exec_handles_map;
88
89
90 } // anonymous namespace
91
92
93 /// Basename of the file containing the stdout of the subprocess.
94 const char* utils::process::executor::detail::stdout_name = "stdout.txt";
95
96
97 /// Basename of the file containing the stderr of the subprocess.
98 const char* utils::process::executor::detail::stderr_name = "stderr.txt";
99
100
101 /// Basename of the subdirectory in which the subprocess is actually executed.
102 ///
103 /// This is a subdirectory of the "unique work directory" generated for the
104 /// subprocess so that our code can create control files on disk and not
105 /// get them clobbered by the subprocess's activity.
106 const char* utils::process::executor::detail::work_subdir = "work";
107
108
109 /// Prepares a subprocess to run a user-provided hook in a controlled manner.
110 ///
111 /// \param unprivileged_user User to switch to if not none.
112 /// \param control_directory Path to the subprocess-specific control directory.
113 /// \param work_directory Path to the subprocess-specific work directory.
114 void
setup_child(const optional<passwd::user> unprivileged_user,const fs::path & control_directory,const fs::path & work_directory)115 utils::process::executor::detail::setup_child(
116 const optional< passwd::user > unprivileged_user,
117 const fs::path& control_directory,
118 const fs::path& work_directory)
119 {
120 logging::set_inmemory();
121 process::isolate_path(unprivileged_user, control_directory);
122 process::isolate_child(unprivileged_user, work_directory);
123 }
124
125
126 /// Internal implementation for the exit_handle class.
127 struct utils::process::executor::exec_handle::impl : utils::noncopyable {
128 /// PID of the process being run.
129 int pid;
130
131 /// Path to the subprocess-specific work directory.
132 fs::path control_directory;
133
134 /// Path to the subprocess's stdout file.
135 const fs::path stdout_file;
136
137 /// Path to the subprocess's stderr file.
138 const fs::path stderr_file;
139
140 /// Start time.
141 datetime::timestamp start_time;
142
143 /// User the subprocess is running as if different than the current one.
144 const optional< passwd::user > unprivileged_user;
145
146 /// Timer to kill the subprocess on activation.
147 process::deadline_killer timer;
148
149 /// Number of owners of the on-disk state.
150 executor::detail::refcnt_t state_owners;
151
152 /// Constructor.
153 ///
154 /// \param pid_ PID of the forked process.
155 /// \param control_directory_ Path to the subprocess-specific work
156 /// directory.
157 /// \param stdout_file_ Path to the subprocess's stdout file.
158 /// \param stderr_file_ Path to the subprocess's stderr file.
159 /// \param start_time_ Timestamp of when this object was constructed.
160 /// \param timeout Maximum amount of time the subprocess can run for.
161 /// \param unprivileged_user_ User the subprocess is running as if
162 /// different than the current one.
163 /// \param [in,out] state_owners_ Number of owners of the on-disk state.
164 /// For first-time processes, this should be a new counter set to 0;
165 /// for followup processes, this should point to the same counter used
166 /// by the preceding process.
implutils::process::executor::exec_handle::impl167 impl(const int pid_,
168 const fs::path& control_directory_,
169 const fs::path& stdout_file_,
170 const fs::path& stderr_file_,
171 const datetime::timestamp& start_time_,
172 const datetime::delta& timeout,
173 const optional< passwd::user > unprivileged_user_,
174 executor::detail::refcnt_t state_owners_) :
175 pid(pid_),
176 control_directory(control_directory_),
177 stdout_file(stdout_file_),
178 stderr_file(stderr_file_),
179 start_time(start_time_),
180 unprivileged_user(unprivileged_user_),
181 timer(timeout, pid_),
182 state_owners(state_owners_)
183 {
184 (*state_owners)++;
185 POST(*state_owners > 0);
186 }
187 };
188
189
190 /// Constructor.
191 ///
192 /// \param pimpl Constructed internal implementation.
exec_handle(std::shared_ptr<impl> pimpl)193 executor::exec_handle::exec_handle(std::shared_ptr< impl > pimpl) :
194 _pimpl(pimpl)
195 {
196 }
197
198
199 /// Destructor.
~exec_handle(void)200 executor::exec_handle::~exec_handle(void)
201 {
202 }
203
204
205 /// Returns the PID of the process being run.
206 ///
207 /// \return A PID.
208 int
pid(void) const209 executor::exec_handle::pid(void) const
210 {
211 return _pimpl->pid;
212 }
213
214
215 /// Returns the path to the subprocess-specific control directory.
216 ///
217 /// This is where the executor may store control files.
218 ///
219 /// \return The path to a directory that exists until cleanup() is called.
220 fs::path
control_directory(void) const221 executor::exec_handle::control_directory(void) const
222 {
223 return _pimpl->control_directory;
224 }
225
226
227 /// Returns the path to the subprocess-specific work directory.
228 ///
229 /// This is guaranteed to be clear of files created by the executor.
230 ///
231 /// \return The path to a directory that exists until cleanup() is called.
232 fs::path
work_directory(void) const233 executor::exec_handle::work_directory(void) const
234 {
235 return _pimpl->control_directory / detail::work_subdir;
236 }
237
238
239 /// Returns the path to the subprocess's stdout file.
240 ///
241 /// \return The path to a file that exists until cleanup() is called.
242 const fs::path&
stdout_file(void) const243 executor::exec_handle::stdout_file(void) const
244 {
245 return _pimpl->stdout_file;
246 }
247
248
249 /// Returns the path to the subprocess's stderr file.
250 ///
251 /// \return The path to a file that exists until cleanup() is called.
252 const fs::path&
stderr_file(void) const253 executor::exec_handle::stderr_file(void) const
254 {
255 return _pimpl->stderr_file;
256 }
257
258
259 /// Internal implementation for the exit_handle class.
260 struct utils::process::executor::exit_handle::impl : utils::noncopyable {
261 /// Original PID of the terminated subprocess.
262 ///
263 /// Note that this PID is no longer valid and cannot be used on system
264 /// tables!
265 const int original_pid;
266
267 /// Termination status of the subprocess, or none if it timed out.
268 const optional< process::status > status;
269
270 /// The user the process ran as, if different than the current one.
271 const optional< passwd::user > unprivileged_user;
272
273 /// Timestamp of when the subprocess was spawned.
274 const datetime::timestamp start_time;
275
276 /// Timestamp of when wait() or wait_any() returned this object.
277 const datetime::timestamp end_time;
278
279 /// Path to the subprocess-specific work directory.
280 const fs::path control_directory;
281
282 /// Path to the subprocess's stdout file.
283 const fs::path stdout_file;
284
285 /// Path to the subprocess's stderr file.
286 const fs::path stderr_file;
287
288 /// Number of owners of the on-disk state.
289 ///
290 /// This will be 1 if this exit_handle is the last holder of the on-disk
291 /// state, in which case cleanup() invocations will wipe the disk state.
292 /// For all other cases, this will hold a higher value.
293 detail::refcnt_t state_owners;
294
295 /// Mutable pointer to the corresponding executor state.
296 ///
297 /// This object references a member of the executor_handle that yielded this
298 /// exit_handle instance. We need this direct access to clean up after
299 /// ourselves when the handle is destroyed.
300 exec_handles_map& all_exec_handles;
301
302 /// Whether the subprocess state has been cleaned yet or not.
303 ///
304 /// Used to keep track of explicit calls to the public cleanup().
305 bool cleaned;
306
307 /// Constructor.
308 ///
309 /// \param original_pid_ Original PID of the terminated subprocess.
310 /// \param status_ Termination status of the subprocess, or none if
311 /// timed out.
312 /// \param unprivileged_user_ The user the process ran as, if different than
313 /// the current one.
314 /// \param start_time_ Timestamp of when the subprocess was spawned.
315 /// \param end_time_ Timestamp of when wait() or wait_any() returned this
316 /// object.
317 /// \param control_directory_ Path to the subprocess-specific work
318 /// directory.
319 /// \param stdout_file_ Path to the subprocess's stdout file.
320 /// \param stderr_file_ Path to the subprocess's stderr file.
321 /// \param [in,out] state_owners_ Number of owners of the on-disk state.
322 /// \param [in,out] all_exec_handles_ Global object keeping track of all
323 /// active executions for an executor. This is a pointer to a member of
324 /// the executor_handle object.
implutils::process::executor::exit_handle::impl325 impl(const int original_pid_,
326 const optional< process::status > status_,
327 const optional< passwd::user > unprivileged_user_,
328 const datetime::timestamp& start_time_,
329 const datetime::timestamp& end_time_,
330 const fs::path& control_directory_,
331 const fs::path& stdout_file_,
332 const fs::path& stderr_file_,
333 detail::refcnt_t state_owners_,
334 exec_handles_map& all_exec_handles_) :
335 original_pid(original_pid_), status(status_),
336 unprivileged_user(unprivileged_user_),
337 start_time(start_time_), end_time(end_time_),
338 control_directory(control_directory_),
339 stdout_file(stdout_file_), stderr_file(stderr_file_),
340 state_owners(state_owners_),
341 all_exec_handles(all_exec_handles_), cleaned(false)
342 {
343 }
344
345 /// Destructor.
~implutils::process::executor::exit_handle::impl346 ~impl(void)
347 {
348 if (!cleaned) {
349 LW(F("Implicitly cleaning up exit_handle for exec_handle %s; "
350 "ignoring errors!") % original_pid);
351 try {
352 cleanup();
353 } catch (const std::runtime_error& error) {
354 LE(F("Subprocess cleanup failed: %s") % error.what());
355 }
356 }
357 }
358
359 /// Cleans up the subprocess on-disk state.
360 ///
361 /// \throw engine::error If the cleanup fails, especially due to the
362 /// inability to remove the work directory.
363 void
cleanuputils::process::executor::exit_handle::impl364 cleanup(void)
365 {
366 PRE(*state_owners > 0);
367 if (*state_owners == 1) {
368 LI(F("Cleaning up exit_handle for exec_handle %s") % original_pid);
369 fs::rm_r(control_directory);
370 } else {
371 LI(F("Not cleaning up exit_handle for exec_handle %s; "
372 "%s owners left") % original_pid % (*state_owners - 1));
373 }
374 // We must decrease our reference only after we have successfully
375 // cleaned up the control directory. Otherwise, the rm_r call would
376 // throw an exception, which would in turn invoke the implicit cleanup
377 // from the destructor, which would make us crash due to an invalid
378 // reference count.
379 (*state_owners)--;
380 // Marking this object as clean here, even if we did not do actually the
381 // cleaning above, is fine (albeit a bit confusing). Note that "another
382 // owner" refers to a handle for a different PID, so that handle will be
383 // the one issuing the cleanup.
384 all_exec_handles.erase(original_pid);
385 cleaned = true;
386 }
387 };
388
389
390 /// Constructor.
391 ///
392 /// \param pimpl Constructed internal implementation.
exit_handle(std::shared_ptr<impl> pimpl)393 executor::exit_handle::exit_handle(std::shared_ptr< impl > pimpl) :
394 _pimpl(pimpl)
395 {
396 }
397
398
399 /// Destructor.
~exit_handle(void)400 executor::exit_handle::~exit_handle(void)
401 {
402 }
403
404
405 /// Cleans up the subprocess status.
406 ///
407 /// This function should be called explicitly as it provides the means to
408 /// control any exceptions raised during cleanup. Do not rely on the destructor
409 /// to clean things up.
410 ///
411 /// \throw engine::error If the cleanup fails, especially due to the inability
412 /// to remove the work directory.
413 void
cleanup(void)414 executor::exit_handle::cleanup(void)
415 {
416 PRE(!_pimpl->cleaned);
417 _pimpl->cleanup();
418 POST(_pimpl->cleaned);
419 }
420
421
422 /// Gets the current number of owners of the on-disk data.
423 ///
424 /// \return A shared reference counter. Even though this function is marked as
425 /// const, the return value is intentionally mutable because we need to update
426 /// reference counts from different but related processes. This is why this
427 /// method is not public.
428 std::shared_ptr< std::size_t >
state_owners(void) const429 executor::exit_handle::state_owners(void) const
430 {
431 return _pimpl->state_owners;
432 }
433
434
435 /// Returns the original PID corresponding to the terminated subprocess.
436 ///
437 /// \return An exec_handle.
438 int
original_pid(void) const439 executor::exit_handle::original_pid(void) const
440 {
441 return _pimpl->original_pid;
442 }
443
444
445 /// Returns the process termination status of the subprocess.
446 ///
447 /// \return A process termination status, or none if the subprocess timed out.
448 const optional< process::status >&
status(void) const449 executor::exit_handle::status(void) const
450 {
451 return _pimpl->status;
452 }
453
454
455 /// Returns the user the process ran as if different than the current one.
456 ///
457 /// \return None if the credentials of the process were the same as the current
458 /// one, or else a user.
459 const optional< passwd::user >&
unprivileged_user(void) const460 executor::exit_handle::unprivileged_user(void) const
461 {
462 return _pimpl->unprivileged_user;
463 }
464
465
466 /// Returns the timestamp of when the subprocess was spawned.
467 ///
468 /// \return A timestamp.
469 const datetime::timestamp&
start_time(void) const470 executor::exit_handle::start_time(void) const
471 {
472 return _pimpl->start_time;
473 }
474
475
476 /// Returns the timestamp of when wait() or wait_any() returned this object.
477 ///
478 /// \return A timestamp.
479 const datetime::timestamp&
end_time(void) const480 executor::exit_handle::end_time(void) const
481 {
482 return _pimpl->end_time;
483 }
484
485
486 /// Returns the path to the subprocess-specific control directory.
487 ///
488 /// This is where the executor may store control files.
489 ///
490 /// \return The path to a directory that exists until cleanup() is called.
491 fs::path
control_directory(void) const492 executor::exit_handle::control_directory(void) const
493 {
494 return _pimpl->control_directory;
495 }
496
497
498 /// Returns the path to the subprocess-specific work directory.
499 ///
500 /// This is guaranteed to be clear of files created by the executor.
501 ///
502 /// \return The path to a directory that exists until cleanup() is called.
503 fs::path
work_directory(void) const504 executor::exit_handle::work_directory(void) const
505 {
506 return _pimpl->control_directory / detail::work_subdir;
507 }
508
509
510 /// Returns the path to the subprocess's stdout file.
511 ///
512 /// \return The path to a file that exists until cleanup() is called.
513 const fs::path&
stdout_file(void) const514 executor::exit_handle::stdout_file(void) const
515 {
516 return _pimpl->stdout_file;
517 }
518
519
520 /// Returns the path to the subprocess's stderr file.
521 ///
522 /// \return The path to a file that exists until cleanup() is called.
523 const fs::path&
stderr_file(void) const524 executor::exit_handle::stderr_file(void) const
525 {
526 return _pimpl->stderr_file;
527 }
528
529
530 /// Internal implementation for the executor_handle.
531 ///
532 /// Because the executor is a singleton, these essentially is a container for
533 /// global variables.
534 struct utils::process::executor::executor_handle::impl : utils::noncopyable {
535 /// Numeric counter of executed subprocesses.
536 ///
537 /// This is used to generate a unique identifier for each subprocess as an
538 /// easy mechanism to discern their unique work directories.
539 size_t last_subprocess;
540
541 /// Interrupts handler.
542 std::auto_ptr< signals::interrupts_handler > interrupts_handler;
543
544 /// Root work directory for all executed subprocesses.
545 std::auto_ptr< fs::auto_directory > root_work_directory;
546
547 /// Mapping of PIDs to the data required at run time.
548 exec_handles_map all_exec_handles;
549
550 /// Whether the executor state has been cleaned yet or not.
551 ///
552 /// Used to keep track of explicit calls to the public cleanup().
553 bool cleaned;
554
555 /// Constructor.
implutils::process::executor::executor_handle::impl556 impl(void) :
557 last_subprocess(0),
558 interrupts_handler(new signals::interrupts_handler()),
559 root_work_directory(new fs::auto_directory(
560 fs::auto_directory::mkdtemp_public(work_directory_template))),
561 cleaned(false)
562 {
563 }
564
565 /// Destructor.
~implutils::process::executor::executor_handle::impl566 ~impl(void)
567 {
568 if (!cleaned) {
569 LW("Implicitly cleaning up executor; ignoring errors!");
570 try {
571 cleanup();
572 cleaned = true;
573 } catch (const std::runtime_error& error) {
574 LE(F("Executor global cleanup failed: %s") % error.what());
575 }
576 }
577 }
578
579 /// Cleans up the executor state.
580 void
cleanuputils::process::executor::executor_handle::impl581 cleanup(void)
582 {
583 PRE(!cleaned);
584
585 for (exec_handles_map::const_iterator iter = all_exec_handles.begin();
586 iter != all_exec_handles.end(); ++iter) {
587 const int& pid = (*iter).first;
588 const exec_handle& data = (*iter).second;
589
590 process::terminate_group(pid);
591 int status;
592 if (::waitpid(pid, &status, 0) == -1) {
593 // Should not happen.
594 LW(F("Failed to wait for PID %s") % pid);
595 }
596
597 try {
598 fs::rm_r(data.control_directory());
599 } catch (const fs::error& e) {
600 LE(F("Failed to clean up subprocess work directory %s: %s") %
601 data.control_directory() % e.what());
602 }
603 }
604 all_exec_handles.clear();
605
606 try {
607 // The following only causes the work directory to be deleted, not
608 // any of its contents, so we expect this to always succeed. This
609 // *should* be sufficient because, in the loop above, we have
610 // individually wiped the subdirectories of any still-unclean
611 // subprocesses.
612 root_work_directory->cleanup();
613 } catch (const fs::error& e) {
614 LE(F("Failed to clean up executor work directory %s: %s; this is "
615 "an internal error") % root_work_directory->directory()
616 % e.what());
617 }
618 root_work_directory.reset(NULL);
619
620 interrupts_handler->unprogram();
621 interrupts_handler.reset(NULL);
622 }
623
624 /// Common code to run after any of the wait calls.
625 ///
626 /// \param original_pid The PID of the terminated subprocess.
627 /// \param status The exit status of the terminated subprocess.
628 ///
629 /// \return A pointer to an object describing the waited-for subprocess.
630 executor::exit_handle
post_waitutils::process::executor::executor_handle::impl631 post_wait(const int original_pid, const process::status& status)
632 {
633 PRE(original_pid == status.dead_pid());
634 LI(F("Waited for subprocess with exec_handle %s") % original_pid);
635
636 process::terminate_group(status.dead_pid());
637
638 const exec_handles_map::iterator iter = all_exec_handles.find(
639 original_pid);
640 exec_handle& data = (*iter).second;
641 data._pimpl->timer.unprogram();
642
643 // It is tempting to assert here (and old code did) that, if the timer
644 // has fired, the process has been forcibly killed by us. This is not
645 // always the case though: for short-lived processes and with very short
646 // timeouts (think 1ms), it is possible for scheduling decisions to
647 // allow the subprocess to finish while at the same time cause the timer
648 // to fire. So we do not assert this any longer and just rely on the
649 // timer expiration to check if the process timed out or not. If the
650 // process did finish but the timer expired... oh well, we do not detect
651 // this correctly but we don't care because this should not really
652 // happen.
653
654 if (!fs::exists(data.stdout_file())) {
655 std::ofstream new_stdout(data.stdout_file().c_str());
656 }
657 if (!fs::exists(data.stderr_file())) {
658 std::ofstream new_stderr(data.stderr_file().c_str());
659 }
660
661 return exit_handle(std::shared_ptr< exit_handle::impl >(
662 new exit_handle::impl(
663 data.pid(),
664 data._pimpl->timer.fired() ?
665 none : utils::make_optional(status),
666 data._pimpl->unprivileged_user,
667 data._pimpl->start_time, datetime::timestamp::now(),
668 data.control_directory(),
669 data.stdout_file(),
670 data.stderr_file(),
671 data._pimpl->state_owners,
672 all_exec_handles)));
673 }
674 };
675
676
677 /// Constructor.
executor_handle(void)678 executor::executor_handle::executor_handle(void) throw() : _pimpl(new impl())
679 {
680 }
681
682
683 /// Destructor.
~executor_handle(void)684 executor::executor_handle::~executor_handle(void)
685 {
686 }
687
688
689 /// Queries the path to the root of the work directory for all subprocesses.
690 ///
691 /// \return A path.
692 const fs::path&
root_work_directory(void) const693 executor::executor_handle::root_work_directory(void) const
694 {
695 return _pimpl->root_work_directory->directory();
696 }
697
698
699 /// Cleans up the executor state.
700 ///
701 /// This function should be called explicitly as it provides the means to
702 /// control any exceptions raised during cleanup. Do not rely on the destructor
703 /// to clean things up.
704 ///
705 /// \throw engine::error If there are problems cleaning up the executor.
706 void
cleanup(void)707 executor::executor_handle::cleanup(void)
708 {
709 PRE(!_pimpl->cleaned);
710 _pimpl->cleanup();
711 _pimpl->cleaned = true;
712 }
713
714
715 /// Initializes the executor.
716 ///
717 /// \pre This function can only be called if there is no other executor_handle
718 /// object alive.
719 ///
720 /// \return A handle to the operations of the executor.
721 executor::executor_handle
setup(void)722 executor::setup(void)
723 {
724 return executor_handle();
725 }
726
727
728 /// Pre-helper for the spawn() method.
729 ///
730 /// \return The created control directory for the subprocess.
731 fs::path
spawn_pre(void)732 executor::executor_handle::spawn_pre(void)
733 {
734 signals::check_interrupt();
735
736 ++_pimpl->last_subprocess;
737
738 const fs::path control_directory =
739 _pimpl->root_work_directory->directory() /
740 (F("%s") % _pimpl->last_subprocess);
741 fs::mkdir_p(control_directory / detail::work_subdir, 0755);
742
743 return control_directory;
744 }
745
746
747 /// Post-helper for the spawn() method.
748 ///
749 /// \param control_directory Control directory as returned by spawn_pre().
750 /// \param stdout_file Path to the subprocess' stdout.
751 /// \param stderr_file Path to the subprocess' stderr.
752 /// \param timeout Maximum amount of time the subprocess can run for.
753 /// \param unprivileged_user If not none, user to switch to before execution.
754 /// \param child The process created by spawn().
755 ///
756 /// \return The execution handle of the started subprocess.
757 executor::exec_handle
spawn_post(const fs::path & control_directory,const fs::path & stdout_file,const fs::path & stderr_file,const datetime::delta & timeout,const optional<passwd::user> unprivileged_user,std::auto_ptr<process::child> child)758 executor::executor_handle::spawn_post(
759 const fs::path& control_directory,
760 const fs::path& stdout_file,
761 const fs::path& stderr_file,
762 const datetime::delta& timeout,
763 const optional< passwd::user > unprivileged_user,
764 std::auto_ptr< process::child > child)
765 {
766 const exec_handle handle(std::shared_ptr< exec_handle::impl >(
767 new exec_handle::impl(
768 child->pid(),
769 control_directory,
770 stdout_file,
771 stderr_file,
772 datetime::timestamp::now(),
773 timeout,
774 unprivileged_user,
775 detail::refcnt_t(new detail::refcnt_t::element_type(0)))));
776 INV_MSG(_pimpl->all_exec_handles.find(handle.pid()) ==
777 _pimpl->all_exec_handles.end(),
778 F("PID %s already in all_exec_handles; not properly cleaned "
779 "up or reused too fast") % handle.pid());;
780 _pimpl->all_exec_handles.insert(exec_handles_map::value_type(
781 handle.pid(), handle));
782 LI(F("Spawned subprocess with exec_handle %s") % handle.pid());
783 return handle;
784 }
785
786
787 /// Pre-helper for the spawn_followup() method.
788 void
spawn_followup_pre(void)789 executor::executor_handle::spawn_followup_pre(void)
790 {
791 signals::check_interrupt();
792 }
793
794
795 /// Post-helper for the spawn_followup() method.
796 ///
797 /// \param base Exit handle of the subprocess to use as context.
798 /// \param timeout Maximum amount of time the subprocess can run for.
799 /// \param child The process created by spawn_followup().
800 ///
801 /// \return The execution handle of the started subprocess.
802 executor::exec_handle
spawn_followup_post(const exit_handle & base,const datetime::delta & timeout,std::auto_ptr<process::child> child)803 executor::executor_handle::spawn_followup_post(
804 const exit_handle& base,
805 const datetime::delta& timeout,
806 std::auto_ptr< process::child > child)
807 {
808 INV(*base.state_owners() > 0);
809 const exec_handle handle(std::shared_ptr< exec_handle::impl >(
810 new exec_handle::impl(
811 child->pid(),
812 base.control_directory(),
813 base.stdout_file(),
814 base.stderr_file(),
815 datetime::timestamp::now(),
816 timeout,
817 base.unprivileged_user(),
818 base.state_owners())));
819 INV_MSG(_pimpl->all_exec_handles.find(handle.pid()) ==
820 _pimpl->all_exec_handles.end(),
821 F("PID %s already in all_exec_handles; not properly cleaned "
822 "up or reused too fast") % handle.pid());;
823 _pimpl->all_exec_handles.insert(exec_handles_map::value_type(
824 handle.pid(), handle));
825 LI(F("Spawned subprocess with exec_handle %s") % handle.pid());
826 return handle;
827 }
828
829
830 /// Waits for completion of any forked process.
831 ///
832 /// \param exec_handle The handle of the process to wait for.
833 ///
834 /// \return A pointer to an object describing the waited-for subprocess.
835 executor::exit_handle
wait(const exec_handle exec_handle)836 executor::executor_handle::wait(const exec_handle exec_handle)
837 {
838 signals::check_interrupt();
839 const process::status status = process::wait(exec_handle.pid());
840 return _pimpl->post_wait(exec_handle.pid(), status);
841 }
842
843
844 /// Waits for completion of any forked process.
845 ///
846 /// \return A pointer to an object describing the waited-for subprocess.
847 executor::exit_handle
wait_any(void)848 executor::executor_handle::wait_any(void)
849 {
850 signals::check_interrupt();
851 const process::status status = process::wait_any();
852 return _pimpl->post_wait(status.dead_pid(), status);
853 }
854
855
856 /// Checks if an interrupt has fired.
857 ///
858 /// Calls to this function should be sprinkled in strategic places through the
859 /// code protected by an interrupts_handler object.
860 ///
861 /// This is just a wrapper over signals::check_interrupt() to avoid leaking this
862 /// dependency to the caller.
863 ///
864 /// \throw signals::interrupted_error If there has been an interrupt.
865 void
check_interrupt(void) const866 executor::executor_handle::check_interrupt(void) const
867 {
868 signals::check_interrupt();
869 }
870