ircd::proc: Add basic subprocess interface; ios integration.

This commit is contained in:
Jason Volk 2020-10-19 18:37:11 -07:00
parent 8b4fdf49c4
commit 1d97263066
5 changed files with 349 additions and 0 deletions

84
include/ircd/exec.h Normal file
View File

@ -0,0 +1,84 @@
// The Construct
//
// Copyright (C) The Construct Developers, Authors & Contributors
// Copyright (C) 2016-2020 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
#pragma once
#define HAVE_IRCD_EXEC_H
namespace ircd
{
struct exec;
mutable_buffer read(exec &, const mutable_buffer &);
const_buffer write(exec &, const const_buffer &);
}
// Forward declarations for boost because it is not included here.
namespace boost::process
{
struct child;
namespace detail::posix { struct async_pipe; }
namespace detail::windows { struct async_pipe; }
using detail::posix::async_pipe; //TODO: XXX
}
/// Subprocess interface.
///
struct ircd::exec
:instance_list<ircd::exec>
{
using args = vector_view<const string_view>;
using const_buffers = vector_view<const const_buffer>;
using mutable_buffers = vector_view<const mutable_buffer>;
static log::log log;
static uint64_t id_ctr;
uint64_t id {0};
std::string path;
std::vector<std::string> argv;
std::unique_ptr<pair<boost::process::async_pipe>> pipe;
std::unique_ptr<boost::process::child> child;
long pid {0}; // set on spawn
long code {0}; // set on exit
public:
size_t read(const mutable_buffers &);
size_t write(const const_buffers &);
bool signal(const int &sig);
long join(const int &sig = 0);
exec(const args &);
exec(exec &&) = delete;
exec(const exec &) = delete;
exec &operator=(exec &&) = delete;
exec &operator=(const exec &) = delete;
~exec() noexcept;
};
inline ircd::const_buffer
ircd::write(exec &p,
const const_buffer &buf)
{
return const_buffer
{
data(buf), p.write(vector_view<const const_buffer>{&buf, 1})
};
}
inline ircd::mutable_buffer
ircd::read(exec &p,
const mutable_buffer &buf)
{
return mutable_buffer
{
data(buf), p.read(vector_view<const mutable_buffer>{&buf, 1})
};
}

View File

@ -95,6 +95,7 @@
#include "fs/fs.h"
#include "ios.h"
#include "ctx/ctx.h"
#include "exec.h"
#include "db/db.h"
#include "js.h"
#include "mods/mods.h"

View File

@ -176,6 +176,7 @@ libircd_la_SOURCES += mods.cc
if LINUX
libircd_la_SOURCES += mods_ldso.cc
endif
libircd_la_SOURCES += exec.cc
if MAGIC
libircd_la_SOURCES += magic.cc
endif
@ -234,6 +235,7 @@ db_fixes.lo: AM_CPPFLAGS += -isystem $(top_srcdir)/deps/rocksdb/include
db_fixes.lo: AM_CPPFLAGS += -isystem $(top_srcdir)/deps/rocksdb
db_port.lo: AM_CPPFLAGS := ${ROCKSDB_UNIT_CPPFLAGS} ${AM_CPPFLAGS}
exception.lo: AM_CPPFLAGS := ${ASIO_UNIT_CPPFLAGS} ${AM_CPPFLAGS}
exec.lo: AM_CPPFLAGS := ${ASIO_UNIT_CPPFLAGS} ${AM_CPPFLAGS}
fmt.lo: AM_CPPFLAGS := ${SPIRIT_UNIT_CPPFLAGS} ${AM_CPPFLAGS}
fmt.lo: AM_CXXFLAGS := ${SPIRIT_UNIT_CXXFLAGS} ${AM_CXXFLAGS}
fs.lo: AM_CPPFLAGS := ${ASIO_UNIT_CPPFLAGS} ${AM_CPPFLAGS}

261
ircd/exec.cc Normal file
View File

