mirror of
https://github.com/matrix-construct/construct
synced 2024-11-11 12:31:07 +01:00
construct: Restructure signal handling to object, unit; restructure console into object.
This commit is contained in:
parent
31798dd09a
commit
1906ac57b7
5 changed files with 487 additions and 406 deletions
|
@ -75,6 +75,7 @@ construct_LDADD = \
|
||||||
|
|
||||||
construct_SOURCES = \
|
construct_SOURCES = \
|
||||||
construct.cc \
|
construct.cc \
|
||||||
|
signals.cc \
|
||||||
console.cc \
|
console.cc \
|
||||||
lgetopt.cc \
|
lgetopt.cc \
|
||||||
###
|
###
|
||||||
|
|
|
@ -15,220 +15,176 @@ using namespace ircd;
|
||||||
|
|
||||||
IRCD_EXCEPTION_HIDENAME(ircd::error, bad_command)
|
IRCD_EXCEPTION_HIDENAME(ircd::error, bad_command)
|
||||||
|
|
||||||
bool console_active;
|
decltype(construct::console)
|
||||||
bool console_inwork;
|
construct::console;
|
||||||
ircd::ctx::ctx *console_ctx;
|
|
||||||
ircd::module *console_module;
|
|
||||||
std::string record_path;
|
|
||||||
|
|
||||||
const char *const
|
decltype(construct::console::stack_sz)
|
||||||
generic_message
|
construct::console::stack_sz
|
||||||
{R"(
|
|
||||||
*** - To end the console session: type exit, or ctrl-d -> EOF
|
|
||||||
*** - To shutdown cleanly: type die, or ctrl-\ -> SIGQUIT
|
|
||||||
*** - To generate a coredump for developers, type ABORT -> abort()
|
|
||||||
***
|
|
||||||
)"};
|
|
||||||
|
|
||||||
conf::item<size_t>
|
|
||||||
stack_sz
|
|
||||||
{
|
{
|
||||||
{ "name", "construct.console.stack.size" },
|
{ "name", "construct.console.stack.size" },
|
||||||
{ "default", long(2_MiB) },
|
{ "default", long(2_MiB) },
|
||||||
};
|
};
|
||||||
|
|
||||||
conf::item<milliseconds>
|
decltype(construct::console::input_max)
|
||||||
ratelimit_sleep
|
construct::console::input_max
|
||||||
|
{
|
||||||
|
{ "name", "construct.console.input.max" },
|
||||||
|
{ "default", long(64_KiB) },
|
||||||
|
};
|
||||||
|
|
||||||
|
decltype(construct::console::ratelimit_sleep)
|
||||||
|
construct::console::ratelimit_sleep
|
||||||
{
|
{
|
||||||
{ "name", "construct.console.ratelimit.sleep" },
|
{ "name", "construct.console.ratelimit.sleep" },
|
||||||
{ "default", 75L },
|
{ "default", 75L },
|
||||||
};
|
};
|
||||||
|
|
||||||
conf::item<size_t>
|
decltype(construct::console::ratelimit_bytes)
|
||||||
ratelimit_bytes
|
construct::console::ratelimit_bytes
|
||||||
{
|
{
|
||||||
{ "name", "construct.console.ratelimit.bytes" },
|
{ "name", "construct.console.ratelimit.bytes" },
|
||||||
{ "default", long(2_KiB) },
|
{ "default", long(2_KiB) },
|
||||||
};
|
};
|
||||||
|
|
||||||
static void check_console_active();
|
decltype(construct::console::generic_message)
|
||||||
static void console_fini();
|
construct::console::generic_message
|
||||||
static void console_init();
|
{R"(
|
||||||
static bool console_cmd__record(const string_view &line);
|
*** - To end the console session: type exit, or ctrl-d -> EOF
|
||||||
static int handle_line_bymodule(const string_view &line);
|
*** - To shutdown cleanly: type die, or ctrl-\ -> SIGQUIT
|
||||||
static bool handle_line(const string_view &line);
|
*** - To generate a coredump for developers, type ABORT -> abort()
|
||||||
static void execute(const std::vector<std::string> lines);
|
***)"_sv
|
||||||
static void console();
|
};
|
||||||
|
|
||||||
void
|
decltype(construct::console::console_message)
|
||||||
console_spawn()
|
construct::console::console_message
|
||||||
{
|
|
||||||
check_console_active();
|
|
||||||
ircd::context
|
|
||||||
{
|
|
||||||
"console",
|
|
||||||
stack_sz,
|
|
||||||
console,
|
|
||||||
ircd::context::DETACH | ircd::context::POST
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
console_execute(const std::vector<std::string> &lines)
|
|
||||||
{
|
|
||||||
check_console_active();
|
|
||||||
ircd::context
|
|
||||||
{
|
|
||||||
"execute",
|
|
||||||
stack_sz,
|
|
||||||
std::bind(&execute, lines),
|
|
||||||
ircd::context::DETACH | ircd::context::POST
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
console_init()
|
|
||||||
{
|
|
||||||
console_cancel();
|
|
||||||
console_active = true;
|
|
||||||
console_ctx = &ctx::cur();
|
|
||||||
console_module = new ircd::module{"console"};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
console_fini()
|
|
||||||
{
|
|
||||||
console_active = false;
|
|
||||||
console_ctx = nullptr;
|
|
||||||
delete console_module; console_module = nullptr;
|
|
||||||
std::cin.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *const console_message
|
|
||||||
{R"(
|
{R"(
|
||||||
***
|
***
|
||||||
*** The server is still running in the background. This is the
|
*** The server is still running in the background. This is the
|
||||||
*** terminal console also available in your !control room.
|
*** terminal console also available in your !control room.
|
||||||
***)"};
|
***)"_sv
|
||||||
|
};
|
||||||
|
|
||||||
|
decltype(construct::console::seen_message)
|
||||||
|
construct::console::seen_message;
|
||||||
|
|
||||||
|
decltype(construct::console::queue)
|
||||||
|
construct::console::queue;
|
||||||
|
|
||||||
|
bool
|
||||||
|
construct::console::spawn()
|
||||||
|
{
|
||||||
|
if(active())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
construct::console = new console;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
construct::console::execute(std::string cmd)
|
||||||
|
{
|
||||||
|
console::queue.emplace_back(std::move(cmd));
|
||||||
|
console::spawn();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
construct::console::interrupt()
|
||||||
|
{
|
||||||
|
if(active())
|
||||||
|
{
|
||||||
|
construct::console->context.interrupt();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
construct::console::terminate()
|
||||||
|
{
|
||||||
|
if(active())
|
||||||
|
{
|
||||||
|
construct::console->context.terminate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
construct::console::active()
|
||||||
|
{
|
||||||
|
return construct::console != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// console::console
|
||||||
|
//
|
||||||
|
|
||||||
|
construct::console::console()
|
||||||
|
:context
|
||||||
|
{
|
||||||
|
"console",
|
||||||
|
stack_sz,
|
||||||
|
std::bind(&console::main, this),
|
||||||
|
ircd::context::POST
|
||||||
|
}
|
||||||
|
,runlevel_changed
|
||||||
|
{
|
||||||
|
std::bind(&console::on_runlevel, this, std::placeholders::_1)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
console()
|
construct::console::main()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ircd::runlevel_changed::dock.wait([]
|
const unwind destruct{[]
|
||||||
{
|
{
|
||||||
return ircd::runlevel == ircd::runlevel::RUN ||
|
construct::console->context.detach();
|
||||||
ircd::runlevel == ircd::runlevel::QUIT ||
|
delete construct::console;
|
||||||
ircd::runlevel == ircd::runlevel::HALT;
|
construct::console = nullptr;
|
||||||
});
|
}};
|
||||||
|
|
||||||
if(ircd::runlevel != ircd::runlevel::RUN)
|
if(!wait_running())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const unwind atexit([]
|
ircd::module module{"console"};
|
||||||
|
this->module = &module;
|
||||||
|
|
||||||
|
if(next_command())
|
||||||
{
|
{
|
||||||
console_fini();
|
while(handle_line())
|
||||||
});
|
if(!next_command())
|
||||||
|
break;
|
||||||
|
|
||||||
console_init();
|
return;
|
||||||
static std::once_flag seen_message;
|
|
||||||
std::call_once(seen_message, []
|
|
||||||
{
|
|
||||||
std::cout << console_message << generic_message;
|
|
||||||
});
|
|
||||||
|
|
||||||
while(1)
|
|
||||||
{
|
|
||||||
std::cout << "\n> " << std::flush;
|
|
||||||
|
|
||||||
thread_local char buf[64_KiB];
|
|
||||||
string_view line;
|
|
||||||
// Suppression scope ends after the command is entered
|
|
||||||
// so the output of the command (if log messages) can be seen.
|
|
||||||
{
|
|
||||||
const log::console_quiet quiet(false);
|
|
||||||
line = fs::stdin::readline(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(line.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(!handle_line(line))
|
|
||||||
break;
|
|
||||||
|
|
||||||
ctx::interruption_point();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << std::endl;
|
show_message(); do
|
||||||
|
{
|
||||||
|
wait_input();
|
||||||
|
}
|
||||||
|
while(handle_line());
|
||||||
}
|
}
|
||||||
catch(const std::exception &e)
|
catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
std::cout << std::endl;
|
std::cout
|
||||||
std::cout << "***" << std::endl;
|
<< "\n***"
|
||||||
std::cout << "*** The console session has ended: " << e.what() << std::endl;
|
<< "\n*** The console session has ended: " << e.what()
|
||||||
std::cout << "***" << std::endl;
|
<< "\n***"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
ircd::log::debug
|
log::debug
|
||||||
{
|
{
|
||||||
"The console session has ended: %s", e.what()
|
"The console session has ended: %s", e.what()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
execute(const std::vector<std::string> lines)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ircd::runlevel_changed::dock.wait([]
|
|
||||||
{
|
|
||||||
return ircd::runlevel == ircd::runlevel::RUN ||
|
|
||||||
ircd::runlevel == ircd::runlevel::HALT;
|
|
||||||
});
|
|
||||||
|
|
||||||
if(ircd::runlevel == ircd::runlevel::HALT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const unwind atexit([]
|
|
||||||
{
|
|
||||||
console_fini();
|
|
||||||
});
|
|
||||||
|
|
||||||
console_init();
|
|
||||||
for(const auto &line : lines)
|
|
||||||
{
|
|
||||||
if(line.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(!handle_line(line))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
std::cout << std::endl;
|
|
||||||
std::cout << "***\n";
|
|
||||||
std::cout << "*** The execution aborted: " << e.what() << "\n";
|
|
||||||
std::cout << "***" << std::endl;
|
|
||||||
|
|
||||||
std::cout << std::flush;
|
|
||||||
std::cout.clear();
|
|
||||||
ircd::log::debug
|
|
||||||
{
|
|
||||||
"The execution aborted: %s", e.what()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
handle_line(const string_view &line)
|
construct::console::handle_line()
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// _theirs is copied for recursive reentrance
|
|
||||||
const unwind outwork{[console_inwork_theirs(console_inwork)]
|
|
||||||
{
|
|
||||||
console_inwork = console_inwork_theirs;
|
|
||||||
}};
|
|
||||||
|
|
||||||
console_inwork = true;
|
|
||||||
|
|
||||||
if(line == "ABORT")
|
if(line == "ABORT")
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
|
@ -236,10 +192,10 @@ try
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
if(startswith(line, "record"))
|
if(startswith(line, "record"))
|
||||||
return console_cmd__record(tokens_after(line, ' ', 0));
|
return cmd__record();
|
||||||
|
|
||||||
int ret{-1};
|
int ret{-1};
|
||||||
if(console_module) switch((ret = handle_line_bymodule(line)))
|
if(module) switch((ret = handle_line_bymodule()))
|
||||||
{
|
{
|
||||||
default: break;
|
default: break;
|
||||||
case 0: return false;
|
case 0: return false;
|
||||||
|
@ -261,9 +217,9 @@ catch(const bad_command &e)
|
||||||
std::cerr << "Bad command or file name: " << e.what() << std::endl;
|
std::cerr << "Bad command or file name: " << e.what() << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch(const ircd::http::error &e)
|
catch(const http::error &e)
|
||||||
{
|
{
|
||||||
ircd::log::error
|
log::error
|
||||||
{
|
{
|
||||||
"%s %s", e.what(), e.content
|
"%s %s", e.what(), e.content
|
||||||
};
|
};
|
||||||
|
@ -272,7 +228,7 @@ catch(const ircd::http::error &e)
|
||||||
}
|
}
|
||||||
catch(const std::exception &e)
|
catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
ircd::log::error
|
log::error
|
||||||
{
|
{
|
||||||
"%s", e.what()
|
"%s", e.what()
|
||||||
};
|
};
|
||||||
|
@ -281,12 +237,13 @@ catch(const std::exception &e)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
handle_line_bymodule(const string_view &line)
|
construct::console::handle_line_bymodule()
|
||||||
{
|
{
|
||||||
using prototype = int (std::ostream &, const string_view &, const string_view &);
|
using prototype = int (std::ostream &, const string_view &, const string_view &);
|
||||||
const ircd::mods::import<prototype> command
|
|
||||||
|
const mods::import<prototype> command
|
||||||
{
|
{
|
||||||
*console_module, "console_command"
|
*module, "console_command"
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
|
@ -322,7 +279,7 @@ handle_line_bymodule(const string_view &line)
|
||||||
// to the output following it.
|
// to the output following it.
|
||||||
const std::string cmdline
|
const std::string cmdline
|
||||||
{
|
{
|
||||||
"\n> "s + std::string{line} + "\n\n"
|
"\n> "s + line + "\n\n"
|
||||||
};
|
};
|
||||||
|
|
||||||
append(fd, string_view(cmdline));
|
append(fd, string_view(cmdline));
|
||||||
|
@ -369,15 +326,20 @@ handle_line_bymodule(const string_view &line)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
console_cmd__record(const string_view &line)
|
construct::console::cmd__record()
|
||||||
{
|
{
|
||||||
if(empty(line) && empty(record_path))
|
const string_view &args
|
||||||
|
{
|
||||||
|
tokens_after(line, ' ', 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
if(empty(args) && empty(record_path))
|
||||||
{
|
{
|
||||||
std::cout << "Console not currently recorded to any file." << std::endl;
|
std::cout << "Console not currently recorded to any file." << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(empty(line) && !empty(record_path))
|
if(empty(args) && !empty(record_path))
|
||||||
{
|
{
|
||||||
std::cout << "Stopped recording to file `" << record_path << "'" << std::endl;
|
std::cout << "Stopped recording to file `" << record_path << "'" << std::endl;
|
||||||
record_path = {};
|
record_path = {};
|
||||||
|
@ -386,7 +348,7 @@ console_cmd__record(const string_view &line)
|
||||||
|
|
||||||
const auto path
|
const auto path
|
||||||
{
|
{
|
||||||
token(line, ' ', 0)
|
token(args, ' ', 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
std::cout << "Recording console to file `" << path << "'" << std::endl;
|
std::cout << "Recording console to file `" << path << "'" << std::endl;
|
||||||
|
@ -395,57 +357,80 @@ console_cmd__record(const string_view &line)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
check_console_active()
|
construct::console::wait_input()
|
||||||
{
|
{
|
||||||
if(console_active)
|
line = {}; do
|
||||||
throw ircd::error
|
{
|
||||||
|
// Suppression scope ends after the command is entered
|
||||||
|
// so the output of the command (if log messages) can be seen.
|
||||||
|
const log::console_quiet quiet(false);
|
||||||
|
std::cout << "\n> " << std::flush;
|
||||||
|
|
||||||
|
line.resize(size_t(input_max));
|
||||||
|
const mutable_buffer buffer
|
||||||
{
|
{
|
||||||
"Console is already active and cannot be reentered"
|
const_cast<char *>(line.data()), line.size()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const string_view read
|
||||||
|
{
|
||||||
|
fs::stdin::readline(buffer)
|
||||||
|
};
|
||||||
|
|
||||||
|
line.resize(size(read));
|
||||||
|
}
|
||||||
|
while(line.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
console_hangup()
|
construct::console::next_command()
|
||||||
try
|
|
||||||
{
|
{
|
||||||
using log::console_quiet;
|
line = {};
|
||||||
|
while(!queue.empty() && line.empty())
|
||||||
console_cancel();
|
|
||||||
|
|
||||||
static console_quiet *quieted;
|
|
||||||
if(!quieted)
|
|
||||||
{
|
{
|
||||||
log::notice("Suppressing console log output after terminal hangup");
|
line = std::move(queue.front());
|
||||||
quieted = new console_quiet;
|
queue.pop_front();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log::notice("Reactivating console logging after second hangup");
|
return !line.empty();
|
||||||
delete quieted;
|
|
||||||
quieted = nullptr;
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
ircd::log::error
|
|
||||||
{
|
|
||||||
"console_hangup(): %s", e.what()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
console_cancel()
|
construct::console::on_runlevel(const enum ircd::runlevel &runlevel)
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if(!console_active)
|
switch(runlevel)
|
||||||
return;
|
|
||||||
|
|
||||||
if(console_ctx)
|
|
||||||
interrupt(*console_ctx);
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
ircd::log::error
|
|
||||||
{
|
{
|
||||||
"Interrupting console: %s", e.what()
|
case ircd::runlevel::QUIT:
|
||||||
};
|
case ircd::runlevel::HALT:
|
||||||
|
case ircd::runlevel::FAULT:
|
||||||
|
console::terminate();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
construct::console::wait_running()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
ircd::runlevel_changed::dock.wait([]
|
||||||
|
{
|
||||||
|
return ircd::runlevel == ircd::runlevel::RUN ||
|
||||||
|
ircd::runlevel == ircd::runlevel::QUIT ||
|
||||||
|
ircd::runlevel == ircd::runlevel::HALT;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ircd::runlevel == ircd::runlevel::RUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
construct::console::show_message()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
std::call_once(seen_message, []
|
||||||
|
{
|
||||||
|
std::cout << console_message << generic_message;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "lgetopt.h"
|
#include "lgetopt.h"
|
||||||
#include "construct.h"
|
#include "construct.h"
|
||||||
|
|
||||||
static void sigfd_handler(const boost::system::error_code &, int) noexcept;
|
|
||||||
static bool startup_checks();
|
static bool startup_checks();
|
||||||
static void applyargs();
|
static void applyargs();
|
||||||
static void enable_coredumps();
|
static void enable_coredumps();
|
||||||
|
@ -77,11 +76,6 @@ std::unique_ptr<boost::asio::io_context> ios
|
||||||
std::make_unique<boost::asio::io_context>()
|
std::make_unique<boost::asio::io_context>()
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::asio::signal_set sigs
|
|
||||||
{
|
|
||||||
*ios
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char *const *argv)
|
int main(int argc, char *const *argv)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -147,39 +141,15 @@ try
|
||||||
// event loop. This means we lose the true instant hardware-interrupt gratitude
|
// event loop. This means we lose the true instant hardware-interrupt gratitude
|
||||||
// of signals but with the benefit of unconditional safety and cross-
|
// of signals but with the benefit of unconditional safety and cross-
|
||||||
// platformness with windows etc.
|
// platformness with windows etc.
|
||||||
sigs.add(SIGHUP);
|
const construct::signals signals{*ios};
|
||||||
sigs.add(SIGINT);
|
|
||||||
sigs.add(SIGQUIT);
|
|
||||||
sigs.add(SIGTERM);
|
|
||||||
sigs.add(SIGUSR1);
|
|
||||||
sigs.async_wait(sigfd_handler);
|
|
||||||
|
|
||||||
// Because we registered signal handlers with the io_context, ios->run()
|
|
||||||
// is now shared between those handlers and libircd. This means the run()
|
|
||||||
// won't return even if we call ircd::quit(). We use the callback to then
|
|
||||||
// cancel the handlers so run() can return and the program can exit.
|
|
||||||
const ircd::runlevel_changed handler{[]
|
|
||||||
(const auto &runlevel)
|
|
||||||
{
|
|
||||||
switch(runlevel)
|
|
||||||
{
|
|
||||||
case ircd::runlevel::HALT:
|
|
||||||
case ircd::runlevel::FAULT:
|
|
||||||
sigs.cancel();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
|
|
||||||
// If the user wants to immediately drop to a command line without having to
|
// If the user wants to immediately drop to a command line without having to
|
||||||
// send a ctrl-c for it, that is provided here.
|
// send a ctrl-c for it, that is provided here.
|
||||||
if(cmdline)
|
if(cmdline)
|
||||||
console_spawn();
|
construct::console::spawn();
|
||||||
|
|
||||||
if(execute)
|
if(execute)
|
||||||
console_execute({execute});
|
construct::console::execute({execute});
|
||||||
|
|
||||||
// Execution.
|
// Execution.
|
||||||
// Blocks until a clean exit from a quit() or an exception comes out of it.
|
// Blocks until a clean exit from a quit() or an exception comes out of it.
|
||||||
|
@ -305,145 +275,3 @@ applyargs()
|
||||||
else
|
else
|
||||||
ircd::fs::aio::enable.set("true");
|
ircd::fs::aio::enable.set("true");
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Signal handling
|
|
||||||
//
|
|
||||||
|
|
||||||
static void handle_quit();
|
|
||||||
static void handle_interruption();
|
|
||||||
static void handle_hangup();
|
|
||||||
static void handle_usr1();
|
|
||||||
static void handle(const int &signum);
|
|
||||||
|
|
||||||
void
|
|
||||||
sigfd_handler(const boost::system::error_code &ec,
|
|
||||||
int signum)
|
|
||||||
noexcept
|
|
||||||
{
|
|
||||||
switch(ec.value())
|
|
||||||
{
|
|
||||||
using namespace boost::system::errc;
|
|
||||||
|
|
||||||
case success:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case operation_canceled:
|
|
||||||
console_cancel();
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
console_cancel();
|
|
||||||
throw std::runtime_error(ec.message());
|
|
||||||
}
|
|
||||||
|
|
||||||
handle(signum);
|
|
||||||
|
|
||||||
switch(ircd::runlevel)
|
|
||||||
{
|
|
||||||
case ircd::runlevel::QUIT:
|
|
||||||
case ircd::runlevel::FAULT:
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
sigs.async_wait(sigfd_handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handle(const int &signum)
|
|
||||||
{
|
|
||||||
switch(signum)
|
|
||||||
{
|
|
||||||
case SIGINT: return handle_interruption();
|
|
||||||
case SIGHUP: return handle_hangup();
|
|
||||||
case SIGQUIT: return handle_quit();
|
|
||||||
case SIGTERM: return handle_quit();
|
|
||||||
case SIGUSR1: return handle_usr1();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handle_quit()
|
|
||||||
try
|
|
||||||
{
|
|
||||||
console_cancel();
|
|
||||||
ircd::quit();
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
ircd::log::error
|
|
||||||
{
|
|
||||||
"SIGQUIT handler: %s", e.what()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handle_hangup()
|
|
||||||
try
|
|
||||||
{
|
|
||||||
console_hangup();
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
ircd::log::error
|
|
||||||
{
|
|
||||||
"SIGHUP handler: %s", e.what()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handle_interruption()
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if(console_active)
|
|
||||||
console_cancel();
|
|
||||||
else
|
|
||||||
console_spawn();
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
ircd::log::error
|
|
||||||
{
|
|
||||||
"SIGINT handler: %s", e.what()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handle_usr1()
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Spawning the context that follows this branch and doing a rehash
|
|
||||||
// when not in a stable state like runlevel::RUN will just make a mess
|
|
||||||
// so any signal received is just dropped and the user can try again.
|
|
||||||
if(ircd::runlevel != ircd::runlevel::RUN)
|
|
||||||
{
|
|
||||||
ircd::log::warning
|
|
||||||
{
|
|
||||||
"Not rehashing conf from SIGUSR1 in runlevel %s",
|
|
||||||
reflect(ircd::runlevel)
|
|
||||||
};
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This signal handler (though not a *real* signal handler) is still
|
|
||||||
// running on the main async stack and not an ircd::ctx. The reload
|
|
||||||
// function does a lot of IO so it requires an ircd::ctx.
|
|
||||||
ircd::context{[]
|
|
||||||
{
|
|
||||||
ircd::mods::import<void ()> reload_conf
|
|
||||||
{
|
|
||||||
"s_conf", "reload_conf"
|
|
||||||
};
|
|
||||||
|
|
||||||
reload_conf();
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
catch(const std::exception &e)
|
|
||||||
{
|
|
||||||
ircd::log::error
|
|
||||||
{
|
|
||||||
"SIGUSR1 handler: %s", e.what()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,15 +8,61 @@
|
||||||
// copyright notice and this permission notice is present in all copies. The
|
// copyright notice and this permission notice is present in all copies. The
|
||||||
// full license for this software is available in the LICENSE file.
|
// full license for this software is available in the LICENSE file.
|
||||||
|
|
||||||
//
|
namespace construct
|
||||||
// console.cc
|
{
|
||||||
//
|
struct signals;
|
||||||
|
struct console;
|
||||||
|
|
||||||
extern bool quietmode;
|
extern struct console *console;
|
||||||
extern bool console_active;
|
}
|
||||||
|
|
||||||
void console_spawn();
|
struct construct::signals
|
||||||
void console_execute(const std::vector<std::string> &lines);
|
{
|
||||||
void console_cancel();
|
std::unique_ptr<boost::asio::signal_set> signal_set;
|
||||||
void console_hangup();
|
ircd::runlevel_changed runlevel_changed;
|
||||||
void console_termstop();
|
|
||||||
|
void set_handle();
|
||||||
|
void on_signal(const boost::system::error_code &, int) noexcept;
|
||||||
|
void on_runlevel(const enum ircd::runlevel &);
|
||||||
|
|
||||||
|
public:
|
||||||
|
signals(boost::asio::io_context &ios);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct construct::console
|
||||||
|
{
|
||||||
|
static ircd::conf::item<size_t> stack_sz;
|
||||||
|
static ircd::conf::item<size_t> input_max;
|
||||||
|
static ircd::conf::item<size_t> ratelimit_bytes;
|
||||||
|
static ircd::conf::item<ircd::milliseconds> ratelimit_sleep;
|
||||||
|
|
||||||
|
static const ircd::string_view generic_message;
|
||||||
|
static const ircd::string_view console_message;
|
||||||
|
static std::once_flag seen_message;
|
||||||
|
static std::deque<std::string> queue;
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
std::string record_path;
|
||||||
|
ircd::module *module {nullptr};
|
||||||
|
ircd::context context;
|
||||||
|
ircd::runlevel_changed runlevel_changed;
|
||||||
|
|
||||||
|
void show_message() const;
|
||||||
|
void on_runlevel(const enum ircd::runlevel &);
|
||||||
|
bool wait_running() const;
|
||||||
|
bool next_command();
|
||||||
|
void wait_input();
|
||||||
|
|
||||||
|
bool cmd__record();
|
||||||
|
int handle_line_bymodule();
|
||||||
|
bool handle_line();
|
||||||
|
void main();
|
||||||
|
|
||||||
|
console();
|
||||||
|
|
||||||
|
static bool active();
|
||||||
|
static bool interrupt();
|
||||||
|
static bool terminate();
|
||||||
|
static bool execute(std::string cmd);
|
||||||
|
static bool spawn();
|
||||||
|
};
|
||||||
|
|
221
construct/signals.cc
Normal file
221
construct/signals.cc
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
// Matrix Construct
|
||||||
|
//
|
||||||
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||||
|
// Copyright (C) 2016-2018 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 <ircd/ircd.h>
|
||||||
|
#include <ircd/asio.h>
|
||||||
|
#include "construct.h"
|
||||||
|
|
||||||
|
namespace construct
|
||||||
|
{
|
||||||
|
namespace ph = std::placeholders;
|
||||||
|
|
||||||
|
static void handle_usr1();
|
||||||
|
static void handle_quit();
|
||||||
|
static void handle_interrupt();
|
||||||
|
static void handle_hangup();
|
||||||
|
static void handle_signal(const int &);
|
||||||
|
}
|
||||||
|
|
||||||
|
construct::signals::signals(boost::asio::io_context &ios)
|
||||||
|
:signal_set
|
||||||
|
{
|
||||||
|
std::make_unique<boost::asio::signal_set>(ios)
|
||||||
|
}
|
||||||
|
,runlevel_changed
|
||||||
|
{
|
||||||
|
std::bind(&signals::on_runlevel, this, ph::_1)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
signal_set->add(SIGHUP);
|
||||||
|
signal_set->add(SIGINT);
|
||||||
|
signal_set->add(SIGQUIT);
|
||||||
|
signal_set->add(SIGTERM);
|
||||||
|
signal_set->add(SIGUSR1);
|
||||||
|
set_handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because we registered signal handlers with the io_context, ios->run()
|
||||||
|
// is now shared between those handlers and libircd. This means the run()
|
||||||
|
// won't return even if we call ircd::quit(). We use this callback to
|
||||||
|
// cancel the signal handlers so run() can return and the program can exit.
|
||||||
|
void
|
||||||
|
construct::signals::on_runlevel(const enum ircd::runlevel &runlevel)
|
||||||
|
{
|
||||||
|
switch(runlevel)
|
||||||
|
{
|
||||||
|
case ircd::runlevel::HALT:
|
||||||
|
case ircd::runlevel::FAULT:
|
||||||
|
signal_set->cancel();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
construct::signals::on_signal(const boost::system::error_code &ec,
|
||||||
|
int signum)
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
assert(!ec || ec.category() == boost::system::system_category());
|
||||||
|
|
||||||
|
switch(ec.value())
|
||||||
|
{
|
||||||
|
// Signal received
|
||||||
|
case boost::system::errc::success:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Shutdown
|
||||||
|
case boost::system::errc::operation_canceled:
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Not expected
|
||||||
|
default:
|
||||||
|
ircd::throw_system_error(ec);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_signal(signum);
|
||||||
|
|
||||||
|
switch(ircd::runlevel)
|
||||||
|
{
|
||||||
|
// Reinstall handler for next signal
|
||||||
|
default:
|
||||||
|
set_handle();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// No reinstall of handler.
|
||||||
|
case ircd::runlevel::QUIT:
|
||||||
|
case ircd::runlevel::FAULT:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
construct::signals::set_handle()
|
||||||
|
{
|
||||||
|
auto handler
|
||||||
|
{
|
||||||
|
std::bind(&signals::on_signal, this, ph::_1, ph::_2)
|
||||||
|
};
|
||||||
|
|
||||||
|
signal_set->async_wait(std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
construct::handle_signal(const int &signum)
|
||||||
|
{
|
||||||
|
switch(signum)
|
||||||
|
{
|
||||||
|
case SIGINT: return handle_interrupt();
|
||||||
|
case SIGHUP: return handle_hangup();
|
||||||
|
case SIGQUIT: return handle_quit();
|
||||||
|
case SIGTERM: return handle_quit();
|
||||||
|
case SIGUSR1: return handle_usr1();
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ircd::log::error
|
||||||
|
{
|
||||||
|
"Caught unhandled signal %d", signum
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
construct::handle_quit()
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ircd::quit();
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
ircd::log::error
|
||||||
|
{
|
||||||
|
"SIGQUIT handler: %s", e.what()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
construct::handle_hangup()
|
||||||
|
try
|
||||||
|
{
|
||||||
|
static bool console_disabled;
|
||||||
|
console_disabled =! console_disabled;
|
||||||
|
|
||||||
|
if(console_disabled)
|
||||||
|
ircd::log::console_disable();
|
||||||
|
else
|
||||||
|
ircd::log::console_enable();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
ircd::log::error
|
||||||
|
{
|
||||||
|
"SIGHUP handler: %s", e.what()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
construct::handle_interrupt()
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!console::active())
|
||||||
|
console::spawn();
|
||||||
|
else
|
||||||
|
console::interrupt();
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
ircd::log::error
|
||||||
|
{
|
||||||
|
"SIGINT handler: %s", e.what()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
construct::handle_usr1()
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Spawning the context that follows this branch and doing a rehash
|
||||||
|
// when not in a stable state like runlevel::RUN will just make a mess
|
||||||
|
// so any signal received is just dropped and the user can try again.
|
||||||
|
if(ircd::runlevel != ircd::runlevel::RUN)
|
||||||
|
{
|
||||||
|
ircd::log::warning
|
||||||
|
{
|
||||||
|
"Not rehashing conf from SIGUSR1 in runlevel %s",
|
||||||
|
reflect(ircd::runlevel)
|
||||||
|
};
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This signal handler (though not a *real* signal handler) is still
|
||||||
|
// running on the main async stack and not an ircd::ctx. The reload
|
||||||
|
// function does a lot of IO so it requires an ircd::ctx.
|
||||||
|
ircd::context{[]
|
||||||
|
{
|
||||||
|
ircd::mods::import<void ()> reload_conf
|
||||||
|
{
|
||||||
|
"s_conf", "reload_conf"
|
||||||
|
};
|
||||||
|
|
||||||
|
reload_conf();
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
catch(const std::exception &e)
|
||||||
|
{
|
||||||
|
ircd::log::error
|
||||||
|
{
|
||||||
|
"SIGUSR1 handler: %s", e.what()
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue