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