/* * 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 . * * Authored by: Thomas Voß */ #include #include #ifndef ANDROID #include "backtrace.h" #endif #include #include #include #include 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) { #ifndef ANDROID 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; }); #endif } } namespace core { namespace posix { bool is_child(pid_t pid) { return pid == 0; } ChildProcess fork(const std::function& 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 occured in the child process:" << std::endl << " what(): " << e.what() << std::endl; print_backtrace(std::cerr, " "); } catch(...) { std::cerr << "core::posix::fork(): An unhandled exception occured 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(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& 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 occured in the child process:" << std::endl << " what(): " << e.what() << std::endl; print_backtrace(std::cerr, " "); } catch(...) { std::cerr << "core::posix::fork(): An unhandled exception occured 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(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); } } }