libclues
Linux C++ Tracing Library
Loading...
Searching...
No Matches
ChildTracee.cxx
1// cosmos
2#include <cosmos/error/ApiError.hxx>
3#include <cosmos/error/RuntimeError.hxx>
4#include <cosmos/formatting.hxx>
5#include <cosmos/fs/filesystem.hxx>
6#include <cosmos/io/ILogger.hxx>
7#include <cosmos/proc/ChildCloner.hxx>
8
9// clues
10#include <clues/ChildTracee.hxx>
11#include <clues/logger.hxx>
12
13namespace clues {
14
15ChildTracee::ChildTracee(Engine &engine, EventConsumer &consumer) :
16 Tracee{engine, consumer} {
17}
18
19void ChildTracee::create(const cosmos::StringVector &args) {
20 /*
21 * Here we have an issue with proper error reporting when the desired
22 * executable cannot be run. The execve() happens in child context and
23 * it is difficult to forward an error indication into parent context.
24 *
25 * ChildCloner offers a feature to forward execve() errors in the
26 * child synchronously to the parent. This doesn't work for our case,
27 * however, since we raise SIGSTOP in the child before executing,
28 * which would cause the `run()` call below to block forever, waiting
29 * for an execve() result.
30 *
31 * Using `fexecve()` would move typical file open errors to an earlier
32 * stage, but it has issues with scripts thats use the shebang to
33 * invoke an interpreter.
34 *
35 * It seems the only sensible thing to do is to check existence of the
36 * target path in a racy way beforehand.
37 */
38 if (args.empty() || !cosmos::fs::which(args[0])) {
39 changeState(State::DEAD);
40 throw cosmos::RuntimeError{
41 cosmos::sprintf("%s: does not exist or is not executable",
42 args.empty() ? "<empty-path>" : args[0].c_str())};
43 }
44
45 cosmos::ChildCloner cloner;
46 cloner.setArgs(args);
47 cloner.setPostForkCB([](const cosmos::ChildCloner &){
48 // this allows our parent to trace us before execve() happens
49 cosmos::signal::raise(cosmos::signal::STOP);
50 });
51 m_child = cloner.run();
52
53 m_child.wait(cosmos::WaitFlags{cosmos::WaitFlag::WAIT_FOR_STOPPED});
55
56 setPID(m_child.pid());
57}
58
59ChildTracee::~ChildTracee() {
60 try {
61 if (m_child.running()) {
62 // make sure we can wait for it
63 try {
64 m_child.kill(cosmos::signal::KILL);
65 } catch( ... ) { }
66 }
67
68 cleanupChild();
69 } catch (const cosmos::CosmosError &ce) {
70 LOG_ERROR("Error detaching from child process PID "
71 << cosmos::to_integral(m_child.pid()) << ":\n\n"
72 << ce.what());
73 }
74}
75
76void ChildTracee::cleanupChild() {
77
78 // this should actually only happen if a ChildTracee is detached
79 // explicitly and we're sending a SIGINT or something like that.
80 if (m_child.running()) {
81 try {
82 // the base class already stored the exit data
83 // this is just for keeping the SubProc state in sync.
84 (void)m_child.wait();
85 } catch (const cosmos::ApiError &error) {
86 if (error.errnum() != cosmos::Errno::NO_CHILD) {
87 // if it is ESRCH then just ignore it, the
88 // Engine already collected the result.
89 //
90 // this step is necessary to clean up the
91 // SubProc's internal state. Otherwise we'd
92 // need to use cosmos::proc::fork() to avoid
93 // using SubProc in the first place.
94 throw;
95 }
96 }
97 }
98}
99
100} // end ns
ChildTracee(Engine &engine, EventConsumer &consumer)
Create a traced process by creating a new process from prog_args
cosmos::SubProc m_child
the sub-process we're tracing
void create(const cosmos::StringVector &args)
Create the child process with the given parameters.
Callback interface for consumers of tracing events.
void setPID(const cosmos::ProcessID tracee)
Sets the tracee PID.
Definition Tracee.cxx:129
@ INJECTED_SIGSTOP
whether we've injected a SIGSTOP that needs to be undone.
Definition Tracee.hxx:64
Tracee(Engine &engine, EventConsumer &consumer, TraceePtr sibling=nullptr)
Definition Tracee.cxx:100
Flags m_flags
These keep track of various state on the tracer side.
Definition Tracee.hxx:442
@ DEAD
the tracee no longer exists.
Definition Tracee.hxx:56