process-cpp: /build/process-cpp/src/process-cpp-3.0.1/src/core/posix/child_process.cpp Source File

process-cpp 3.0.0 A simple convenience library for handling processes in C++11.

child_process.cpp
Go to the documentation of this file.
1 /*
2 * Copyright © 2013 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
19 #include <core/posix/child_process.h>
20
21 #include <boost/iostreams/device/file_descriptor.hpp>
22 #include <boost/iostreams/stream.hpp>
23
24 #include <atomic>
25 #include <fstream>
26 #include <mutex>
27 #include <unordered_map>
28
29 #include <fcntl.h>
30 #include <poll.h>
31 #include <unistd.h>
32
33 #include <sys/eventfd.h>
34 #include <sys/signalfd.h>
35
36 namespace io = boost::iostreams;
37
38 namespace
39 {
40
41 struct DeathObserverImpl : public core::posix::ChildProcess::DeathObserver
42 {
43 DeathObserverImpl(const std::shared_ptr<core::posix::SignalTrap>& trap)
44 : on_sig_child_connection
45 {
46 trap->signal_raised().connect([this](core::posix::Signal signal)
47 {
48 switch (signal)
49 {
50 case core::posix::Signal::sig_chld:
51 on_sig_child();
52 break;
53 default:
54 break;
55 }
56 })
57 }
58 {
59 if (!trap->has(core::posix::Signal::sig_chld))
60 throw std::logic_error(
61 "DeathObserver::DeathObserverImpl: Given SignalTrap"
62 " instance does not trap Signal::sig_chld.");
63 }
64
65 bool add(const core::posix::ChildProcess& process) override
66 {
67 if (process.pid() == -1)
68 return false;
69
70 std::lock_guard<std::mutex> lg(guard);
71
72 bool added = false;
73 auto new_process = std::make_pair(process.pid(), process);
74 std::tie(std::ignore, added) = children.insert(new_process);
75
76 if (added)
77 {
78 // The process may have died between it's instantiation and it
79 // being added to the children map. Check that it's still alive.
80 int status{-1};
81 if (::waitpid(process.pid(), &status, WNOHANG) != 0) // child no longer alive
82 {
83 // we missed the SIGCHLD signal so we must now manually
84 // inform our subscribers.
85 signals.child_died(new_process.second);
86 children.erase(new_process.first);
87 return false;
88 }
89 }
90
91 return added;
92 }
93
94 bool has(const core::posix::ChildProcess& process) const override
95 {
96 std::lock_guard<std::mutex> lg(guard);
97 return children.count(process.pid()) > 0;
98 }
99
100 const core::Signal<core::posix::ChildProcess>& child_died() const override
101 {
102 return signals.child_died;
103 }
104
105 void on_sig_child() override
106 {
107 pid_t pid{-1}; int status{-1};
108 while (true)
109 {
110 pid = ::waitpid(0, &status, WNOHANG);
111
112 if (pid == -1)
113 {
114 if (errno == ECHILD)
115 {
116 break; // No more children
117 }
118 continue; // Ignore stray SIGCHLD signals
119 }
120 else if (pid == 0)
121 {
122 break; // No more children
123 }
124 else
125 {
126 std::lock_guard<std::mutex> lg(guard);
127 auto it = children.find(pid);
128
129 if (it != children.end())
130 {
131 if (WIFSIGNALED(status) || WIFEXITED(status))
132 {
133 signals.child_died(it->second);
134 children.erase(it);
135 }
136 }
137 }
138 }
139 }
140
141 mutable std::mutex guard;
142 std::unordered_map<pid_t, core::posix::ChildProcess> children;
143 core::ScopedConnection on_sig_child_connection;
144 struct
145 {
146 core::Signal<core::posix::ChildProcess> child_died;
147 } signals;
148 };
149 }
150
151 std::unique_ptr<core::posix::ChildProcess::DeathObserver>
152 core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(
153 std::shared_ptr<core::posix::SignalTrap> trap)
154 {
155 static std::atomic<bool> has_been_created_once{false};
156
157 if (has_been_created_once.exchange(true))
158 throw std::runtime_error
159 {
160 "DeathObserver::create_once_with_signal_trap: "
161 "Cannot create more than one instance."
162 };
163
164 try
165 {
166 std::unique_ptr<core::posix::ChildProcess::DeathObserver> result
167 {
168 new DeathObserverImpl{trap}
169 };
170
171 return result;
172 } catch(...)
173 {
174 // We make sure that a throwing c'tor does not impact our ability to
175 // retry creation of a DeathObserver instance.
176 has_been_created_once.store(false);
177
178 std::rethrow_exception(std::current_exception());
179 }
180
181 assert(false && "We should never reach here.");
182
183 // Silence the compiler.
184 return std::unique_ptr<core::posix::ChildProcess::DeathObserver>{};
185 }
186
187 namespace core
188 {
189 namespace posix
190 {
191 ChildProcess::Pipe ChildProcess::Pipe::invalid()
192 {
193 static Pipe p;
194 static std::once_flag flag;
195
196 std::call_once(flag, [&]() { p.close_read_fd(); p.close_write_fd(); });
197
198 return p;
199 }
200
201 ChildProcess::Pipe::Pipe()
202 {
203 int rc = ::pipe(fds);
204
205 if (rc == -1)
206 throw std::system_error(errno, std::system_category());
207 }
208
209 ChildProcess::Pipe::Pipe(const ChildProcess::Pipe& rhs) : fds{-1, -1}
210 {
211 if (rhs.fds[0] != -1)
212 fds[0] = ::dup(rhs.fds[0]);
213
214 if (rhs.fds[1] != -1)
215 fds[1] = ::dup(rhs.fds[1]);
216 }
217
218 ChildProcess::Pipe::~Pipe()
219 {
220 if (fds[0] != -1)
221 ::close(fds[0]);
222 if (fds[1] != -1)
223 ::close(fds[1]);
224 }
225
226 int ChildProcess::Pipe::read_fd() const
227 {
228 return fds[0];
229 }
230
231 void ChildProcess::Pipe::close_read_fd()
232 {
233 if (fds[0] != -1)
234 {
235 ::close(fds[0]);
236 fds[0] = -1;
237 }
238 }
239
240 int ChildProcess::Pipe::write_fd() const
241 {
242 return fds[1];
243 }
244
245 void ChildProcess::Pipe::close_write_fd()
246 {
247 if (fds[1] != -1)
248 {
249 ::close(fds[1]);
250 fds[1] = -1;
251 }
252 }
253
254 ChildProcess::Pipe& ChildProcess::Pipe::operator=(const ChildProcess::Pipe& rhs)
255 {
256 if (fds[0] != -1)
257 ::close(fds[0]);
258 if (fds[1] != -1)
259 ::close(fds[1]);
260
261 if (rhs.fds[0] != -1)
262 fds[0] = ::dup(rhs.fds[0]);
263 else
264 fds[0] = -1;
265 if (rhs.fds[1] != -1)
266 fds[1] = ::dup(rhs.fds[1]);
267 else
268 fds[1] = -1;
269
270 return *this;
271 }
272
273 struct ChildProcess::Private
274 {
275 // stdin and stdout are always "relative" to the childprocess, i.e., we
276 // write to stdin of the child process and read from its stdout.
277 Private(pid_t pid,
278 const ChildProcess::Pipe& stderr,
279 const ChildProcess::Pipe& stdin,
280 const ChildProcess::Pipe& stdout)
281 : pipes{stderr, stdin, stdout},
282 serr(pipes.stderr.read_fd(), io::never_close_handle),
283 sin(pipes.stdin.write_fd(), io::never_close_handle),
284 sout(pipes.stdout.read_fd(), io::never_close_handle),
285 cerr(&serr),
286 cin(&sin),
287 cout(&sout),
288 original_parent_pid(::getpid()),
289 original_child_pid(pid)
290 {
291 }
292
293 ~Private()
294 {
295 // Check if we are in the original parent process.
296 if (original_parent_pid == getpid())
297 {
298 // If so, check if we are considering a valid pid here.
299 // If so, we kill the original child.
300 if (original_child_pid != -1)
301 ::kill(original_child_pid, SIGKILL);
302 }
303 }
304
305 struct
306 {
307 ChildProcess::Pipe stdin;
308 ChildProcess::Pipe stdout;
309 ChildProcess::Pipe stderr;
310 } pipes;
311 io::stream_buffer<io::file_descriptor_source> serr;
312 io::stream_buffer<io::file_descriptor_sink> sin;
313 io::stream_buffer<io::file_descriptor_source> sout;
314 std::istream cerr;
315 std::ostream cin;
316 std::istream cout;
317
318 // We need to store the original parent pid as we might have been forked
319 // and with our automatic cleanup in place, it might happen that the d'tor
320 // is called from the child process.
321 pid_t original_parent_pid;
322 pid_t original_child_pid;
323 };
324
325 ChildProcess ChildProcess::invalid()
326 {
327 // We take the init process as child.
328 static const pid_t invalid_pid = 1;
329 return ChildProcess(invalid_pid, Pipe::invalid(), Pipe::invalid(), Pipe::invalid());
330 }
331
332 ChildProcess::ChildProcess(pid_t pid,
333 const ChildProcess::Pipe& stdin_pipe,
334 const ChildProcess::Pipe& stdout_pipe,
335 const ChildProcess::Pipe& stderr_pipe)
336 : Process(pid),
337 d(new Private{pid, stdin_pipe, stdout_pipe, stderr_pipe})
338 {
339 }
340
341 ChildProcess::~ChildProcess()
342 {
343 }
344
345 wait::Result ChildProcess::wait_for(const wait::Flags& flags)
346 {
347 int status = -1;
348 pid_t result_pid = ::waitpid(pid(), std::addressof(status), static_cast<int>(flags));
349
350 if (result_pid == -1)
351 throw std::system_error(errno, std::system_category());
352
353 wait::Result result;
354
355 if (result_pid == 0)
356 {
357 result.status = wait::Result::Status::no_state_change;
358 return result;
359 }
360
361 if (WIFEXITED(status))
362 {
363 result.status = wait::Result::Status::exited;
364 result.detail.if_exited.status = static_cast<exit::Status>(WEXITSTATUS(status));
365 } else if (WIFSIGNALED(status))
366 {
367 result.status = wait::Result::Status::signaled;
368 result.detail.if_signaled.signal = static_cast<Signal>(WTERMSIG(status));
369 result.detail.if_signaled.core_dumped = WCOREDUMP(status);
370 } else if (WIFSTOPPED(status))
371 {
372 result.status = wait::Result::Status::stopped;
373 result.detail.if_stopped.signal = static_cast<Signal>(WSTOPSIG(status));
374 } else if (WIFCONTINUED(status))
375 {
376 result.status = wait::Result::Status::continued;
377 }
378
379 return result;
380 }
381
382 std::istream& ChildProcess::cerr()
383 {
384 return d->cerr;
385 }
386
387 std::ostream& ChildProcess::cin()
388 {
389 return d->cin;
390 }
391
392 std::istream& ChildProcess::cout()
393 {
394 return d->cout;
395 }
396 }
397 }
core::posix::ChildProcess::Private::sin
io::stream_buffer< io::file_descriptor_sink > sin
Definition: child_process.cpp:312
core::posix::ChildProcess::DeathObserver::has
virtual bool has(const ChildProcess &child) const =0
has checks whether the specified child is observed.
core::posix::wait::Result::if_signaled
struct core::posix::wait::Result::@0::@2 if_signaled
core::posix::StandardStream::empty
@ empty
core::posix::ChildProcess::DeathObserver::add
virtual bool add(const ChildProcess &child)=0
add adds the specified child to the list of observed child processes.
core::posix::ChildProcess::Private::stdout
ChildProcess::Pipe stdout
Definition: child_process.cpp:308
core::posix::wait::Result::if_stopped
struct core::posix::wait::Result::@0::@3 if_stopped
core::posix::ChildProcess::Private::stderr
ChildProcess::Pipe stderr
Definition: child_process.cpp:309
core::posix::ChildProcess::Private::original_parent_pid
pid_t original_parent_pid
Definition: child_process.cpp:321
core::posix::wait::Result::Status::no_state_change
@ no_state_change
No state change occured.
core::posix::ChildProcess::Private::~Private
~Private()
Definition: child_process.cpp:293
core::posix::ChildProcess::invalid
static ChildProcess invalid()
Creates an invalid ChildProcess.
Definition: child_process.cpp:325
core::posix::ChildProcess::Private::cout
std::istream cout
Definition: child_process.cpp:316
core::posix::ChildProcess::DeathObserver
The DeathObserver class observes child process' states and emits a signal when a monitored child has ...
Definition: child_process.h:57
core::posix::exit::Status
Status
The Status enum wrap's the posix exit status.
Definition: exit.h:34
core::posix::ChildProcess::wait_for
wait::Result wait_for(const wait::Flags &flags)
Wait for the child process to change state.
Definition: child_process.cpp:345
core::posix::wait::Result
The Result struct encapsulates the result of waiting for a process state change.
Definition: wait.h:55
core::posix::wait::Result::status
enum core::posix::wait::Result::Status status
core::posix::wait::Result::Status::exited
@ exited
The process exited normally.
core::posix::Signal
Signal
The Signal enum collects the most common POSIX signals.
Definition: signal.h:39
core::posix::ChildProcess::Private::original_child_pid
pid_t original_child_pid
Definition: child_process.cpp:322
core::posix::ChildProcess
The Process class models a child process of this process.
Definition: child_process.h:44
core::posix::ChildProcess::Private::cerr
std::istream cerr
Definition: child_process.cpp:314
core::posix::wait::Result::Status::signaled
@ signaled
The process was signalled and terminated.
core
Definition: child_process.h:32
core::posix::wait::Result::Status::continued
@ continued
The process resumed operation.
core::posix::ChildProcess::Private
Definition: child_process.cpp:274
core::posix::Process::pid
virtual pid_t pid() const
Query the pid of the process.
Definition: process.cpp:59
core::posix::ChildProcess::~ChildProcess
~ChildProcess()
Definition: child_process.cpp:341
core::posix::ChildProcess::Private::Private
Private(pid_t pid, const ChildProcess::Pipe &stderr, const ChildProcess::Pipe &stdin, const ChildProcess::Pipe &stdout)
Definition: child_process.cpp:277
core::posix::ChildProcess::DeathObserver::on_sig_child
virtual void on_sig_child()=0
Checks and reaps all child processes registered with the observer instance.
core::posix::wait::Result::if_exited
struct core::posix::wait::Result::@0::@1 if_exited
core::posix::SignalTrap::signal_raised
virtual core::Signal< Signal > & signal_raised()=0
Emitted whenever a trapped signal is raised by the operating system.
core::posix::ChildProcess::cerr
std::istream & cerr()
Access this process's stderr.
Definition: child_process.cpp:382
core::posix::ChildProcess::DeathObserver::child_died
virtual const core::Signal< ChildProcess > & child_died() const =0
child_died is emitted whenever an observed child ceases to exist.
core::posix::wait::Result::detail
union core::posix::wait::Result::@0 detail
Union of result-specific details.
core::posix::ChildProcess::cin
std::ostream & cin()
Access this process's stdin.
Definition: child_process.cpp:387
core::posix::wait::Result::Status::stopped
@ stopped
The process was signalled and stopped.
core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap
static std::unique_ptr< DeathObserver > create_once_with_signal_trap(std::shared_ptr< SignalTrap > trap)
Creates the unique instance of class DeathObserver.
Definition: child_process.cpp:152
core::posix::SignalTrap::has
virtual bool has(Signal signal)=0
Returns true if the given signal is trapped by this instance.
child_process.h
core::posix::ChildProcess::Private::stdin
ChildProcess::Pipe stdin
Definition: child_process.cpp:307
core::posix::ChildProcess::Private::sout
io::stream_buffer< io::file_descriptor_source > sout
Definition: child_process.cpp:313
core::posix::ChildProcess::Private::serr
io::stream_buffer< io::file_descriptor_source > serr
Definition: child_process.cpp:311
core::posix::ChildProcess::Private::cin
std::ostream cin
Definition: child_process.cpp:315
core::posix::Process
The Process class models a process and possible operations on it.
Definition: process.h:45
core::posix::wait::Flags
Flags
Flags enumerates different behavior when waiting for a child process to change state.
Definition: wait.h:43
core::posix::ChildProcess::cout
std::istream & cout()
Access this process's stdout.
Definition: child_process.cpp:392