Initial work
This commit is contained in:
commit
3222551702
374 changed files with 40290 additions and 0 deletions
47
external/process-cpp-minimal/src/CMakeLists.txt
vendored
Normal file
47
external/process-cpp-minimal/src/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Authored by: Thomas Voss <thomas.voss@canonical.com>
|
||||
add_library(
|
||||
process-cpp
|
||||
|
||||
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}
|
||||
)
|
||||
24
external/process-cpp-minimal/src/core/posix/CMakeLists.txt
vendored
Normal file
24
external/process-cpp-minimal/src/core/posix/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Authored by: Thomas Voss <thomas.voss@canonical.com>
|
||||
|
||||
add_library(
|
||||
posix-process
|
||||
|
||||
process.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(linux)
|
||||
|
||||
153
external/process-cpp-minimal/src/core/posix/backtrace.cpp
vendored
Normal file
153
external/process-cpp-minimal/src/core/posix/backtrace.cpp
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authored by: Thomas Voß <thomas.voss@canonical.com>
|
||||
*/
|
||||
|
||||
#include "backtrace.h"
|
||||
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <execinfo.h>
|
||||
|
||||
namespace bt = core::posix::backtrace;
|
||||
|
||||
namespace impl
|
||||
{
|
||||
std::tuple<std::string, bool> 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> bt::Frame::Symbol::for_testing_from_raw_symbol(const char* symbol)
|
||||
{
|
||||
return std::shared_ptr<bt::Frame::Symbol>(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;
|
||||
}
|
||||
}
|
||||
122
external/process-cpp-minimal/src/core/posix/backtrace.h
vendored
Normal file
122
external/process-cpp-minimal/src/core/posix/backtrace.h
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authored by: Thomas Voß <thomas.voss@canonical.com>
|
||||
*/
|
||||
|
||||
#ifndef CORE_POSIX_BACKTRACE_H_
|
||||
#define CORE_POSIX_BACKTRACE_H_
|
||||
|
||||
#include <core/posix/visibility.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
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<Symbol> 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<bool(const Frame& frame)> 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 CORE_POSIX_DLL_PUBLIC visit_with_handler(const FrameHandler& handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CORE_POSIX_BACKTRACE_H_
|
||||
397
external/process-cpp-minimal/src/core/posix/child_process.cpp
vendored
Normal file
397
external/process-cpp-minimal/src/core/posix/child_process.cpp
vendored
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* 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/child_process.h>
|
||||
|
||||
#include <boost/iostreams/device/file_descriptor.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
namespace io = boost::iostreams;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct DeathObserverImpl : public core::posix::ChildProcess::DeathObserver
|
||||
{
|
||||
DeathObserverImpl(const std::shared_ptr<core::posix::SignalTrap>& 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<std::mutex> 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<std::mutex> lg(guard);
|
||||
return children.count(process.pid()) > 0;
|
||||
}
|
||||
|
||||
const core::Signal<core::posix::ChildProcess>& 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<std::mutex> 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<pid_t, core::posix::ChildProcess> children;
|
||||
core::ScopedConnection on_sig_child_connection;
|
||||
struct
|
||||
{
|
||||
core::Signal<core::posix::ChildProcess> child_died;
|
||||
} signals;
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<core::posix::ChildProcess::DeathObserver>
|
||||
core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(
|
||||
std::shared_ptr<core::posix::SignalTrap> trap)
|
||||
{
|
||||
static std::atomic<bool> 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<core::posix::ChildProcess::DeathObserver> 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<core::posix::ChildProcess::DeathObserver>{};
|
||||
}
|
||||
|
||||
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<io::file_descriptor_source> serr;
|
||||
io::stream_buffer<io::file_descriptor_sink> sin;
|
||||
io::stream_buffer<io::file_descriptor_source> 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<int>(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<exit::Status>(WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status))
|
||||
{
|
||||
result.status = wait::Result::Status::signaled;
|
||||
result.detail.if_signaled.signal = static_cast<Signal>(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<Signal>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
external/process-cpp-minimal/src/core/posix/exec.cpp
vendored
Normal file
75
external/process-cpp-minimal/src/core/posix/exec.cpp
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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/exec.h>
|
||||
#include <core/posix/fork.h>
|
||||
#include <core/posix/standard_stream.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
namespace core
|
||||
{
|
||||
namespace posix
|
||||
{
|
||||
ChildProcess exec(const std::string& fn,
|
||||
const std::vector<std::string>& argv,
|
||||
const std::map<std::string, std::string>& env,
|
||||
const StandardStream& flags)
|
||||
{
|
||||
std::function<void()> null_function = [](){};
|
||||
return exec(fn, argv, env, flags, null_function);
|
||||
}
|
||||
|
||||
ChildProcess exec(const std::string& fn,
|
||||
const std::vector<std::string>& argv,
|
||||
const std::map<std::string, std::string>& env,
|
||||
const StandardStream& flags,
|
||||
const std::function<void()>& 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<posix::exit::Status>(execve(fn.c_str(), pargv, penv));
|
||||
}, flags);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
180
external/process-cpp-minimal/src/core/posix/fork.cpp
vendored
Normal file
180
external/process-cpp-minimal/src/core/posix/fork.cpp
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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 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<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 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<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
external/process-cpp-minimal/src/core/posix/linux/CMakeLists.txt
vendored
Normal file
28
external/process-cpp-minimal/src/core/posix/linux/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Authored by: Thomas Voss <thomas.voss@canonical.com>
|
||||
|
||||
add_library(
|
||||
linux-process
|
||||
|
||||
process.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
linux-process
|
||||
|
||||
posix-process
|
||||
)
|
||||
|
||||
81
external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_adj.cpp
vendored
Normal file
81
external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_adj.cpp
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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/linux/proc/process/oom_adj.h>
|
||||
|
||||
#include <core/posix/process.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <linux/oom.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score.cpp
vendored
Normal file
49
external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score.cpp
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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/linux/proc/process/oom_score.h>
|
||||
|
||||
#include <core/posix/process.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score_adj.cpp
vendored
Normal file
76
external/process-cpp-minimal/src/core/posix/linux/proc/process/oom_score_adj.cpp
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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/linux/proc/process/oom_score_adj.h>
|
||||
|
||||
#include <core/posix/process.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <linux/oom.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
external/process-cpp-minimal/src/core/posix/linux/proc/process/stat.cpp
vendored
Normal file
106
external/process-cpp-minimal/src/core/posix/linux/proc/process/stat.cpp
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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/linux/proc/process/stat.h>
|
||||
|
||||
#include <core/posix/process.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <sstream>
|
||||
|
||||
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<State>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
external/process-cpp-minimal/src/core/posix/linux/proc/process/state.cpp
vendored
Normal file
40
external/process-cpp-minimal/src/core/posix/linux/proc/process/state.cpp
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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/linux/proc/process/state.h>
|
||||
|
||||
#include <core/posix/process.h>
|
||||
|
||||
#include <istream>
|
||||
|
||||
namespace core
|
||||
{
|
||||
namespace posix
|
||||
{
|
||||
namespace linux
|
||||
{
|
||||
namespace proc
|
||||
{
|
||||
namespace process
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
86
external/process-cpp-minimal/src/core/posix/process.cpp
vendored
Normal file
86
external/process-cpp-minimal/src/core/posix/process.cpp
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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/process.h>
|
||||
|
||||
#include <core/posix/signal.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
external/process-cpp-minimal/src/core/posix/process_group.cpp
vendored
Normal file
47
external/process-cpp-minimal/src/core/posix/process_group.cpp
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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/process_group.h>
|
||||
|
||||
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})
|
||||
{
|
||||
}
|
||||
|
||||
ProcessGroup ProcessGroup::invalid()
|
||||
{
|
||||
static const pid_t invalid_pid = 1;
|
||||
return ProcessGroup(invalid_pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
221
external/process-cpp-minimal/src/core/posix/signal.cpp
vendored
Normal file
221
external/process-cpp-minimal/src/core/posix/signal.cpp
vendored
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* 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/signalable.h>
|
||||
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
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<core::posix::Signal> 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<int>(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;
|
||||
default:
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
::close(event_fd);
|
||||
}
|
||||
|
||||
bool has(core::posix::Signal signal) override
|
||||
{
|
||||
return ::sigismember(&blocked_signals_mask, static_cast<int>(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<core::posix::Signal>(signal_info[i].ssi_signo)))
|
||||
{
|
||||
on_signal_raised(
|
||||
static_cast<core::posix::Signal>(
|
||||
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<core::posix::Signal>& signal_raised() override
|
||||
{
|
||||
return on_signal_raised;
|
||||
}
|
||||
|
||||
private:
|
||||
Scope scope;
|
||||
std::atomic<State> state;
|
||||
int event_fd;
|
||||
core::Signal<core::posix::Signal> on_signal_raised;
|
||||
::sigset_t old_signals_mask;
|
||||
::sigset_t blocked_signals_mask;
|
||||
};
|
||||
}
|
||||
|
||||
std::shared_ptr<core::posix::SignalTrap> core::posix::trap_signals_for_process(
|
||||
std::initializer_list<core::posix::Signal> blocked_signals)
|
||||
{
|
||||
return std::make_shared<impl::SignalTrap>(
|
||||
impl::SignalTrap::Scope::process,
|
||||
blocked_signals);
|
||||
}
|
||||
|
||||
std::shared_ptr<core::posix::SignalTrap> core::posix::trap_signals_for_all_subsequent_threads(
|
||||
std::initializer_list<core::posix::Signal> blocked_signals)
|
||||
{
|
||||
return std::make_shared<impl::SignalTrap>(
|
||||
impl::SignalTrap::Scope::thread,
|
||||
blocked_signals);
|
||||
}
|
||||
|
||||
52
external/process-cpp-minimal/src/core/posix/signalable.cpp
vendored
Normal file
52
external/process-cpp-minimal/src/core/posix/signalable.cpp
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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/signalable.h>
|
||||
|
||||
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<int>(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<int>(signal));
|
||||
|
||||
if (result == -1)
|
||||
{
|
||||
e = std::error_code(errno, std::system_category());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
external/process-cpp-minimal/src/core/posix/standard_stream.cpp
vendored
Normal file
35
external/process-cpp-minimal/src/core/posix/standard_stream.cpp
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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/standard_stream.h>
|
||||
|
||||
namespace core
|
||||
{
|
||||
namespace posix
|
||||
{
|
||||
StandardStream operator|(StandardStream l, StandardStream r)
|
||||
{
|
||||
return static_cast<StandardStream>(static_cast<std::uint8_t>(l) | static_cast<std::uint8_t>(r));
|
||||
}
|
||||
|
||||
StandardStream operator&(StandardStream l, StandardStream r)
|
||||
{
|
||||
return static_cast<StandardStream>(static_cast<std::uint8_t>(l) & static_cast<std::uint8_t>(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
177
external/process-cpp-minimal/src/core/posix/this_process.cpp
vendored
Normal file
177
external/process-cpp-minimal/src/core/posix/this_process.cpp
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* 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/this_process.h>
|
||||
#include <core/posix/process.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
||||
#if defined(_GNU_SOURCE)
|
||||
#include <unistd.h>
|
||||
#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<void(const std::string&, const std::string&)>& functor) noexcept(true)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
external/process-cpp-minimal/src/core/posix/wait.cpp
vendored
Normal file
33
external/process-cpp-minimal/src/core/posix/wait.cpp
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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/wait.h>
|
||||
|
||||
namespace core
|
||||
{
|
||||
namespace posix
|
||||
{
|
||||
namespace wait
|
||||
{
|
||||
Flags operator|(Flags l, Flags r)
|
||||
{
|
||||
return static_cast<Flags>(static_cast<std::uint8_t>(l) | static_cast<std::uint8_t>(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
external/process-cpp-minimal/src/core/testing/cross_process_sync.cpp
vendored
Normal file
99
external/process-cpp-minimal/src/core/testing/cross_process_sync.cpp
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authored by: Thomas Voss <thomas.voss@canonical.com>
|
||||
*/
|
||||
|
||||
#include <core/testing/cross_process_sync.h>
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
||||
78
external/process-cpp-minimal/src/core/testing/fork_and_run.cpp
vendored
Normal file
78
external/process-cpp-minimal/src/core/testing/fork_and_run.cpp
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authored by: Thomas Voß <thomas.voss@canonical.com>
|
||||
*/
|
||||
|
||||
#include <core/testing/fork_and_run.h>
|
||||
|
||||
#include <core/posix/exit.h>
|
||||
#include <core/posix/fork.h>
|
||||
#include <core/posix/wait.h>
|
||||
|
||||
core::testing::ForkAndRunResult core::testing::operator|(
|
||||
core::testing::ForkAndRunResult lhs,
|
||||
core::testing::ForkAndRunResult rhs)
|
||||
{
|
||||
return static_cast<core::testing::ForkAndRunResult>(
|
||||
static_cast<unsigned int> (lhs) | static_cast<unsigned int>(rhs));
|
||||
}
|
||||
|
||||
core::testing::ForkAndRunResult core::testing::operator&(
|
||||
core::testing::ForkAndRunResult lhs,
|
||||
core::testing::ForkAndRunResult rhs)
|
||||
{
|
||||
return static_cast<core::testing::ForkAndRunResult>(
|
||||
static_cast<unsigned int> (lhs) & static_cast<unsigned int>(rhs));
|
||||
}
|
||||
|
||||
core::testing::ForkAndRunResult core::testing::fork_and_run(
|
||||
const std::function<core::posix::exit::Status()>& service,
|
||||
const std::function<core::posix::exit::Status()>& 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue