process-cpp  3.0.0
A simple convenience library for handling processes in C++11.
fork.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/exit.h>
20 #include <core/posix/fork.h>
21 
22 #include "backtrace.h"
23 
24 #include <iomanip>
25 #include <iostream>
26 #include <system_error>
27 
28 #include <unistd.h>
29 
30 namespace
31 {
32 void redirect_stream_to_fd(int fd, int stream)
33 {
34  auto rc = ::dup2(fd, stream);
35  if (rc == -1)
36  throw std::system_error(errno, std::system_category());
37 }
38 
39 void print_backtrace(std::ostream& out, const std::string& line_prefix)
40 {
42  {
43  out << line_prefix << std::dec << std::setw(2) << frame.depth() << "@" << std::hex << std::setw(14) << frame.frame_pointer() << ": "
44  << (frame.symbol().is_cxx() ? frame.symbol().demangled() : frame.symbol().raw()) << std::endl;
45  return true;
46  });
47 }
48 }
49 
50 namespace core
51 {
52 namespace posix
53 {
54 
55 bool is_child(pid_t pid) { return pid == 0; }
56 
57 ChildProcess fork(const std::function<posix::exit::Status()>& main,
58  const StandardStream& flags)
59 {
60  ChildProcess::Pipe stdin_pipe{ChildProcess::Pipe::invalid()};
61  ChildProcess::Pipe stdout_pipe{ChildProcess::Pipe::invalid()};
62  ChildProcess::Pipe stderr_pipe{ChildProcess::Pipe::invalid()};
63 
65  stdin_pipe = ChildProcess::Pipe();
67  stdout_pipe = ChildProcess::Pipe();
69  stderr_pipe = ChildProcess::Pipe();
70 
71  pid_t pid = ::fork();
72 
73  if (pid == -1)
74  throw std::system_error(errno, std::system_category());
75 
76  if (is_child(pid))
77  {
79 
80  try
81  {
82  stdin_pipe.close_write_fd();
83  stdout_pipe.close_read_fd();
84  stderr_pipe.close_read_fd();
85  // We replace stdin and stdout of the child process first:
86  if ((flags & StandardStream::stdin) != StandardStream::empty)
87  redirect_stream_to_fd(stdin_pipe.read_fd(), STDIN_FILENO);
88  if ((flags & StandardStream::stdout) != StandardStream::empty)
89  redirect_stream_to_fd(stdout_pipe.write_fd(), STDOUT_FILENO);
90  if ((flags & StandardStream::stderr) != StandardStream::empty)
91  redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO);
92 
93  result = main();
94  } catch(const std::exception& e)
95  {
96  std::cerr << "core::posix::fork(): An unhandled std::exception occured in the child process:" << std::endl
97  << " what(): " << e.what() << std::endl;
98  print_backtrace(std::cerr, " ");
99  } catch(...)
100  {
101  std::cerr << "core::posix::fork(): An unhandled exception occured in the child process." << std::endl;
102  print_backtrace(std::cerr, " ");
103  }
104 
105  // We have to ensure that we exit here. Otherwise, we run into
106  // all sorts of weird issues.
107  // Also, as we fork() without exec(), we must use _exit() or atexit()
108  // handlers can cause myraid of issues, from double flushing stream to
109  // crashing when trying to pthread_cancel() non-existent thread.
110  ::_exit(static_cast<int>(result));
111  }
112 
113  // We are in the parent process, and create a process object
114  // corresponding to the newly forked process.
115  stdin_pipe.close_read_fd();
116  stdout_pipe.close_write_fd();
117  stderr_pipe.close_write_fd();
118 
119  return ChildProcess(pid,
120  stdin_pipe,
121  stdout_pipe,
122  stderr_pipe);
123 }
124 
125 ChildProcess vfork(const std::function<posix::exit::Status()>& main,
126  const StandardStream& flags)
127 {
128  ChildProcess::Pipe stdin_pipe, stdout_pipe, stderr_pipe;
129 
130  pid_t pid = ::vfork();
131 
132  if (pid == -1)
133  throw std::system_error(errno, std::system_category());
134 
135  if (is_child(pid))
136  {
138 
139  try
140  {
141  // We replace stdin and stdout of the child process first:
142  stdin_pipe.close_write_fd();
143  stdout_pipe.close_read_fd();
144  stderr_pipe.close_read_fd();
145  // We replace stdin and stdout of the child process first:
147  redirect_stream_to_fd(stdin_pipe.read_fd(), STDIN_FILENO);
149  redirect_stream_to_fd(stdout_pipe.write_fd(), STDOUT_FILENO);
151  redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO);
152 
153  result = main();
154  } catch(const std::exception& e)
155  {
156  std::cerr << "core::posix::fork(): An unhandled std::exception occured in the child process:" << std::endl
157  << " what(): " << e.what() << std::endl;
158  print_backtrace(std::cerr, " ");
159  } catch(...)
160  {
161  std::cerr << "core::posix::fork(): An unhandled exception occured in the child process." << std::endl;
162  print_backtrace(std::cerr, " ");
163  }
164 
165  // We have to ensure that we exit here. Otherwise, we run into
166  // all sorts of weird issues.
167  // Also, as we vfork() without exec(), we must use _exit() or atexit()
168  // handlers can cause myraid of issues, from double flushing stream to
169  // crashing when trying to pthread_cancel() non-existent thread.
170  ::_exit(static_cast<int>(result));
171  }
172 
173  // We are in the parent process, and create a process object
174  // corresponding to the newly forked process.
175  // Close the parent's pipe end
176  stdin_pipe.close_read_fd();
177  stdout_pipe.close_write_fd();
178  stderr_pipe.close_write_fd();
179 
180  return ChildProcess(pid,
181  stdin_pipe,
182  stdout_pipe,
183  stderr_pipe);
184 }
185 }
186 }
bool is_child(pid_t pid)
Definition: fork.cpp:55
virtual std::size_t depth() const =0
depth returns the depth of this frame in the overall backtrace.
CORE_POSIX_DLL_PUBLIC ChildProcess vfork(const std::function< posix::exit::Status()> &main, const StandardStream &flags)
fork vforks a new process and executes the provided main function in the newly forked process...
Definition: fork.cpp:125
Status
The Status enum wrap&#39;s the posix exit status.
Definition: exit.h:33
The Frame class models an individual frame of a backtrace.
Definition: backtrace.h:37
StandardStream
The StandardStream enum wraps the POSIX standard streams.
void visit_with_handler(const FrameHandler &handler)
visit_with_handler iterates the backtrace of the calling program, invoking the handler for every fram...
Definition: backtrace.cpp:125
virtual const Symbol & symbol() const =0
symbol returns the symbolic representation of this frame.
virtual std::string raw() const =0
raw The raw symbolic representation of a frame pointer.
CORE_POSIX_DLL_PUBLIC ChildProcess fork(const std::function< posix::exit::Status()> &main, const StandardStream &flags)
fork forks a new process and executes the provided main function in the newly forked process...
Definition: fork.cpp:57
virtual std::string demangled() const =0
demangled returns the demangled C++ symbol name or raw.
CORE_POSIX_DLL_PUBLIC std::ostream & cerr() noexcept(true)
Access this process&#39;s stderr.
virtual void * frame_pointer() const =0
frame_pointer returns the the raw frame pointer of this frame.
The Process class models a child process of this process.
Definition: child_process.h:43
int main(int argc, char *argv[])
virtual bool is_cxx() const =0
is_cxx checks whether the symbol refers to a mangled C++ symbol.