@ -0,0 +1,261 @@
// The Construct
//
// Copyright (C) The Construct Developers, Authors & Contributors
// Copyright (C) 2016-2020 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
#include <boost/process.hpp>
decltype(ircd::exec::log)
ircd::exec::log
{
"exec"
};
decltype(ircd::exec::exec::id_ctr)
ircd::exec::exec::id_ctr;
template<>
decltype(ircd::util::instance_list<ircd::exec>::allocator)
ircd::util::instance_list<ircd::exec>::allocator{};
template<>
decltype(ircd::util::instance_list<ircd::exec>::list)
ircd::util::instance_list<ircd::exec>::list
{
allocator
};
ircd::exec::exec(const args &args)
:id
{
++id_ctr
}
,path
(
args.at(0)
)
,argv
(
std::next(begin(args), 1), end(args)
)
,pipe
{
std::make_unique<pair<boost::process::async_pipe>>
(
static_cast<asio::io_context &>(ios::main.context()),
static_cast<asio::io_context &>(ios::main.context())
)
}
,child
{
std::make_unique<boost::process::child>
(
fs::_path(path),
argv,
(boost::process::std_in) = pipe->first,
(boost::process::std_out & boost::process::std_err) = pipe->second
)
}
,pid
{
child->id()
}
{
log::notice
{
log, "id:%lu pid:%ld `%s' exec argc:%zu",
id,
pid,
path,
argv.size(),
};
}
ircd::exec::~exec()
noexcept try
{
join(SIGKILL);
}
catch(const std::exception &e)
{
log::critical
{
log, "unhandled :%s",
e.what(),
};
}
long
ircd::exec::join(const int &sig)
try
{
if(!child)
return code;
if(!child->valid())
return code;
if(!child->running())
return code;
// when milliseconds=0 this branch appears to be taken too much
if(!child->wait_for(milliseconds(10)))
{
const bool signaled
{
signal(sig)
};
log::dwarning
{
log, "id:%lu pid:%ld `%s' signal:%d waiting for exit...",
id,
pid,
path,
sig,
};
child->wait();
}
assert(!child->running());
code = child->exit_code();
const auto &level
{
code == 0?
log::level::INFO:
log::level::ERROR
};
log::logf
{
log, level,
"id:%lu pid:%ld `%s' exit (%ld)",
id,
pid,
path,
code,
};
return code;
}
catch(const std::exception &e)
{
log::error
{
log, "id:%lu pid:%ld `%s' join :%s",
id,
pid,
path,
e.what(),
};
throw;
}
bool
ircd::exec::signal(const int &sig)
{
if(!child)
return false;
if(!child->valid())
return false;
if(!child->running())
return false;
const bool kill
{
#ifdef SIGKILL
sig == SIGKILL
#else
sig == 9
#endif
};
if(kill)
child->terminate();
return true;
}
size_t
ircd::exec::write(const const_buffers &bufs)
{
assert(pipe);
assert(child);
auto &pipe
{
this->pipe->first
};
const auto interruption
{
[&pipe](ctx::ctx *const &) noexcept
{
if(pipe.is_open())
pipe.cancel();
}
};
size_t ret{0}; ctx::continuation
{
continuation::asio_predicate, interruption, [&pipe, &bufs, &ret]
(auto &yield)
{
ret = pipe.async_write_some(bufs, yield);
}
};
return ret;
}
size_t
ircd::exec::read(const mutable_buffers &bufs)
{
assert(pipe);
assert(child);
auto &pipe
{
this->pipe->second
};
const auto interruption
{
[&pipe](ctx::ctx *const &) noexcept
{
if(pipe.is_open())
pipe.cancel();
}
};
boost::system::error_code ec;
size_t ret{0}; ctx::continuation
{
continuation::asio_predicate, interruption, [&pipe, &bufs, &ret, &ec]
(auto &yield)
{
ret = pipe.async_read_some(bufs, yield[ec]);
}
};
if(ec)
{
assert(!ret);
if(ec == boost::asio::error::eof)
return 0;
throw_system_error(ec);
__builtin_unreachable();
}
assert(ret);
return ret;
}

View File

@ -168,6 +168,7 @@ run git submodule update --init --recursive --checkout libs/coroutine
## of libs/context...
run git submodule update --init --recursive --checkout libs/context
run git submodule update --init --recursive --checkout libs/thread
run git submodule update --init --recursive --checkout libs/process
run git submodule update --init --recursive --checkout libs/chrono
run git submodule update --init --recursive --checkout libs/atomic
run git submodule update --init --recursive --checkout libs/ratio