fork.cpp
Go to the documentation of this file.
/*
* Copyright © 2013 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Thomas Voß <thomas.voss@canonical.com>
*/
#include <core/posix/exit.h>
#include <core/posix/fork.h>
#include "backtrace.h"
#include <iomanip>
#include <iostream>
#include <system_error>
#include <unistd.h>
namespace
{
void redirect_stream_to_fd(int fd, int stream)
{
auto rc = ::dup2(fd, stream);
if (rc == -1)
throw std::system_error(errno, std::system_category());
}
void print_backtrace(std::ostream& out, const std::string& line_prefix)
{
core::posix::backtrace::visit_with_handler([&out, line_prefix](const core::posix::backtrace::Frame& frame)
{
out << line_prefix << std::dec << std::setw(2) << frame.depth() << "@" << std::hex << std::setw(14) << frame.frame_pointer() << ": "
<< (frame.symbol().is_cxx() ? frame.symbol().demangled() : frame.symbol().raw()) << std::endl;
return true;
});
}
}
namespace core
{
namespace posix
{
bool is_child(pid_t pid) { return pid == 0; }
ChildProcess fork(const std::function<posix::exit::Status()>& main,
const StandardStream& flags)
{
ChildProcess::Pipe stdin_pipe{ChildProcess::Pipe::invalid()};
ChildProcess::Pipe stdout_pipe{ChildProcess::Pipe::invalid()};
ChildProcess::Pipe stderr_pipe{ChildProcess::Pipe::invalid()};
if ((flags & StandardStream::stdin) != StandardStream::empty)
stdin_pipe = ChildProcess::Pipe();
if ((flags & StandardStream::stdout) != StandardStream::empty)
stdout_pipe = ChildProcess::Pipe();
if ((flags & StandardStream::stderr) != StandardStream::empty)
stderr_pipe = ChildProcess::Pipe();
pid_t pid = ::fork();
if (pid == -1)
throw std::system_error(errno, std::system_category());
if (is_child(pid))
{
posix::exit::Status result = posix::exit::Status::failure;
try
{
stdin_pipe.close_write_fd();
stdout_pipe.close_read_fd();
stderr_pipe.close_read_fd();
// We replace stdin and stdout of the child process first:
if ((flags & StandardStream::stdin) != StandardStream::empty)
redirect_stream_to_fd(stdin_pipe.read_fd(), STDIN_FILENO);
if ((flags & StandardStream::stdout) != StandardStream::empty)
redirect_stream_to_fd(stdout_pipe.write_fd(), STDOUT_FILENO);
if ((flags & StandardStream::stderr) != StandardStream::empty)
redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO);
result = main();
} catch(const std::exception& e)
{
std::cerr << "core::posix::fork(): An unhandled std::exception occurred in the child process:" << std::endl
<< " what(): " << e.what() << std::endl;
print_backtrace(std::cerr, " ");
} catch(...)
{
std::cerr << "core::posix::fork(): An unhandled exception occurred in the child process." << std::endl;
print_backtrace(std::cerr, " ");
}
// We have to ensure that we exit here. Otherwise, we run into
// all sorts of weird issues.
::exit(static_cast<int>(result));
}
// We are in the parent process, and create a process object
// corresponding to the newly forked process.
stdin_pipe.close_read_fd();
stdout_pipe.close_write_fd();
stderr_pipe.close_write_fd();
return ChildProcess(pid,
stdin_pipe,
stdout_pipe,
stderr_pipe);
}
ChildProcess vfork(const std::function<posix::exit::Status()>& main,
const StandardStream& flags)
{
ChildProcess::Pipe stdin_pipe, stdout_pipe, stderr_pipe;
pid_t pid = ::vfork();
if (pid == -1)
throw std::system_error(errno, std::system_category());
if (is_child(pid))
{
posix::exit::Status result = posix::exit::Status::failure;
try
{
// We replace stdin and stdout of the child process first:
stdin_pipe.close_write_fd();
stdout_pipe.close_read_fd();
stderr_pipe.close_read_fd();
// We replace stdin and stdout of the child process first:
if ((flags & StandardStream::stdin) != StandardStream::empty)
redirect_stream_to_fd(stdin_pipe.read_fd(), STDIN_FILENO);
if ((flags & StandardStream::stdout) != StandardStream::empty)
redirect_stream_to_fd(stdout_pipe.write_fd(), STDOUT_FILENO);
if ((flags & StandardStream::stderr) != StandardStream::empty)
redirect_stream_to_fd(stderr_pipe.write_fd(), STDERR_FILENO);
result = main();
} catch(const std::exception& e)
{
std::cerr << "core::posix::fork(): An unhandled std::exception occurred in the child process:" << std::endl
<< " what(): " << e.what() << std::endl;
print_backtrace(std::cerr, " ");
} catch(...)
{
std::cerr << "core::posix::fork(): An unhandled exception occurred in the child process." << std::endl;
print_backtrace(std::cerr, " ");
}
// We have to ensure that we exit here. Otherwise, we run into
// all sorts of weird issues.
::exit(static_cast<int>(result));
}
// We are in the parent process, and create a process object
// corresponding to the newly forked process.
// Close the parent's pipe end
stdin_pipe.close_read_fd();
stdout_pipe.close_write_fd();
stderr_pipe.close_write_fd();
return ChildProcess(pid,
stdin_pipe,
stdout_pipe,
stderr_pipe);
}
}
}
main
int main(int argc, char *argv[])
Definition: CMakeCCompilerId.c:765
backtrace.h
core::posix::ChildProcess
The Process class models a child process of this process.
Definition: child_process.h:44
core::posix::backtrace::Frame::Symbol::demangled
virtual std::string demangled() const =0
demangled returns the demangled C++ symbol name or raw.
core::posix::backtrace::Frame::Symbol::raw
virtual std::string raw() const =0
raw The raw symbolic representation of a frame pointer.
core::posix::backtrace::Frame::Symbol::is_cxx
virtual bool is_cxx() const =0
is_cxx checks whether the symbol refers to a mangled C++ symbol.
core::posix::backtrace::Frame
The Frame class models an individual frame of a backtrace.
Definition: backtrace.h:38
core::posix::backtrace::Frame::symbol
virtual const Symbol & symbol() const =0
symbol returns the symbolic representation of this frame.
core::posix::backtrace::Frame::depth
virtual std::size_t depth() const =0
depth returns the depth of this frame in the overall backtrace.
core::posix::backtrace::Frame::frame_pointer
virtual void * frame_pointer() const =0
frame_pointer returns the the raw frame pointer of this frame.
exit.h
fork.h
core::posix::backtrace::visit_with_handler
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
core::posix::exit::Status
Status
The Status enum wrap's the posix exit status.
Definition: exit.h:34
core::posix::exit::Status::failure
@ failure
core::posix::this_process::cerr
CORE_POSIX_DLL_PUBLIC std::ostream & cerr() noexcept(true)
Access this process's stderr.
Definition: this_process.cpp:171
core::posix::vfork
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:122
core::posix::fork
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
core::posix::StandardStream
StandardStream
The StandardStream enum wraps the POSIX standard streams.
Definition: standard_stream.h:34
core::posix::StandardStream::stdin
@ stdin
core::posix::StandardStream::stderr
@ stderr
core::posix::StandardStream::stdout
@ stdout
core::posix::StandardStream::empty
@ empty
core::posix::is_child
bool is_child(pid_t pid)
Definition: fork.cpp:55
core
Definition: child_process.h:32