xref: /freebsd/contrib/kyua/utils/process/executor.cpp (revision ec0ea6efa1ad229d75c394c1a9b9cac33af2b1d3)
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
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.
167     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.
193 executor::exec_handle::exec_handle(std::shared_ptr< impl > pimpl) :
194     _pimpl(pimpl)
195 {
196 }
197 
198 
199 /// Destructor.
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
209 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
221 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
233 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&
243 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&
253 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.
325     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.
346     ~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
364     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.
393 executor::exit_handle::exit_handle(std::shared_ptr< impl > pimpl) :
394     _pimpl(pimpl)
395 {
396 }
397 
398 
399 /// Destructor.
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
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 >
429 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
439 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 >&
449 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 >&
460 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&
470 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&
480 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
492 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
504 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&
514 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&
524 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.
556     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.
566     ~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
581     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
631     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.
678 executor::executor_handle::executor_handle(void) throw() : _pimpl(new impl())
679 {
680 }
681 
682 
683 /// Destructor.
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&
693 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
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
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
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
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
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
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
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
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
866 executor::executor_handle::check_interrupt(void) const
867 {
868     signals::check_interrupt();
869 }
870