process-cpp-3.0.1/ 0000775 0001751 0001751 00000000000 12553414462 014341 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/CMakeLists.txt 0000664 0001751 0001751 00000005344 12553403402 017077 0 ustar tvoss tvoss 0000000 0000000 # Copyright © 2013 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Authored by: Thomas Voss
cmake_minimum_required(VERSION 2.8)
project(process-cpp)
find_package(Boost COMPONENTS iostreams system REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(Threads REQUIRED)
pkg_check_modules(PROPERTIES_CPP properties-cpp)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(GNUInstallDirs)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Wextra -fvisibility=hidden")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -fno-strict-aliasing -fvisibility=hidden -fvisibility-inlines-hidden -pedantic -Wextra")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
option(PROCESS_CPP_WERROR "Treat warnings as errors" ON)
if(PROCESS_CPP_WERROR)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wno-error=format")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-error=format")
endif(PROCESS_CPP_WERROR)
string(TOLOWER "${CMAKE_BUILD_TYPE}" cmake_build_type_lower)
#####################################################################
# Enable code coverage calculation with gcov/gcovr/lcov
# Usage:
# * Switch build type to coverage (use ccmake or cmake-gui)
# * Invoke make, make test, make coverage
# * Find html report in subdir coveragereport
# * Find xml report feasible for jenkins in coverage.xml
#####################################################################
IF(cmake_build_type_lower MATCHES coverage)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs" )
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage -fprofile-arcs" )
SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -ftest-coverage -fprofile-arcs" )
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -ftest-coverage -fprofile-arcs" )
ENDIF(cmake_build_type_lower MATCHES coverage)
set(PROCESS_CPP_VERSION_MAJOR 3)
set(PROCESS_CPP_VERSION_MINOR 0)
set(PROCESS_CPP_VERSION_PATCH 0)
include(CTest)
include_directories(
include/
${Boost_INCLUDE_DIRS}
${PROPERTIES_CPP_INCLUDE_DIRS}
)
add_subdirectory(doc)
add_subdirectory(data)
add_subdirectory(include)
add_subdirectory(src)
add_subdirectory(tests)
process-cpp-3.0.1/symbols.map 0000664 0001751 0001751 00000000345 12553403402 016522 0 ustar tvoss tvoss 0000000 0000000 {
global:
extern "C++" {
core::*;
typeinfo?for?core::*;
typeinfo?name?for?core::*;
VTT?for?core::*;
virtual?thunk?to?core::*;
vtable?for?core::*;
std::hash*;
};
local:
extern "C++" {
*;
};
}; process-cpp-3.0.1/src/ 0000775 0001751 0001751 00000000000 12553414462 015130 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/src/CMakeLists.txt 0000664 0001751 0001751 00000003563 12553403402 017667 0 ustar tvoss tvoss 0000000 0000000 # Copyright © 2013 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Authored by: Thomas Voss
add_library(
process-cpp SHARED
core/posix/backtrace.h
core/posix/backtrace.cpp
core/posix/child_process.cpp
core/posix/exec.cpp
core/posix/fork.cpp
core/posix/process.cpp
core/posix/process_group.cpp
core/posix/signal.cpp
core/posix/signalable.cpp
core/posix/standard_stream.cpp
core/posix/wait.cpp
core/posix/this_process.cpp
core/posix/linux/proc/process/oom_adj.cpp
core/posix/linux/proc/process/oom_score.cpp
core/posix/linux/proc/process/oom_score_adj.cpp
core/posix/linux/proc/process/stat.cpp
core/testing/cross_process_sync.cpp
core/testing/fork_and_run.cpp
)
target_link_libraries(
process-cpp
${Boost_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)
# We compile with all symbols visible by default. For the shipping library, we strip
# out all symbols that are not in core::*
set(symbol_map "${CMAKE_SOURCE_DIR}/symbols.map")
set_target_properties(
process-cpp
PROPERTIES
LINK_FLAGS "${ldflags} -Wl,--version-script,${symbol_map}"
LINK_DEPENDS ${symbol_map}
VERSION ${PROCESS_CPP_VERSION_MAJOR}.${PROCESS_CPP_VERSION_MINOR}.${PROCESS_CPP_VERSION_PATCH}
SOVERSION ${PROCESS_CPP_VERSION_MAJOR}
)
install(
TARGETS process-cpp
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
process-cpp-3.0.1/src/core/ 0000775 0001751 0001751 00000000000 12553414462 016060 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/src/core/testing/ 0000775 0001751 0001751 00000000000 12553414462 017535 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/src/core/testing/fork_and_run.cpp 0000664 0001751 0001751 00000005563 12553403402 022711 0 ustar tvoss tvoss 0000000 0000000 /*
* Copyright © 2012-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
#include
#include
core::testing::ForkAndRunResult core::testing::operator|(
core::testing::ForkAndRunResult lhs,
core::testing::ForkAndRunResult rhs)
{
return static_cast(
static_cast (lhs) | static_cast(rhs));
}
core::testing::ForkAndRunResult core::testing::operator&(
core::testing::ForkAndRunResult lhs,
core::testing::ForkAndRunResult rhs)
{
return static_cast(
static_cast (lhs) & static_cast(rhs));
}
core::testing::ForkAndRunResult core::testing::fork_and_run(
const std::function& service,
const std::function& client)
{
core::testing::ForkAndRunResult result = core::testing::ForkAndRunResult::empty;
auto service_process = core::posix::fork(service, core::posix::StandardStream::empty);
auto client_process = core::posix::fork(client, core::posix::StandardStream::empty);
auto client_result = client_process.wait_for(core::posix::wait::Flags::untraced);
switch (client_result.status)
{
case core::posix::wait::Result::Status::exited:
if (client_result.detail.if_exited.status == core::posix::exit::Status::failure)
result = result | core::testing::ForkAndRunResult::client_failed;
break;
default:
result = result | core::testing::ForkAndRunResult::client_failed;
break;
}
service_process.send_signal_or_throw(core::posix::Signal::sig_term);
auto service_result = service_process.wait_for(core::posix::wait::Flags::untraced);
switch (service_result.status)
{
case core::posix::wait::Result::Status::exited:
if (service_result.detail.if_exited.status == core::posix::exit::Status::failure)
result = result | core::testing::ForkAndRunResult::service_failed;
break;
default:
result = result | core::testing::ForkAndRunResult::service_failed;
break;
}
return result;
}
process-cpp-3.0.1/src/core/testing/cross_process_sync.cpp 0000664 0001751 0001751 00000005537 12553403402 024166 0 ustar tvoss tvoss 0000000 0000000 /*
* Copyright © 2013 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Authored by: Thomas Voss
*/
#include
#include
#include
#include
namespace
{
const int read_fd = 0;
const int write_fd = 1;
}
core::testing::CrossProcessSync::CrossProcessSync() : counter(0)
{
if (::pipe(fds) < 0)
throw std::system_error(errno, std::system_category());
}
core::testing::CrossProcessSync::CrossProcessSync(const CrossProcessSync& rhs) : counter(rhs.counter)
{
fds[0] = ::dup(rhs.fds[0]);
fds[1] = ::dup(rhs.fds[1]);
}
core::testing::CrossProcessSync::~CrossProcessSync() noexcept
{
::close(fds[0]);
::close(fds[1]);
}
core::testing::CrossProcessSync& core::testing::CrossProcessSync::operator=(const core::testing::CrossProcessSync& rhs)
{
::close(fds[0]);
::close(fds[1]);
fds[0] = ::dup(rhs.fds[0]);
fds[1] = ::dup(rhs.fds[1]);
counter = rhs.counter;
return *this;
}
void core::testing::CrossProcessSync::try_signal_ready_for(const std::chrono::milliseconds& duration)
{
static const short empty_revents = 0;
pollfd poll_fd[1] = { { fds[write_fd], POLLOUT, empty_revents } };
int rc = -1;
if ((rc = ::poll(poll_fd, 1, duration.count())) < 0)
throw std::system_error(errno, std::system_category());
else if (rc == 0)
throw Error::Timeout{};
static const std::uint32_t value = 1;
if (sizeof(value) != write(fds[write_fd], std::addressof(value), sizeof(value)))
throw std::system_error(errno, std::system_category());
}
std::uint32_t core::testing::CrossProcessSync::wait_for_signal_ready_for(const std::chrono::milliseconds& duration)
{
static const short empty_revents = 0;
pollfd poll_fd[1] = { { fds[read_fd], POLLIN, empty_revents } };
int rc = -1;
if ((rc = ::poll(poll_fd, 1, duration.count())) < 0)
throw std::system_error(errno, std::system_category());
else if (rc == 0)
throw Error::Timeout{};
std::uint32_t value = 0;
if (sizeof(value) != read(fds[read_fd], std::addressof(value), sizeof(value)))
throw std::system_error(errno, std::system_category());
if (value != 1)
throw std::system_error(errno, std::system_category());
counter += value;
return counter;
}
process-cpp-3.0.1/src/core/posix/ 0000775 0001751 0001751 00000000000 12553414462 017222 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/src/core/posix/signalable.cpp 0000664 0001751 0001751 00000002462 12553403402 022023 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
namespace core
{
namespace posix
{
struct Signalable::Private
{
pid_t pid;
};
Signalable::Signalable(pid_t pid) : d(new Private{pid})
{
}
void Signalable::send_signal_or_throw(Signal signal)
{
auto result = ::kill(d->pid, static_cast(signal));
if (result == -1)
throw std::system_error(errno, std::system_category());
}
void Signalable::send_signal(Signal signal, std::error_code& e) noexcept
{
auto result = ::kill(d->pid, static_cast(signal));
if (result == -1)
{
e = std::error_code(errno, std::system_category());
}
}
}
}
process-cpp-3.0.1/src/core/posix/backtrace.cpp 0000664 0001751 0001751 00000007220 12553403402 021636 0 ustar tvoss tvoss 0000000 0000000 /*
* Copyright © 2014 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 "backtrace.h"
#include
#include
namespace bt = core::posix::backtrace;
namespace impl
{
std::tuple demangle(const std::string& symbol)
{
int status = 1;
auto result = abi::__cxa_demangle(symbol.c_str(),
nullptr,
nullptr,
&status);
if (!result || status != 0)
{
return std::make_tuple(std::string(), false);
}
std::string s{result};
::free(result);
return std::make_tuple(s, true);
}
struct Frame : public bt::Frame
{
struct Symbol : public bt::Frame::Symbol
{
Symbol(const char* symbol) : raw_(symbol)
{
auto first = raw_.find_first_of("(");
auto last = raw_.find_last_of(")");
if (first != std::string::npos && last != std::string::npos)
{
auto mangled_symbol = raw_.substr(first+1,
(last-1) - (first+1));
auto plus = mangled_symbol.find_first_of("+");
if (plus != std::string::npos)
mangled_symbol.erase(plus);
std::tie(demangled_, is_cxx_) = demangle(mangled_symbol);
if (!is_cxx_)
demangled_ = raw_;
}
}
bool is_cxx() const
{
return is_cxx_;
}
std::string demangled() const
{
return demangled_;
}
std::string raw() const
{
return raw_;
}
std::string raw_;
std::string demangled_;
bool is_cxx_ = false;
};
std::size_t depth_;
void* frame_pointer_;
Symbol symbol_;
Frame(std::size_t depth, void* frame_pointer, const char* symbol)
: depth_(depth),
frame_pointer_(frame_pointer),
symbol_(symbol)
{
}
std::size_t depth() const
{
return depth_;
}
virtual void* frame_pointer() const
{
return frame_pointer_;
}
const Symbol& symbol() const
{
return symbol_;
}
};
}
std::shared_ptr bt::Frame::Symbol::for_testing_from_raw_symbol(const char* symbol)
{
return std::shared_ptr(new impl::Frame::Symbol(symbol));
}
void bt::visit_with_handler(const bt::FrameHandler& handler)
{
static const unsigned int max_frames=64;
void *frames[max_frames];
auto frame_count = ::backtrace(frames, max_frames);
auto symbols = ::backtrace_symbols(frames, frame_count);
struct Scope
{
Scope(char** symbols) : symbols(symbols)
{
}
~Scope()
{
::free(symbols);
}
char** symbols = nullptr;
} scope{symbols};
for (int i = 0; i < frame_count; i++)
{
impl::Frame frame(i, frames[i], symbols[i]);
if (!handler(frame))
return;
}
}
process-cpp-3.0.1/src/core/posix/CMakeLists.txt 0000664 0001751 0001751 00000001342 12553403402 021752 0 ustar tvoss tvoss 0000000 0000000 # Copyright © 2013 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Authored by: Thomas Voss
add_library(
posix-process
process.cpp
)
add_subdirectory(linux)
process-cpp-3.0.1/src/core/posix/standard_stream.cpp 0000664 0001751 0001751 00000002142 12553403402 023070 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
namespace core
{
namespace posix
{
StandardStream operator|(StandardStream l, StandardStream r)
{
return static_cast(static_cast(l) | static_cast(r));
}
StandardStream operator&(StandardStream l, StandardStream r)
{
return static_cast(static_cast(l) & static_cast(r));
}
}
}
process-cpp-3.0.1/src/core/posix/process_group.cpp 0000664 0001751 0001751 00000002041 12553403402 022605 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
namespace core
{
namespace posix
{
struct ProcessGroup::Private
{
pid_t id;
};
pid_t ProcessGroup::id() const
{
return d->id;
}
ProcessGroup::ProcessGroup(pid_t id)
: Signalable(-id), // We rely on ::kill to deliver signals, thus negate the id (see man 2 kill).
d(new Private{id})
{
}
}
}
process-cpp-3.0.1/src/core/posix/signal.cpp 0000664 0001751 0001751 00000013614 12553403402 021200 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
#include
#include
#include
namespace impl
{
void set_thread_signal_mask(::sigset_t* new_mask, ::sigset_t* old_mask)
{
::pthread_sigmask(SIG_BLOCK, new_mask, old_mask);
}
void set_process_signal_mask(::sigset_t* new_mask, ::sigset_t* old_mask)
{
::sigprocmask(SIG_BLOCK, new_mask, old_mask);
}
class SignalTrap : public core::posix::SignalTrap
{
public:
enum class Scope
{
process,
thread
};
enum class State
{
not_running,
running
};
SignalTrap(Scope scope, std::initializer_list blocked_signals)
: scope(scope),
state(State::not_running),
event_fd(::eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK))
{
if (event_fd == -1)
throw std::system_error(errno, std::system_category());
::sigemptyset(&blocked_signals_mask);
for(auto signal : blocked_signals)
::sigaddset(&blocked_signals_mask, static_cast(signal));
switch (scope)
{
case Scope::process:
set_process_signal_mask(&blocked_signals_mask, &old_signals_mask);
break;
case Scope::thread:
set_thread_signal_mask(&blocked_signals_mask, &old_signals_mask);
break;
}
}
~SignalTrap()
{
switch (scope)
{
case Scope::process:
set_process_signal_mask(&old_signals_mask, nullptr);
break;
case Scope::thread:
set_thread_signal_mask(&old_signals_mask, nullptr);
break;
}
::close(event_fd);
}
bool has(core::posix::Signal signal) override
{
return ::sigismember(&blocked_signals_mask, static_cast(signal));
}
void run() override
{
static constexpr int signal_fd_idx = 0;
static constexpr int event_fd_idx = 1;
static constexpr int signal_info_buffer_size = 5;
if (state.load() == State::running)
throw std::runtime_error("SignalTrap::run can only be run once.");
state.store(State::running);
// Make sure we clean up the signal fd whenever
// we leave the scope of run.
struct Scope
{
~Scope()
{
if (signal_fd != -1)
::close(signal_fd);
}
int signal_fd;
} scope{::signalfd(-1, &blocked_signals_mask, SFD_CLOEXEC | SFD_NONBLOCK)};
if (scope.signal_fd == -1)
throw std::system_error(errno, std::system_category());
pollfd fds[2];
signalfd_siginfo signal_info[signal_info_buffer_size];
for (;;)
{
fds[signal_fd_idx] = {scope.signal_fd, POLLIN, 0};
fds[event_fd_idx] = {event_fd, POLLIN, 0};
auto rc = ::poll(fds, 2, -1);
if (rc == -1)
{
if (errno == EINTR)
continue;
break;
}
if (rc == 0)
continue;
if (fds[signal_fd_idx].revents & POLLIN)
{
auto result = ::read(scope.signal_fd, signal_info, sizeof(signal_info));
for (uint i = 0; i < result / sizeof(signalfd_siginfo); i++)
{
if (has(static_cast(signal_info[i].ssi_signo)))
{
on_signal_raised(
static_cast(
signal_info[i].ssi_signo));
}
}
}
if (fds[event_fd_idx].revents & POLLIN)
{
std::int64_t value{1};
// Consciously void-ing the return value here.
// Not much we can do about an error.
auto result = ::read(event_fd, &value, sizeof(value));
(void) result;
break;
}
}
state.store(State::not_running);
}
void stop() override
{
static const std::int64_t value = {1};
if (sizeof(value) != ::write(event_fd, &value, sizeof(value)))
throw std::system_error(errno, std::system_category());
}
core::Signal& signal_raised() override
{
return on_signal_raised;
}
private:
Scope scope;
std::atomic state;
int event_fd;
core::Signal on_signal_raised;
::sigset_t old_signals_mask;
::sigset_t blocked_signals_mask;
};
}
std::shared_ptr core::posix::trap_signals_for_process(
std::initializer_list blocked_signals)
{
return std::make_shared(
impl::SignalTrap::Scope::process,
blocked_signals);
}
std::shared_ptr core::posix::trap_signals_for_all_subsequent_threads(
std::initializer_list blocked_signals)
{
return std::make_shared(
impl::SignalTrap::Scope::thread,
blocked_signals);
}
process-cpp-3.0.1/src/core/posix/fork.cpp 0000664 0001751 0001751 00000014344 12553403402 020665 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include "backtrace.h"
#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)
{
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& 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);
}
}
}
process-cpp-3.0.1/src/core/posix/exec.cpp 0000664 0001751 0001751 00000004174 12553403402 020650 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
#include
#include
namespace core
{
namespace posix
{
ChildProcess exec(const std::string& fn,
const std::vector& argv,
const std::map& env,
const StandardStream& flags)
{
std::function null_function = [](){};
return exec(fn, argv, env, flags, null_function);
}
ChildProcess exec(const std::string& fn,
const std::vector& argv,
const std::map& env,
const StandardStream& flags,
const std::function& child_setup)
{
return posix::fork([fn, argv, env, child_setup]()
{
char** it; char** pargv; char** penv;
it = pargv = new char*[argv.size()+2];
*it = ::strdup(fn.c_str());
it++;
for (auto element : argv)
{
*it = ::strdup(element.c_str());
it++;
}
*it = nullptr;
it = penv = new char*[env.size()+1];
for (auto pair : env)
{
*it = ::strdup((pair.first + "=" + pair.second).c_str());
it++;
}
*it = nullptr;
child_setup();
return static_cast(execve(fn.c_str(), pargv, penv));
}, flags);
}
}
}
process-cpp-3.0.1/src/core/posix/process.cpp 0000664 0001751 0001751 00000003357 12553403402 021404 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
#include
namespace core
{
namespace posix
{
struct Process::Private
{
pid_t pid;
};
Process Process::invalid()
{
static const pid_t invalid_pid = 0;
Process p(invalid_pid);
p.d->pid = -1;
return p;
}
Process::Process(pid_t pid)
: Signalable(pid),
d(new Private{pid})
{
if (pid < 0)
throw std::runtime_error("Cannot construct instance for invalid pid.");
}
Process::~Process() noexcept
{
}
pid_t Process::pid() const
{
return d->pid;
}
ProcessGroup Process::process_group_or_throw() const
{
pid_t pgid = ::getpgid(pid());
if (pgid == -1)
throw std::system_error(errno, std::system_category());
return ProcessGroup(pgid);
}
ProcessGroup Process::process_group(std::error_code& se) const noexcept(true)
{
pid_t pgid = ::getpgid(pid());
if (pgid == -1)
{
se = std::error_code(errno, std::system_category());
}
return ProcessGroup(pgid);
}
}
}
process-cpp-3.0.1/src/core/posix/child_process.cpp 0000664 0001751 0001751 00000024372 12553403402 022547 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace io = boost::iostreams;
namespace
{
struct DeathObserverImpl : public core::posix::ChildProcess::DeathObserver
{
DeathObserverImpl(const std::shared_ptr& trap)
: on_sig_child_connection
{
trap->signal_raised().connect([this](core::posix::Signal signal)
{
switch (signal)
{
case core::posix::Signal::sig_chld:
on_sig_child();
break;
default:
break;
}
})
}
{
if (!trap->has(core::posix::Signal::sig_chld))
throw std::logic_error(
"DeathObserver::DeathObserverImpl: Given SignalTrap"
" instance does not trap Signal::sig_chld.");
}
bool add(const core::posix::ChildProcess& process) override
{
if (process.pid() == -1)
return false;
std::lock_guard lg(guard);
bool added = false;
auto new_process = std::make_pair(process.pid(), process);
std::tie(std::ignore, added) = children.insert(new_process);
if (added)
{
// The process may have died between it's instantiation and it
// being added to the children map. Check that it's still alive.
int status{-1};
if (::waitpid(process.pid(), &status, WNOHANG) != 0) // child no longer alive
{
// we missed the SIGCHLD signal so we must now manually
// inform our subscribers.
signals.child_died(new_process.second);
children.erase(new_process.first);
return false;
}
}
return added;
}
bool has(const core::posix::ChildProcess& process) const override
{
std::lock_guard lg(guard);
return children.count(process.pid()) > 0;
}
const core::Signal& child_died() const override
{
return signals.child_died;
}
void on_sig_child() override
{
pid_t pid{-1}; int status{-1};
while (true)
{
pid = ::waitpid(0, &status, WNOHANG);
if (pid == -1)
{
if (errno == ECHILD)
{
break; // No more children
}
continue; // Ignore stray SIGCHLD signals
}
else if (pid == 0)
{
break; // No more children
}
else
{
std::lock_guard lg(guard);
auto it = children.find(pid);
if (it != children.end())
{
if (WIFSIGNALED(status) || WIFEXITED(status))
{
signals.child_died(it->second);
children.erase(it);
}
}
}
}
}
mutable std::mutex guard;
std::unordered_map children;
core::ScopedConnection on_sig_child_connection;
struct
{
core::Signal child_died;
} signals;
};
}
std::unique_ptr
core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(
std::shared_ptr trap)
{
static std::atomic has_been_created_once{false};
if (has_been_created_once.exchange(true))
throw std::runtime_error
{
"DeathObserver::create_once_with_signal_trap: "
"Cannot create more than one instance."
};
try
{
std::unique_ptr result
{
new DeathObserverImpl{trap}
};
return result;
} catch(...)
{
// We make sure that a throwing c'tor does not impact our ability to
// retry creation of a DeathObserver instance.
has_been_created_once.store(false);
std::rethrow_exception(std::current_exception());
}
assert(false && "We should never reach here.");
// Silence the compiler.
return std::unique_ptr{};
}
namespace core
{
namespace posix
{
ChildProcess::Pipe ChildProcess::Pipe::invalid()
{
static Pipe p;
static std::once_flag flag;
std::call_once(flag, [&]() { p.close_read_fd(); p.close_write_fd(); });
return p;
}
ChildProcess::Pipe::Pipe()
{
int rc = ::pipe(fds);
if (rc == -1)
throw std::system_error(errno, std::system_category());
}
ChildProcess::Pipe::Pipe(const ChildProcess::Pipe& rhs) : fds{-1, -1}
{
if (rhs.fds[0] != -1)
fds[0] = ::dup(rhs.fds[0]);
if (rhs.fds[1] != -1)
fds[1] = ::dup(rhs.fds[1]);
}
ChildProcess::Pipe::~Pipe()
{
if (fds[0] != -1)
::close(fds[0]);
if (fds[1] != -1)
::close(fds[1]);
}
int ChildProcess::Pipe::read_fd() const
{
return fds[0];
}
void ChildProcess::Pipe::close_read_fd()
{
if (fds[0] != -1)
{
::close(fds[0]);
fds[0] = -1;
}
}
int ChildProcess::Pipe::write_fd() const
{
return fds[1];
}
void ChildProcess::Pipe::close_write_fd()
{
if (fds[1] != -1)
{
::close(fds[1]);
fds[1] = -1;
}
}
ChildProcess::Pipe& ChildProcess::Pipe::operator=(const ChildProcess::Pipe& rhs)
{
if (fds[0] != -1)
::close(fds[0]);
if (fds[1] != -1)
::close(fds[1]);
if (rhs.fds[0] != -1)
fds[0] = ::dup(rhs.fds[0]);
else
fds[0] = -1;
if (rhs.fds[1] != -1)
fds[1] = ::dup(rhs.fds[1]);
else
fds[1] = -1;
return *this;
}
struct ChildProcess::Private
{
// stdin and stdout are always "relative" to the childprocess, i.e., we
// write to stdin of the child process and read from its stdout.
Private(pid_t pid,
const ChildProcess::Pipe& stderr,
const ChildProcess::Pipe& stdin,
const ChildProcess::Pipe& stdout)
: pipes{stderr, stdin, stdout},
serr(pipes.stderr.read_fd(), io::never_close_handle),
sin(pipes.stdin.write_fd(), io::never_close_handle),
sout(pipes.stdout.read_fd(), io::never_close_handle),
cerr(&serr),
cin(&sin),
cout(&sout),
original_parent_pid(::getpid()),
original_child_pid(pid)
{
}
~Private()
{
// Check if we are in the original parent process.
if (original_parent_pid == getpid())
{
// If so, check if we are considering a valid pid here.
// If so, we kill the original child.
if (original_child_pid != -1)
::kill(original_child_pid, SIGKILL);
}
}
struct
{
ChildProcess::Pipe stdin;
ChildProcess::Pipe stdout;
ChildProcess::Pipe stderr;
} pipes;
io::stream_buffer serr;
io::stream_buffer sin;
io::stream_buffer sout;
std::istream cerr;
std::ostream cin;
std::istream cout;
// We need to store the original parent pid as we might have been forked
// and with our automatic cleanup in place, it might happen that the d'tor
// is called from the child process.
pid_t original_parent_pid;
pid_t original_child_pid;
};
ChildProcess ChildProcess::invalid()
{
// We take the init process as child.
static const pid_t invalid_pid = 1;
return ChildProcess(invalid_pid, Pipe::invalid(), Pipe::invalid(), Pipe::invalid());
}
ChildProcess::ChildProcess(pid_t pid,
const ChildProcess::Pipe& stdin_pipe,
const ChildProcess::Pipe& stdout_pipe,
const ChildProcess::Pipe& stderr_pipe)
: Process(pid),
d(new Private{pid, stdin_pipe, stdout_pipe, stderr_pipe})
{
}
ChildProcess::~ChildProcess()
{
}
wait::Result ChildProcess::wait_for(const wait::Flags& flags)
{
int status = -1;
pid_t result_pid = ::waitpid(pid(), std::addressof(status), static_cast(flags));
if (result_pid == -1)
throw std::system_error(errno, std::system_category());
wait::Result result;
if (result_pid == 0)
{
result.status = wait::Result::Status::no_state_change;
return result;
}
if (WIFEXITED(status))
{
result.status = wait::Result::Status::exited;
result.detail.if_exited.status = static_cast(WEXITSTATUS(status));
} else if (WIFSIGNALED(status))
{
result.status = wait::Result::Status::signaled;
result.detail.if_signaled.signal = static_cast(WTERMSIG(status));
result.detail.if_signaled.core_dumped = WCOREDUMP(status);
} else if (WIFSTOPPED(status))
{
result.status = wait::Result::Status::stopped;
result.detail.if_stopped.signal = static_cast(WSTOPSIG(status));
} else if (WIFCONTINUED(status))
{
result.status = wait::Result::Status::continued;
}
return result;
}
std::istream& ChildProcess::cerr()
{
return d->cerr;
}
std::ostream& ChildProcess::cin()
{
return d->cin;
}
std::istream& ChildProcess::cout()
{
return d->cout;
}
}
}
process-cpp-3.0.1/src/core/posix/this_process.cpp 0000664 0001751 0001751 00000007355 12553403402 022435 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
#include
#include
#include
#include
#include
#if defined(_GNU_SOURCE)
#include
#else
extern char** environ;
#endif
namespace core
{
namespace posix
{
namespace this_process
{
namespace env
{
namespace
{
std::mutex& env_guard()
{
static std::mutex m;
return m;
}
}
void for_each(const std::function& functor) noexcept(true)
{
std::lock_guard lg(env_guard());
auto it = ::environ;
while (it != nullptr && *it != nullptr)
{
std::string line(*it);
functor(line.substr(0,line.find_first_of('=')),
line.substr(line.find_first_of('=')+1));
++it;
}
}
std::string get_or_throw(const std::string& key)
{
std::lock_guard lg(env_guard());
auto result = ::getenv(key.c_str());
if (result == nullptr)
{
std::stringstream ss;
ss << "Variable with name " << key << " is not defined in the environment";
throw std::runtime_error(ss.str());
}
return std::string{result};
}
std::string get(const std::string& key,
const std::string& default_value) noexcept(true)
{
std::lock_guard lg(env_guard());
auto result = ::getenv(key.c_str());
return std::string{result ? result : default_value};
}
void unset_or_throw(const std::string& key)
{
std::lock_guard lg(env_guard());
auto rc = ::unsetenv(key.c_str());
if (rc == -1)
throw std::system_error(errno, std::system_category());
}
bool unset(const std::string& key,
std::error_code& se) noexcept(true)
{
std::lock_guard lg(env_guard());
auto rc = ::unsetenv(key.c_str());
if (rc == -1)
{
se = std::error_code(errno, std::system_category());
return false;
}
return true;
}
void set_or_throw(const std::string& key,
const std::string& value)
{
std::lock_guard lg(env_guard());
static const int overwrite = 0;
auto rc = ::setenv(key.c_str(), value.c_str(), overwrite);
if (rc == -1)
throw std::system_error(errno, std::system_category());
}
bool set(const std::string &key,
const std::string &value,
std::error_code& se) noexcept(true)
{
std::lock_guard lg(env_guard());
static const int overwrite = 0;
auto rc = ::setenv(key.c_str(), value.c_str(), overwrite);
if (rc == -1)
{
se = std::error_code(errno, std::system_category());
return false;
}
return true;
}
}
Process instance() noexcept(true)
{
static const Process self{getpid()};
return self;
}
Process parent() noexcept(true)
{
return Process(getppid());
}
std::istream& cin() noexcept(true)
{
return std::cin;
}
std::ostream& cout() noexcept(true)
{
return std::cout;
}
std::ostream& cerr() noexcept(true)
{
return std::cerr;
}
}
}
}
process-cpp-3.0.1/src/core/posix/backtrace.h 0000664 0001751 0001751 00000006152 12553403402 021306 0 ustar tvoss tvoss 0000000 0000000 /*
* Copyright © 2014 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ß
*/
#ifndef CORE_POSIX_BACKTRACE_H_
#define CORE_POSIX_BACKTRACE_H_
#include
#include
#include
#include
namespace core
{
namespace posix
{
namespace backtrace
{
/**
* @brief The Frame class models an individual frame of a backtrace.
*/
class Frame
{
public:
/**
* @brief The Symbol class models the symbolic representation of a frame pointer.
*/
class Symbol
{
public:
static std::shared_ptr for_testing_from_raw_symbol(const char* symbol);
Symbol(const Symbol&) = delete;
virtual ~Symbol() = default;
Symbol& operator=(const Symbol&) = delete;
/**
* @brief is_cxx checks whether the symbol refers to a mangled C++ symbol.
* @return true iff the symbol refers to a mangled C++ symbol.
*/
virtual bool is_cxx() const = 0;
/**
* @brief demangled returns the demangled C++ symbol name or raw.
*/
virtual std::string demangled() const = 0;
/**
* @brief raw The raw symbolic representation of a frame pointer.
* @return
*/
virtual std::string raw() const = 0;
protected:
Symbol() = default;
};
Frame(const Frame&) = delete;
virtual ~Frame() = default;
Frame& operator=(const Frame&) = delete;
/**
* @brief depth returns the depth of this frame in the overall backtrace.
*/
virtual std::size_t depth() const = 0;
/**
* @brief frame_pointer returns the the raw frame pointer of this frame.
* @return
*/
virtual void* frame_pointer() const = 0;
/**
* @brief symbol returns the symbolic representation of this frame.
*/
virtual const Symbol& symbol() const = 0;
protected:
Frame() = default;
};
/**
* @brief FrameHandler is the functor invoked for every frame of a backtrace.
*
* A FrameHandler should return true if it wants to continue walking the stack
* or false otherwise.
*/
typedef std::function FrameHandler;
/**
*@brief visit_with_handler iterates the backtrace of the calling program,
*invoking the handler for every frame.
*
* A FrameHandler should return true if it wants to continue walking the stack
* or false otherwise
*
* @param handler The handler invoked for every frame.
*/
void visit_with_handler(const FrameHandler& handler);
}
}
}
#endif // CORE_POSIX_BACKTRACE_H_
process-cpp-3.0.1/src/core/posix/wait.cpp 0000664 0001751 0001751 00000001637 12553403402 020671 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
namespace core
{
namespace posix
{
namespace wait
{
Flags operator|(Flags l, Flags r)
{
return static_cast(static_cast(l) | static_cast(r));
}
}
}
}
process-cpp-3.0.1/src/core/posix/linux/ 0000775 0001751 0001751 00000000000 12553414462 020361 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/src/core/posix/linux/CMakeLists.txt 0000664 0001751 0001751 00000001404 12553403402 023110 0 ustar tvoss tvoss 0000000 0000000 # Copyright © 2013 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Authored by: Thomas Voss
add_library(
linux-process
process.cpp
)
target_link_libraries(
linux-process
posix-process
)
process-cpp-3.0.1/src/core/posix/linux/proc/ 0000775 0001751 0001751 00000000000 12553414462 021324 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/src/core/posix/linux/proc/process/ 0000775 0001751 0001751 00000000000 12553414462 023002 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/src/core/posix/linux/proc/process/oom_score.cpp 0000664 0001751 0001751 00000002224 12553403402 025463 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
namespace core
{
namespace posix
{
namespace linux
{
namespace proc
{
namespace process
{
const posix::Process& operator>>(const posix::Process& process, OomScore& score)
{
std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_score";
std::ifstream in(ss.str());
in >> score.value;
return process;
}
}
}
}
}
}
process-cpp-3.0.1/src/core/posix/linux/proc/process/oom_adj.cpp 0000664 0001751 0001751 00000003272 12553403402 025112 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
#include
#include
namespace core
{
namespace posix
{
namespace linux
{
namespace proc
{
namespace process
{
int OomAdj::disable_value()
{
return OOM_DISABLE;
}
int OomAdj::min_value()
{
return OOM_ADJUST_MIN;
}
int OomAdj::max_value()
{
return OOM_ADJUST_MAX;
}
const posix::Process& operator>>(const posix::Process& process, OomAdj& adj)
{
std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_adj";
std::ifstream in(ss.str());
in >> adj.value;
return process;
}
const posix::Process& operator<<(const posix::Process& process, const OomAdj& adj)
{
if (!adj.is_valid())
throw std::logic_error("Value for adjusting the oom score is invalid.");
std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_adj";
std::ofstream out(ss.str());
out << adj.value;
return process;
}
}
}
}
}
}
process-cpp-3.0.1/src/core/posix/linux/proc/process/oom_score_adj.cpp 0000664 0001751 0001751 00000003313 12553403402 026301 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
#include
#include
namespace core
{
namespace posix
{
namespace linux
{
namespace proc
{
namespace process
{
int OomScoreAdj::min_value()
{
return OOM_SCORE_ADJ_MIN;
}
int OomScoreAdj::max_value()
{
return OOM_SCORE_ADJ_MAX;
}
const posix::Process& operator>>(const posix::Process& process, OomScoreAdj& score_adj)
{
std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_score_adj";
std::ifstream in(ss.str());
in >> score_adj.value;
return process;
}
const posix::Process& operator<<(const posix::Process& process, const OomScoreAdj& score_adj)
{
if (!score_adj.is_valid())
throw std::logic_error("Value for adjusting the oom score is invalid.");
std::stringstream ss; ss << "/proc/" << process.pid() << "/oom_score_adj";
std::ofstream out(ss.str());
out << score_adj.value;
return process;
}
}
}
}
}
}
process-cpp-3.0.1/src/core/posix/linux/proc/process/state.cpp 0000664 0001751 0001751 00000001621 12553403402 024616 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
namespace core
{
namespace posix
{
namespace linux
{
namespace proc
{
namespace process
{
}
}
}
}
}
process-cpp-3.0.1/src/core/posix/linux/proc/process/stat.cpp 0000664 0001751 0001751 00000005303 12553403402 024452 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
#include
namespace core
{
namespace posix
{
namespace linux
{
namespace proc
{
namespace process
{
std::istream& operator>>(std::istream& in, State& state)
{
char c; in >> c; state = static_cast(c);
return in;
}
std::istream& operator>>(std::istream& in, Stat& stat)
{
in >> stat.pid
>> stat.executable
>> stat.state
>> stat.parent
>> stat.process_group
>> stat.session_id
>> stat.tty_nr
>> stat.controlling_process_group
>> stat.kernel_flags
>> stat.minor_faults_count
>> stat.minor_faults_count_by_children
>> stat.major_faults_count
>> stat.major_faults_count_by_children
>> stat.time.user
>> stat.time.system
>> stat.time.user_for_children
>> stat.time.system_for_children
>> stat.priority
>> stat.nice
>> stat.thread_count
>> stat.time_before_next_sig_alarm
>> stat.start_time
>> stat.size.virt
>> stat.size.resident_set
>> stat.size.resident_set_limit
>> stat.addresses.start_code
>> stat.addresses.end_code
>> stat.addresses.start_stack
>> stat.addresses.stack_pointer
>> stat.addresses.instruction_pointer
>> stat.signals.pending
>> stat.signals.blocked
>> stat.signals.ignored
>> stat.signals.caught
>> stat.channel
>> stat.swap_count
>> stat.swap_count_children
>> stat.exit_signal
>> stat.cpu_count
>> stat.realtime_priority
>> stat.scheduling_policy
>> stat.aggregated_block_io_delays
>> stat.guest_time
>> stat.guest_time_children;
return in;
}
const posix::Process& operator>>(const posix::Process& process, Stat& stat)
{
std::stringstream ss; ss << "/proc/" << process.pid() << "/stat";
std::ifstream in(ss.str());
in >> stat;
return process;
}
}
}
}
}
}
process-cpp-3.0.1/README.md 0000664 0001751 0001751 00000006532 12553403402 015616 0 ustar tvoss tvoss 0000000 0000000 process-cpp {#mainpage}
===========
process-cpp is a simple and straightforward wrapper around process creation and
control targeted towards linux. It helps both with handling child processes and with
interacting with the current process. Some of its features include:
- Thread-safe get/set/unset operation on the current process's environment.
- Throwing and non-throwing overloads of functions when system calls are involved.
- Seamless redirection of input, output and error streams of child processes.
- Type-safe interaction with the virtual proc filesystem, both for reading & writing.
The library's main purpose is to assist in testing and when a software component
needs to carry out process creation/control tasks, e.g., a graphical shell. To this end,
the library is extensively tested and tries to ensure fail-safe operation as much as possible.
A simple echo
-------------
~~~~~~~~~~~~~{.cpp}
// Fork and run a simple echo:
posix::ChildProcess child = posix::fork(
[]()
{
std::string line;
while(true)
{
std::cin >> line;
std::cout << line << std::endl;
}
return EXIT_FAILURE;
},
posix::StandardStreamFlags()
.set(posix::StandardStream::stdin)
.set(posix::StandardStream::stdout));
// Check that the resulting process has a valid pid.
EXPECT_TRUE(child.pid() > 0);
// Check on echo functionality.
const std::string echo_value{"42"};
child.cin() << echo_value << std::endl;
std::string line; child.cout() >> line;
EXPECT_EQ(echo_value, line);
// Stop the process and synchronize with the process changing state.
EXPECT_NO_THROW(child.send_signal(posix::Signal::sig_stop));
auto result = child.wait_for(posix::wait::Flag::untraced);
EXPECT_EQ(posix::wait::Result::Status::stopped,
result.status);
EXPECT_EQ(posix::Signal::sig_stop,
result.detail.if_stopped.signal);
// Kill the stopped process and synchronize to its state change.
EXPECT_NO_THROW(child.send_signal(posix::Signal::sig_kill));
result = child.wait_for(posix::wait::Flag::untraced);
EXPECT_EQ(posix::wait::Result::Status::signaled,
result.status);
EXPECT_EQ(posix::Signal::sig_kill,
result.detail.if_signaled.signal);
~~~~~~~~~~~~~
Adjusting OOM Score Values
--------------------------
~~~~~~~~~~~~~{.cpp}
// Setup the manipulator with a well-known value.
posix::linux::proc::process::OomScoreAdj oom_score_adj
{
posix::linux::proc::process::OomScoreAdj::max_value()
};
// Apply the manipulator to the current process
EXPECT_NO_THROW(posix::this_process::instance() << oom_score_adj);
// Read back the manipulators value for the current process
EXPECT_NO_THROW(posix::this_process::instance() >> oom_score_adj);
// And check that applying the manipulator was successful.
EXPECT_EQ(posix::linux::proc::process::OomScoreAdj::max_value(),
oom_score_adj.value);
// Instantiate the observer...
posix::linux::proc::process::OomScore oom_score;
// ... and fill in its value for the current process.
EXPECT_NO_THROW(posix::this_process::instance() >> oom_score);
// Check that applying the manipulator before results in adjustments to the
// OOM score.
EXPECT_TRUE(is_approximately_equal(oom_score.value, posix::linux::proc::process::OomScoreAdj::max_value()));
~~~~~~~~~~~~~
process-cpp-3.0.1/cmake/ 0000775 0001751 0001751 00000000000 12553414462 015421 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/cmake/FindGtest.cmake 0000664 0001751 0001751 00000003372 12553403402 020307 0 ustar tvoss tvoss 0000000 0000000 include(ExternalProject)
include(FindPackageHandleStandardArgs)
#gtest
set(GTEST_INSTALL_DIR /usr/src/gmock/gtest/include)
find_path(GTEST_INCLUDE_DIR gtest/gtest.h
HINTS ${GTEST_INSTALL_DIR})
#gmock
find_path(GMOCK_INSTALL_DIR gmock/CMakeLists.txt
HINTS /usr/src)
if(${GMOCK_INSTALL_DIR} STREQUAL "GMOCK_INSTALL_DIR-NOTFOUND")
message(FATAL_ERROR "google-mock package not found")
endif()
set(GMOCK_INSTALL_DIR ${GMOCK_INSTALL_DIR}/gmock)
find_path(GMOCK_INCLUDE_DIR gmock/gmock.h)
set(GMOCK_PREFIX gmock)
set(GMOCK_BINARY_DIR ${CMAKE_BINARY_DIR}/${GMOCK_PREFIX}/libs)
set(GTEST_BINARY_DIR ${GMOCK_BINARY_DIR}/gtest)
set(GTEST_CMAKE_ARGS "")
if (${MIR_IS_CROSS_COMPILING})
set(GTEST_CMAKE_ARGS
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_MODULE_PATH}/LinuxCrossCompile.cmake)
endif()
ExternalProject_Add(
GMock
#where to build in source tree
PREFIX ${GMOCK_PREFIX}
#where the source is external to the project
SOURCE_DIR ${GMOCK_INSTALL_DIR}
#forward the compilers to the subproject so cross-arch builds work
CMAKE_ARGS ${GTEST_CMAKE_ARGS}
BINARY_DIR ${GMOCK_BINARY_DIR}
#we don't need to install, so skip
INSTALL_COMMAND ""
)
set(GMOCK_LIBRARY ${GMOCK_BINARY_DIR}/libgmock.a)
set(GMOCK_MAIN_LIBRARY ${GMOCK_BINARY_DIR}/libgmock_main.a)
set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY})
set(GTEST_LIBRARY ${GTEST_BINARY_DIR}/libgtest.a)
set(GTEST_MAIN_LIBRARY ${GTEST_BINARY_DIR}/libgtest_main.a)
set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY})
set(GTEST_ALL_LIBRARIES ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES})
find_package_handle_standard_args(GTest DEFAULT_MSG
GMOCK_INCLUDE_DIR
GTEST_INCLUDE_DIR)
process-cpp-3.0.1/tests/ 0000775 0001751 0001751 00000000000 12553414462 015503 5 ustar tvoss tvoss 0000000 0000000 process-cpp-3.0.1/tests/CMakeLists.txt 0000664 0001751 0001751 00000005624 12553403402 020242 0 ustar tvoss tvoss 0000000 0000000 # Copyright © 2013 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Authored by: Thomas Voss
# Build with system gmock and embedded gtest
set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory")
set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory")
set (GTEST_INCLUDE_DIR "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory")
set (GMOCK_BOTH_LIBRARIES gmock gmock_main)
add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock")
#set (OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
# Don't treat warnings as errors in 3rd_party/{gmock,cucumber-cpp}
#string (REPLACE " -Werror " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
#find_package(Gtest REQUIRED)
include_directories(${GMOCK_INCLUDE_DIR} ${GTEST_INCLUDE_DIR})
#set (CMAKE_CXX_FLAGS ${OLD_CMAKE_CXX_FLAGS})
include_directories(
${CMAKE_SOURCE_DIR}/src
${GTEST_INCLUDE_DIR}
)
add_executable(
posix_process_test
posix_process_test.cpp
)
add_executable(
linux_process_test
linux_process_test.cpp
)
add_executable(
fork_and_run_test
fork_and_run_test.cpp
# We include an external source file to prevent from leaking
# symbols to the outside world
${CMAKE_SOURCE_DIR}/src/core/posix/backtrace.cpp
)
add_executable(
cross_process_sync_test
cross_process_sync_test.cpp
)
add_executable(
death_observer_test
death_observer_test.cpp
)
target_link_libraries(
posix_process_test
process-cpp
${CMAKE_THREAD_LIBS_INIT}
${GMOCK_BOTH_LIBRARIES}
)
target_link_libraries(
linux_process_test
process-cpp
${CMAKE_THREAD_LIBS_INIT}
${GMOCK_BOTH_LIBRARIES}
)
target_link_libraries(
fork_and_run_test
process-cpp
${CMAKE_THREAD_LIBS_INIT}
${GMOCK_BOTH_LIBRARIES}
)
target_link_libraries(
cross_process_sync_test
process-cpp
${CMAKE_THREAD_LIBS_INIT}
${GMOCK_BOTH_LIBRARIES}
)
target_link_libraries(
death_observer_test
process-cpp
${CMAKE_THREAD_LIBS_INIT}
${GMOCK_BOTH_LIBRARIES}
)
add_test(posix_process_test ${CMAKE_CURRENT_BINARY_DIR}/posix_process_test)
add_test(linux_process_test ${CMAKE_CURRENT_BINARY_DIR}/linux_process_test)
add_test(fork_and_run_test ${CMAKE_CURRENT_BINARY_DIR}/fork_and_run_test)
add_test(cross_process_sync_test ${CMAKE_CURRENT_BINARY_DIR}/cross_process_sync_test)
add_test(death_observer_test ${CMAKE_CURRENT_BINARY_DIR}/death_observer_test)
process-cpp-3.0.1/tests/posix_process_test.cpp 0000664 0001751 0001751 00000046541 12553403402 022150 0 ustar tvoss tvoss 0000000 0000000 /*
* 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
#include
#include
#include
#include
#include
#include