xref: /freebsd/contrib/kyua/utils/process/executor.cpp (revision 257e70f1d5ee61037c8c59b116538d3b6b1427a2)
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 <forward_list>
43 #include <fstream>
44 #include <map>
45 #include <memory>
46 #include <stdexcept>
47 #include <utility>
48 
49 #include "utils/datetime.hpp"
50 #include "utils/format/macros.hpp"
51 #include "utils/fs/auto_cleaners.hpp"
52 #include "utils/fs/exceptions.hpp"
53 #include "utils/fs/operations.hpp"
54 #include "utils/fs/path.hpp"
55 #include "utils/logging/macros.hpp"
56 #include "utils/logging/operations.hpp"
57 #include "utils/noncopyable.hpp"
58 #include "utils/optional.ipp"
59 #include "utils/passwd.hpp"
60 #include "utils/process/child.ipp"
61 #include "utils/process/deadline_killer.hpp"
62 #include "utils/process/isolation.hpp"
63 #include "utils/process/operations.hpp"
64 #include "utils/process/status.hpp"
65 #include "utils/sanity.hpp"
66 #include "utils/signals/interrupts.hpp"
67 #include "utils/signals/timer.hpp"
68 
69 namespace datetime = utils::datetime;
70 namespace executor = utils::process::executor;
71 namespace fs = utils::fs;
72 namespace logging = utils::logging;
73 namespace passwd = utils::passwd;
74 namespace process = utils::process;
75 namespace signals = utils::signals;
76 
77 using utils::none;
78 using utils::optional;
79 
80 
81 namespace {
82 
83 
84 /// Template for temporary directories created by the executor.
85 static const char* work_directory_template = PACKAGE_TARNAME ".XXXXXX";
86 
87 
88 /// Mapping of active subprocess PIDs to their execution data.
89 typedef std::map< int, executor::exec_handle > exec_handles_map;
90 
91 
92 }  // anonymous namespace
93 
94 
95 /// Basename of the file containing the stdout of the subprocess.
96 const char* utils::process::executor::detail::stdout_name = "stdout.txt";
97 
98 
99 /// Basename of the file containing the stderr of the subprocess.
100 const char* utils::process::executor::detail::stderr_name = "stderr.txt";
101 
102 
103 /// Basename of the subdirectory in which the subprocess is actually executed.
104 ///
105 /// This is a subdirectory of the "unique work directory" generated for the
106 /// subprocess so that our code can create control files on disk and not
107 /// get them clobbered by the subprocess's activity.
108 const char* utils::process::executor::detail::work_subdir = "work";
109 
110 
111 /// Prepares a subprocess to run a user-provided hook in a controlled manner.
112 ///
113 /// \param unprivileged_user User to switch to if not none.
114 /// \param control_directory Path to the subprocess-specific control directory.
115 /// \param work_directory Path to the subprocess-specific work directory.
116 void
setup_child(const optional<passwd::user> unprivileged_user,const fs::path & control_directory,const fs::path & work_directory)117 utils::process::executor::detail::setup_child(
118     const optional< passwd::user > unprivileged_user,
119     const fs::path& control_directory,
120     const fs::path& work_directory)
121 {
122     logging::set_inmemory();
123     process::isolate_path(unprivileged_user, control_directory);
124     process::isolate_child(unprivileged_user, work_directory);
125 }
126 
127 
128 /// Internal implementation for the exec_handle class.
129 struct utils::process::executor::exec_handle::impl : utils::noncopyable {
130     /// PID of the process being run.
131     int pid;
132 
133     /// Path to the subprocess-specific work directory.
134     fs::path control_directory;
135 
136     /// Path to the subprocess's stdout file.
137     const fs::path stdout_file;
138 
139     /// Path to the subprocess's stderr file.
140     const fs::path stderr_file;
141 
142     /// Start time.
143     datetime::timestamp start_time;
144 
145     /// User the subprocess is running as if different than the current one.
146     const optional< passwd::user > unprivileged_user;
147 
148     /// Timer to kill the subprocess on activation.
149     process::deadline_killer timer;
150 
151     /// Number of owners of the on-disk state.
152     executor::detail::refcnt_t state_owners;
153 
154     /// Constructor.
155     ///
156     /// \param pid_ PID of the forked process.
157     /// \param control_directory_ Path to the subprocess-specific work
158     ///     directory.
159     /// \param stdout_file_ Path to the subprocess's stdout file.
160     /// \param stderr_file_ Path to the subprocess's stderr file.
161     /// \param start_time_ Timestamp of when this object was constructed.
162     /// \param timeout Maximum amount of time the subprocess can run for.
163     /// \param unprivileged_user_ User the subprocess is running as if
164     ///     different than the current one.
165     /// \param [in,out] state_owners_ Number of owners of the on-disk state.
166     ///     For first-time processes, this should be a new counter set to 0;
167     ///     for followup processes, this should point to the same counter used
168     ///     by the preceding process.
implutils::process::executor::exec_handle::impl169     impl(const int pid_,
170          const fs::path& control_directory_,
171          const fs::path& stdout_file_,
172          const fs::path& stderr_file_,
173          const datetime::timestamp& start_time_,
174          const datetime::delta& timeout,
175          const optional< passwd::user > unprivileged_user_,
176          executor::detail::refcnt_t state_owners_) :
177         pid(pid_),
178         control_directory(control_directory_),
179         stdout_file(stdout_file_),
180         stderr_file(stderr_file_),
181         start_time(start_time_),
182         unprivileged_user(unprivileged_user_),
183         timer(timeout, pid_),
184         state_owners(state_owners_)
185     {
186         (*state_owners)++;
187         POST(*state_owners > 0);
188     }
189 };
190 
191 
192 /// Constructor.
193 ///
194 /// \param pimpl Constructed internal implementation.
exec_handle(std::shared_ptr<impl> pimpl)195 executor::exec_handle::exec_handle(std::shared_ptr< impl > pimpl) :
196     _pimpl(pimpl)
197 {
198 }
199 
200 
201 /// Destructor.
~exec_handle(void)202 executor::exec_handle::~exec_handle(void)
203 {
204 }
205 
206 
207 /// Returns the PID of the process being run.
208 ///
209 /// \return A PID.
210 int
pid(void) const211 executor::exec_handle::pid(void) const
212 {
213     return _pimpl->pid;
214 }
215 
216 
217 /// Returns the path to the subprocess-specific control directory.
218 ///
219 /// This is where the executor may store control files.
220 ///
221 /// \return The path to a directory that exists until cleanup() is called.
222 fs::path
control_directory(void) const223 executor::exec_handle::control_directory(void) const
224 {
225     return _pimpl->control_directory;
226 }
227 
228 
229 /// Returns the path to the subprocess-specific work directory.
230 ///
231 /// This is guaranteed to be clear of files created by the executor.
232 ///
233 /// \return The path to a directory that exists until cleanup() is called.
234 fs::path
work_directory(void) const235 executor::exec_handle::work_directory(void) const
236 {
237     return _pimpl->control_directory / detail::work_subdir;
238 }
239 
240 
241 /// Returns the path to the subprocess's stdout file.
242 ///
243 /// \return The path to a file that exists until cleanup() is called.
244 const fs::path&
stdout_file(void) const245 executor::exec_handle::stdout_file(void) const
246 {
247     return _pimpl->stdout_file;
248 }
249 
250 
251 /// Returns the path to the subprocess's stderr file.
252 ///
253 /// \return The path to a file that exists until cleanup() is called.
254 const fs::path&
stderr_file(void) const255 executor::exec_handle::stderr_file(void) const
256 {
257     return _pimpl->stderr_file;
258 }
259 
260 
261 /// Internal implementation for the exit_handle class.
262 struct utils::process::executor::exit_handle::impl : utils::noncopyable {
263     /// Original PID of the terminated subprocess.
264     ///
265     /// Note that this PID is no longer valid and cannot be used on system
266     /// tables!
267     const int original_pid;
268 
269     /// Termination status of the subprocess, or none if it timed out.
270     const optional< process::status > status;
271 
272     /// The user the process ran as, if different than the current one.
273     const optional< passwd::user > unprivileged_user;
274 
275     /// Timestamp of when the subprocess was spawned.
276     const datetime::timestamp start_time;
277 
278     /// Timestamp of when wait() or wait_any() returned this object.
279     const datetime::timestamp end_time;
280 
281     /// Path to the subprocess-specific work directory.
282     const fs::path control_directory;
283 
284     /// Path to the subprocess's stdout file.
285     const fs::path stdout_file;
286 
287     /// Path to the subprocess's stderr file.
288     const fs::path stderr_file;
289 
290     /// Number of owners of the on-disk state.
291     ///
292     /// This will be 1 if this exit_handle is the last holder of the on-disk
293     /// state, in which case cleanup() invocations will wipe the disk state.
294     /// For all other cases, this will hold a higher value.
295     detail::refcnt_t state_owners;
296 
297     /// Mutable pointer to the corresponding executor state.
298     ///
299     /// This object references a member of the executor_handle that yielded this
300     /// exit_handle instance.  We need this direct access to clean up after
301     /// ourselves when the handle is destroyed.
302     exec_handles_map& all_exec_handles;
303 
304     /// Whether the subprocess state has been cleaned yet or not.
305     ///
306     /// Used to keep track of explicit calls to the public cleanup().
307     bool cleaned;
308 
309     /// Constructor.
310     ///
311     /// \param original_pid_ Original PID of the terminated subprocess.
312     /// \param status_ Termination status of the subprocess, or none if
313     ///     timed out.
314     /// \param unprivileged_user_ The user the process ran as, if different than
315     ///     the current one.
316     /// \param start_time_ Timestamp of when the subprocess was spawned.
317     /// \param end_time_ Timestamp of when wait() or wait_any() returned this
318     ///     object.
319     /// \param control_directory_ Path to the subprocess-specific work
320     ///     directory.
321     /// \param stdout_file_ Path to the subprocess's stdout file.
322     /// \param stderr_file_ Path to the subprocess's stderr file.
323     /// \param [in,out] state_owners_ Number of owners of the on-disk state.
324     /// \param [in,out] all_exec_handles_ Global object keeping track of all
325     ///     active executions for an executor.  This is a pointer to a member of
326     ///     the executor_handle object.
implutils::process::executor::exit_handle::impl327     impl(const int original_pid_,
328          const optional< process::status > status_,
329          const optional< passwd::user > unprivileged_user_,
330          const datetime::timestamp& start_time_,
331          const datetime::timestamp& end_time_,
332          const fs::path& control_directory_,
333          const fs::path& stdout_file_,
334          const fs::path& stderr_file_,
335          detail::refcnt_t state_owners_,
336          exec_handles_map& all_exec_handles_) :
337         original_pid(original_pid_), status(status_),
338         unprivileged_user(unprivileged_user_),
339         start_time(start_time_), end_time(end_time_),
340         control_directory(control_directory_),
341         stdout_file(stdout_file_), stderr_file(stderr_file_),
342         state_owners(state_owners_),
343         all_exec_handles(all_exec_handles_), cleaned(false)
344     {
345     }
346 
347     /// Destructor.
~implutils::process::executor::exit_handle::impl348     ~impl(void)
349     {
350         if (!cleaned) {
351             LW(F("Implicitly cleaning up exit_handle for exec_handle %s; "
352                  "ignoring errors!") % original_pid);
353             try {
354                 cleanup();
355             } catch (const std::runtime_error& error) {
356                 LE(F("Subprocess cleanup failed: %s") % error.what());
357             }
358         }
359     }
360 
361     /// Cleans up the subprocess on-disk state.
362     ///
363     /// \throw engine::error If the cleanup fails, especially due to the
364     ///     inability to remove the work directory.
365     void
cleanuputils::process::executor::exit_handle::impl366     cleanup(void)
367     {
368         PRE(*state_owners > 0);
369         if (*state_owners == 1) {
370             LI(F("Cleaning up exit_handle for exec_handle %s") % original_pid);
371             fs::rm_r(control_directory);
372         } else {
373             LI(F("Not cleaning up exit_handle for exec_handle %s; "
374                  "%s owners left") % original_pid % (*state_owners - 1));
375         }
376         // We must decrease our reference only after we have successfully
377         // cleaned up the control directory.  Otherwise, the rm_r call would
378         // throw an exception, which would in turn invoke the implicit cleanup
379         // from the destructor, which would make us crash due to an invalid
380         // reference count.
381         (*state_owners)--;
382         // Marking this object as clean here, even if we did not do actually the
383         // cleaning above, is fine (albeit a bit confusing).  Note that "another
384         // owner" refers to a handle for a different PID, so that handle will be
385         // the one issuing the cleanup.
386         all_exec_handles.erase(original_pid);
387         cleaned = true;
388     }
389 };
390 
391 
392 /// Constructor.
393 ///
394 /// \param pimpl Constructed internal implementation.
exit_handle(std::shared_ptr<impl> pimpl)395 executor::exit_handle::exit_handle(std::shared_ptr< impl > pimpl) :
396     _pimpl(pimpl)
397 {
398 }
399 
400 
401 /// Destructor.
~exit_handle(void)402 executor::exit_handle::~exit_handle(void)
403 {
404 }
405 
406 
407 /// Cleans up the subprocess status.
408 ///
409 /// This function should be called explicitly as it provides the means to
410 /// control any exceptions raised during cleanup.  Do not rely on the destructor
411 /// to clean things up.
412 ///
413 /// \throw engine::error If the cleanup fails, especially due to the inability
414 ///     to remove the work directory.
415 void
cleanup(void)416 executor::exit_handle::cleanup(void)
417 {
418     PRE(!_pimpl->cleaned);
419     _pimpl->cleanup();
420     POST(_pimpl->cleaned);
421 }
422 
423 
424 /// Gets the current number of owners of the on-disk data.
425 ///
426 /// \return A shared reference counter.  Even though this function is marked as
427 /// const, the return value is intentionally mutable because we need to update
428 /// reference counts from different but related processes.  This is why this
429 /// method is not public.
430 std::shared_ptr< std::size_t >
state_owners(void) const431 executor::exit_handle::state_owners(void) const
432 {
433     return _pimpl->state_owners;
434 }
435 
436 
437 /// Returns the original PID corresponding to the terminated subprocess.
438 ///
439 /// \return An exec_handle.
440 int
original_pid(void) const441 executor::exit_handle::original_pid(void) const
442 {
443     return _pimpl->original_pid;
444 }
445 
446 
447 /// Returns the process termination status of the subprocess.
448 ///
449 /// \return A process termination status, or none if the subprocess timed out.
450 const optional< process::status >&
status(void) const451 executor::exit_handle::status(void) const
452 {
453     return _pimpl->status;
454 }
455 
456 
457 /// Returns the user the process ran as if different than the current one.
458 ///
459 /// \return None if the credentials of the process were the same as the current
460 /// one, or else a user.
461 const optional< passwd::user >&
unprivileged_user(void) const462 executor::exit_handle::unprivileged_user(void) const
463 {
464     return _pimpl->unprivileged_user;
465 }
466 
467 
468 /// Returns the timestamp of when the subprocess was spawned.
469 ///
470 /// \return A timestamp.
471 const datetime::timestamp&
start_time(void) const472 executor::exit_handle::start_time(void) const
473 {
474     return _pimpl->start_time;
475 }
476 
477 
478 /// Returns the timestamp of when wait() or wait_any() returned this object.
479 ///
480 /// \return A timestamp.
481 const datetime::timestamp&
end_time(void) const482 executor::exit_handle::end_time(void) const
483 {
484     return _pimpl->end_time;
485 }
486 
487 
488 /// Returns the path to the subprocess-specific control directory.
489 ///
490 /// This is where the executor may store control files.
491 ///
492 /// \return The path to a directory that exists until cleanup() is called.
493 fs::path
control_directory(void) const494 executor::exit_handle::control_directory(void) const
495 {
496     return _pimpl->control_directory;
497 }
498 
499 
500 /// Returns the path to the subprocess-specific work directory.
501 ///
502 /// This is guaranteed to be clear of files created by the executor.
503 ///
504 /// \return The path to a directory that exists until cleanup() is called.
505 fs::path
work_directory(void) const506 executor::exit_handle::work_directory(void) const
507 {
508     return _pimpl->control_directory / detail::work_subdir;
509 }
510 
511 
512 /// Returns the path to the subprocess's stdout file.
513 ///
514 /// \return The path to a file that exists until cleanup() is called.
515 const fs::path&
stdout_file(void) const516 executor::exit_handle::stdout_file(void) const
517 {
518     return _pimpl->stdout_file;
519 }
520 
521 
522 /// Returns the path to the subprocess's stderr file.
523 ///
524 /// \return The path to a file that exists until cleanup() is called.
525 const fs::path&
stderr_file(void) const526 executor::exit_handle::stderr_file(void) const
527 {
528     return _pimpl->stderr_file;
529 }
530 
531 
532 /// Internal implementation for the executor_handle.
533 ///
534 /// Because the executor is a singleton, these essentially is a container for
535 /// global variables.
536 struct utils::process::executor::executor_handle::impl : utils::noncopyable {
537     /// Numeric counter of executed subprocesses.
538     ///
539     /// This is used to generate a unique identifier for each subprocess as an
540     /// easy mechanism to discern their unique work directories.
541     size_t last_subprocess;
542 
543     /// Interrupts handler.
544     std::auto_ptr< signals::interrupts_handler > interrupts_handler;
545 
546     /// Root work directory for all executed subprocesses.
547     std::auto_ptr< fs::auto_directory > root_work_directory;
548 
549     /// Mapping of PIDs to the data required at run time.
550     exec_handles_map all_exec_handles;
551 
552     /// Former members of all_exec_handles removed due to PID reuse.
553     std::forward_list<exec_handle> stale_exec_handles;
554 
555     /// Whether the executor state has been cleaned yet or not.
556     ///
557     /// Used to keep track of explicit calls to the public cleanup().
558     bool cleaned;
559 
560     /// Constructor.
implutils::process::executor::executor_handle::impl561     impl(void) :
562         last_subprocess(0),
563         interrupts_handler(new signals::interrupts_handler()),
564         root_work_directory(new fs::auto_directory(
565             fs::auto_directory::mkdtemp_public(work_directory_template))),
566         all_exec_handles(),
567         stale_exec_handles(),
568         cleaned(false)
569     {
570     }
571 
572     /// Destructor.
~implutils::process::executor::executor_handle::impl573     ~impl(void)
574     {
575         if (!cleaned) {
576             LW("Implicitly cleaning up executor; ignoring errors!");
577             try {
578                 cleanup();
579                 cleaned = true;
580             } catch (const std::runtime_error& error) {
581                 LE(F("Executor global cleanup failed: %s") % error.what());
582             }
583         }
584     }
585 
586     /// Cleans up the executor state.
587     void
cleanuputils::process::executor::executor_handle::impl588     cleanup(void)
589     {
590         PRE(!cleaned);
591 
592         for (exec_handles_map::const_iterator iter = all_exec_handles.begin();
593              iter != all_exec_handles.end(); ++iter) {
594             const int& pid = (*iter).first;
595             const exec_handle& data = (*iter).second;
596 
597             process::terminate_group(pid);
598             int status;
599             if (::waitpid(pid, &status, 0) == -1) {
600                 // Should not happen.
601                 LW(F("Failed to wait for PID %s") % pid);
602             }
603 
604             try {
605                 fs::rm_r(data.control_directory());
606             } catch (const fs::error& e) {
607                 LE(F("Failed to clean up subprocess work directory %s: %s") %
608                    data.control_directory() % e.what());
609             }
610         }
611         all_exec_handles.clear();
612 
613         for (auto iter : stale_exec_handles) {
614             // The process already exited, so no need to kill and wait.
615             try {
616                 fs::rm_r(iter.control_directory());
617             } catch (const fs::error& e) {
618                 LE(F("Failed to clean up stale subprocess work directory "
619                     "%s: %s") % iter.control_directory() % e.what());
620             }
621         }
622         stale_exec_handles.clear();
623 
624         try {
625             // The following only causes the work directory to be deleted, not
626             // any of its contents, so we expect this to always succeed.  This
627             // *should* be sufficient because, in the loop above, we have
628             // individually wiped the subdirectories of any still-unclean
629             // subprocesses.
630             root_work_directory->cleanup();
631         } catch (const fs::error& e) {
632             LE(F("Failed to clean up executor work directory %s: %s; "
633                 "this could be an internal error or a buggy test") %
634                 root_work_directory->directory() % e.what());
635         }
636         root_work_directory.reset(NULL);
637 
638         interrupts_handler->unprogram();
639         interrupts_handler.reset(NULL);
640     }
641 
642     /// Common code to run after any of the wait calls.
643     ///
644     /// \param original_pid The PID of the terminated subprocess.
645     /// \param status The exit status of the terminated subprocess.
646     ///
647     /// \return A pointer to an object describing the waited-for subprocess.
648     executor::exit_handle
post_waitutils::process::executor::executor_handle::impl649     post_wait(const int original_pid, const process::status& status)
650     {
651         PRE(original_pid == status.dead_pid());
652         LI(F("Waited for subprocess with exec_handle %s") % original_pid);
653 
654         process::terminate_group(status.dead_pid());
655 
656         const exec_handles_map::iterator iter = all_exec_handles.find(
657             original_pid);
658         exec_handle& data = (*iter).second;
659         data._pimpl->timer.unprogram();
660 
661         // It is tempting to assert here (and old code did) that, if the timer
662         // has fired, the process has been forcibly killed by us.  This is not
663         // always the case though: for short-lived processes and with very short
664         // timeouts (think 1ms), it is possible for scheduling decisions to
665         // allow the subprocess to finish while at the same time cause the timer
666         // to fire.  So we do not assert this any longer and just rely on the
667         // timer expiration to check if the process timed out or not.  If the
668         // process did finish but the timer expired... oh well, we do not detect
669         // this correctly but we don't care because this should not really
670         // happen.
671 
672         if (!fs::exists(data.stdout_file())) {
673             std::ofstream new_stdout(data.stdout_file().c_str());
674         }
675         if (!fs::exists(data.stderr_file())) {
676             std::ofstream new_stderr(data.stderr_file().c_str());
677         }
678 
679         return exit_handle(std::shared_ptr< exit_handle::impl >(
680             new exit_handle::impl(
681                 data.pid(),
682                 data._pimpl->timer.fired() ?
683                     none : utils::make_optional(status),
684                 data._pimpl->unprivileged_user,
685                 data._pimpl->start_time, datetime::timestamp::now(),
686                 data.control_directory(),
687                 data.stdout_file(),
688                 data.stderr_file(),
689                 data._pimpl->state_owners,
690                 all_exec_handles)));
691     }
692 
693     executor::exit_handle
reaputils::process::executor::executor_handle::impl694     reap(const pid_t original_pid)
695     {
696         const exec_handles_map::iterator iter = all_exec_handles.find(
697             original_pid);
698         exec_handle& data = (*iter).second;
699         data._pimpl->timer.unprogram();
700 
701         if (!fs::exists(data.stdout_file())) {
702             std::ofstream new_stdout(data.stdout_file().c_str());
703         }
704         if (!fs::exists(data.stderr_file())) {
705             std::ofstream new_stderr(data.stderr_file().c_str());
706         }
707 
708         return exit_handle(std::shared_ptr< exit_handle::impl >(
709             new exit_handle::impl(
710                 data.pid(),
711                 none,
712                 data._pimpl->unprivileged_user,
713                 data._pimpl->start_time, datetime::timestamp::now(),
714                 data.control_directory(),
715                 data.stdout_file(),
716                 data.stderr_file(),
717                 data._pimpl->state_owners,
718                 all_exec_handles)));
719     }
720 };
721 
722 
723 /// Constructor.
executor_handle(void)724 executor::executor_handle::executor_handle(void) throw() : _pimpl(new impl())
725 {
726 }
727 
728 
729 /// Destructor.
~executor_handle(void)730 executor::executor_handle::~executor_handle(void)
731 {
732 }
733 
734 
735 /// Queries the path to the root of the work directory for all subprocesses.
736 ///
737 /// \return A path.
738 const fs::path&
root_work_directory(void) const739 executor::executor_handle::root_work_directory(void) const
740 {
741     return _pimpl->root_work_directory->directory();
742 }
743 
744 
745 /// Cleans up the executor state.
746 ///
747 /// This function should be called explicitly as it provides the means to
748 /// control any exceptions raised during cleanup.  Do not rely on the destructor
749 /// to clean things up.
750 ///
751 /// \throw engine::error If there are problems cleaning up the executor.
752 void
cleanup(void)753 executor::executor_handle::cleanup(void)
754 {
755     PRE(!_pimpl->cleaned);
756     _pimpl->cleanup();
757     _pimpl->cleaned = true;
758 }
759 
760 
761 /// Initializes the executor.
762 ///
763 /// \pre This function can only be called if there is no other executor_handle
764 /// object alive.
765 ///
766 /// \return A handle to the operations of the executor.
767 executor::executor_handle
setup(void)768 executor::setup(void)
769 {
770     return executor_handle();
771 }
772 
773 
774 /// Pre-helper for the spawn() method.
775 ///
776 /// \return The created control directory for the subprocess.
777 fs::path
spawn_pre(void)778 executor::executor_handle::spawn_pre(void)
779 {
780     signals::check_interrupt();
781 
782     ++_pimpl->last_subprocess;
783 
784     const fs::path control_directory =
785         _pimpl->root_work_directory->directory() /
786         (F("%s") % _pimpl->last_subprocess);
787     fs::mkdir_p(control_directory / detail::work_subdir, 0755);
788 
789     return control_directory;
790 }
791 
792 
793 /// Post-helper for the spawn() method.
794 ///
795 /// \param control_directory Control directory as returned by spawn_pre().
796 /// \param stdout_file Path to the subprocess' stdout.
797 /// \param stderr_file Path to the subprocess' stderr.
798 /// \param timeout Maximum amount of time the subprocess can run for.
799 /// \param unprivileged_user If not none, user to switch to before execution.
800 /// \param child The process created by spawn().
801 ///
802 /// \return The execution handle of the started subprocess.
803 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)804 executor::executor_handle::spawn_post(
805     const fs::path& control_directory,
806     const fs::path& stdout_file,
807     const fs::path& stderr_file,
808     const datetime::delta& timeout,
809     const optional< passwd::user > unprivileged_user,
810     std::auto_ptr< process::child > child)
811 {
812     const exec_handle handle(std::shared_ptr< exec_handle::impl >(
813         new exec_handle::impl(
814             child->pid(),
815             control_directory,
816             stdout_file,
817             stderr_file,
818             datetime::timestamp::now(),
819             timeout,
820             unprivileged_user,
821             detail::refcnt_t(new detail::refcnt_t::element_type(0)))));
822     const auto value = exec_handles_map::value_type(handle.pid(), handle);
823     auto insert_pair = _pimpl->all_exec_handles.insert(value);
824     if (!insert_pair.second) {
825         LI(F("PID %s already in all_exec_handles") % handle.pid());
826         _pimpl->stale_exec_handles.push_front(insert_pair.first->second);
827         _pimpl->all_exec_handles.erase(insert_pair.first);
828         insert_pair = _pimpl->all_exec_handles.insert(value);
829         INV_MSG(insert_pair.second, F("PID %s still in all_exec_handles") %
830             handle.pid());
831     }
832     LI(F("Spawned subprocess with exec_handle %s") % handle.pid());
833     return handle;
834 }
835 
836 
837 /// Pre-helper for the spawn_followup() method.
838 void
spawn_followup_pre(void)839 executor::executor_handle::spawn_followup_pre(void)
840 {
841     signals::check_interrupt();
842 }
843 
844 
845 /// Post-helper for the spawn_followup() method.
846 ///
847 /// \param base Exit handle of the subprocess to use as context.
848 /// \param timeout Maximum amount of time the subprocess can run for.
849 /// \param child The process created by spawn_followup().
850 ///
851 /// \return The execution handle of the started subprocess.
852 executor::exec_handle
spawn_followup_post(const exit_handle & base,const datetime::delta & timeout,std::auto_ptr<process::child> child)853 executor::executor_handle::spawn_followup_post(
854     const exit_handle& base,
855     const datetime::delta& timeout,
856     std::auto_ptr< process::child > child)
857 {
858     INV(*base.state_owners() > 0);
859     const exec_handle handle(std::shared_ptr< exec_handle::impl >(
860         new exec_handle::impl(
861             child->pid(),
862             base.control_directory(),
863             base.stdout_file(),
864             base.stderr_file(),
865             datetime::timestamp::now(),
866             timeout,
867             base.unprivileged_user(),
868             base.state_owners())));
869     const auto value = exec_handles_map::value_type(handle.pid(), handle);
870     auto insert_pair = _pimpl->all_exec_handles.insert(value);
871     if (!insert_pair.second) {
872         LI(F("PID %s already in all_exec_handles") % handle.pid());
873         _pimpl->stale_exec_handles.push_front(insert_pair.first->second);
874         _pimpl->all_exec_handles.erase(insert_pair.first);
875         insert_pair = _pimpl->all_exec_handles.insert(value);
876         INV_MSG(insert_pair.second, F("PID %s still in all_exec_handles") %
877             handle.pid());
878     }
879     LI(F("Spawned subprocess with exec_handle %s") % handle.pid());
880     return handle;
881 }
882 
883 
884 /// Waits for completion of any forked process.
885 ///
886 /// \param exec_handle The handle of the process to wait for.
887 ///
888 /// \return A pointer to an object describing the waited-for subprocess.
889 executor::exit_handle
wait(const exec_handle exec_handle)890 executor::executor_handle::wait(const exec_handle exec_handle)
891 {
892     signals::check_interrupt();
893     const process::status status = process::wait(exec_handle.pid());
894     return _pimpl->post_wait(exec_handle.pid(), status);
895 }
896 
897 
898 /// Waits for completion of any forked process.
899 ///
900 /// \return A pointer to an object describing the waited-for subprocess.
901 executor::exit_handle
wait_any(void)902 executor::executor_handle::wait_any(void)
903 {
904     signals::check_interrupt();
905     const process::status status = process::wait_any();
906     return _pimpl->post_wait(status.dead_pid(), status);
907 }
908 
909 
910 /// Forms exit_handle for the given PID subprocess.
911 ///
912 /// Can be used in the cases when we want to do cleanup(s) of a killed test
913 /// subprocess, but we do not have exit handle as we usually do after normal
914 /// wait mechanism.
915 ///
916 /// \return A pointer to an object describing the subprocess.
917 executor::exit_handle
reap(const int pid)918 executor::executor_handle::reap(const int pid)
919 {
920     return _pimpl->reap(pid);
921 }
922 
923 
924 /// Checks if an interrupt has fired.
925 ///
926 /// Calls to this function should be sprinkled in strategic places through the
927 /// code protected by an interrupts_handler object.
928 ///
929 /// This is just a wrapper over signals::check_interrupt() to avoid leaking this
930 /// dependency to the caller.
931 ///
932 /// \throw signals::interrupted_error If there has been an interrupt.
933 void
check_interrupt(void) const934 executor::executor_handle::check_interrupt(void) const
935 {
936     signals::check_interrupt();
937 }
938