2 * Copyright © 2013 Canonical Ltd.
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.
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.
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/>.
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
19 #include <core/posix/exit.h>
20 #include <core/posix/fork.h>
23 #include "backtrace.h"
28 #include <system_error>
34 void redirect_stream_to_fd(int fd, int stream)
36 auto rc = ::dup2(fd, stream);
38 throw std::system_error(errno, std::system_category());
41 void print_backtrace(std::ostream& out, const std::string& line_prefix)
44 core::posix::backtrace::visit_with_handler([&out, line_prefix](const core::posix::backtrace::Frame& frame)
46 out << line_prefix << std::dec << std::setw(2) << frame.depth() << "@" << std::hex << std::setw(14) << frame.frame_pointer() << ": "
47 << (frame.symbol().is_cxx() ? frame.symbol().demangled() : frame.symbol().raw()) << std::endl;
59 bool is_child(pid_t pid) { return pid == 0; }
61 ChildProcess fork(const std::function<posix::exit::Status()>& main,
62 const StandardStream& flags)
64 ChildProcess::Pipe stdin_pipe{ChildProcess::Pipe::invalid()};
65 ChildProcess::Pipe stdout_pipe{ChildProcess::Pipe::invalid()};
66 ChildProcess::Pipe stderr_pipe{ChildProcess::Pipe::invalid()};
68 if ((flags & StandardStream::stdin) != StandardStream::empty)
69 stdin_pipe = ChildProcess::Pipe();
70 if ((flags & StandardStream::stdout) != StandardStream::empty)
71 stdout_pipe = ChildProcess::Pipe();
72 if ((flags & StandardStream::stderr) != StandardStream::empty)
73 stderr_pipe = ChildProcess::Pipe();
78 throw std::system_error(errno, std::system_category());
82 posix::exit::Status result = posix::exit::Status::failure;
86 stdin_pipe.close_write_fd();
87 stdout_pipe.close_read_fd();
88 stderr_pipe.close_read_fd();
89 // We replace stdin and stdout of the child process first:
90 if ((flags & StandardStream::stdin) != StandardStream::empty)
91 redirect_stream_to_fd(stdin_pipe.read_fd(), STDIN_FILENO);
92 if ((flags & StandardStream::stdout) != StandardStream::empty)
93 redirect_stream_to_fd(stdout_pipe.write_fd(), STDOUT_FILENO);
94 if ((flags & StandardStream::stderr) != StandardStream::empty)
95 redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO);
98 } catch(const std::exception& e)
100 std::cerr << "core::posix::fork(): An unhandled std::exception occured in the child process:" << std::endl
101 << " what(): " << e.what() << std::endl;
102 print_backtrace(std::cerr, " ");
105 std::cerr << "core::posix::fork(): An unhandled exception occured in the child process." << std::endl;
106 print_backtrace(std::cerr, " ");
109 // We have to ensure that we exit here. Otherwise, we run into
110 // all sorts of weird issues.
111 ::exit(static_cast<int>(result));
114 // We are in the parent process, and create a process object
115 // corresponding to the newly forked process.
116 stdin_pipe.close_read_fd();
117 stdout_pipe.close_write_fd();
118 stderr_pipe.close_write_fd();
120 return ChildProcess(pid,
126 ChildProcess vfork(const std::function<posix::exit::Status()>& main,
127 const StandardStream& flags)
129 ChildProcess::Pipe stdin_pipe, stdout_pipe, stderr_pipe;
131 pid_t pid = ::vfork();
134 throw std::system_error(errno, std::system_category());
138 posix::exit::Status result = posix::exit::Status::failure;
142 // We replace stdin and stdout of the child process first:
143 stdin_pipe.close_write_fd();
144 stdout_pipe.close_read_fd();
145 stderr_pipe.close_read_fd();
146 // We replace stdin and stdout of the child process first:
147 if ((flags & StandardStream::stdin) != StandardStream::empty)
148 redirect_stream_to_fd(stdin_pipe.read_fd(), STDIN_FILENO);
149 if ((flags & StandardStream::stdout) != StandardStream::empty)
150 redirect_stream_to_fd(stdout_pipe.write_fd(), STDOUT_FILENO);
151 if ((flags & StandardStream::stderr) != StandardStream::empty)
152 redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO);
155 } catch(const std::exception& e)
157 std::cerr << "core::posix::fork(): An unhandled std::exception occured in the child process:" << std::endl
158 << " what(): " << e.what() << std::endl;
159 print_backtrace(std::cerr, " ");
162 std::cerr << "core::posix::fork(): An unhandled exception occured in the child process." << std::endl;
163 print_backtrace(std::cerr, " ");
166 // We have to ensure that we exit here. Otherwise, we run into
167 // all sorts of weird issues.
168 ::exit(static_cast<int>(result));
171 // We are in the parent process, and create a process object
172 // corresponding to the newly forked process.
173 // Close the parent's pipe end
174 stdin_pipe.close_read_fd();
175 stdout_pipe.close_write_fd();
176 stderr_pipe.close_write_fd();
178 return ChildProcess(pid,