mirror of
https://github.com/matrix-construct/construct
synced 2025-03-14 05:20:17 +01:00
ircd::proc: Add basic subprocess interface; ios integration.
This commit is contained in:
parent
8b4fdf49c4
commit
1d97263066
5 changed files with 349 additions and 0 deletions
84
include/ircd/exec.h
Normal file
84
include/ircd/exec.h
Normal 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})
|
||||
};
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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
261
ircd/exec.cc
Normal 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;
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue