0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-06 12:08:56 +02:00
construct/construct/console.cc

378 lines
7.1 KiB
C++
Raw Normal View History

2018-02-04 03:22:01 +01:00
// 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.
2017-03-21 03:24:41 +01:00
#include <ircd/ircd.h>
#include <ircd/asio.h>
2017-11-30 20:44:23 +01:00
#include <ircd/m/m.h>
#include <ircd/util/params.h>
2018-02-22 22:33:53 +01:00
#include "construct.h"
2017-03-21 03:24:41 +01:00
using namespace ircd;
2018-02-22 22:28:34 +01:00
IRCD_EXCEPTION_HIDENAME(ircd::error, bad_command)
2017-03-21 03:24:41 +01:00
const char *const generic_message
{R"(
2017-08-23 23:11:40 +02:00
*** - To end the console session: type ctrl-d -> EOF
*** - To exit cleanly: type exit, die, or ctrl-\ -> SIGQUIT
2017-03-21 03:24:41 +01:00
*** - To generate a coredump for developers, type ABORT -> abort()
***
)"};
const size_t stack_sz
{
2017-11-25 23:31:58 +01:00
8_MiB
};
2017-03-21 03:24:41 +01:00
bool console_active;
bool console_inwork;
2017-03-21 03:24:41 +01:00
ircd::ctx::ctx *console_ctx;
2018-02-22 22:28:34 +01:00
ircd::module *console_module;
2017-03-21 03:24:41 +01:00
2018-02-22 22:28:34 +01:00
static void check_console_active();
static void console_fini();
static void console_init();
static int handle_line_bymodule(const string_view &line);
2018-02-22 22:28:34 +01:00
static bool handle_line(const string_view &line);
static void execute(const std::vector<std::string> lines);
2017-03-21 03:24:41 +01:00
static void console();
2017-11-25 23:31:58 +01:00
void
2018-02-22 22:28:34 +01:00
console_spawn()
2017-11-25 23:31:58 +01:00
{
check_console_active();
ircd::context
{
2018-02-22 22:28:34 +01:00
"console",
stack_sz,
console,
ircd::context::DETACH | ircd::context::POST
};
}
2017-03-21 03:24:41 +01:00
void
2018-02-22 22:28:34 +01:00
console_execute(const std::vector<std::string> &lines)
2017-03-21 03:24:41 +01:00
{
2017-11-25 23:31:58 +01:00
check_console_active();
ircd::context
{
2018-02-22 22:28:34 +01:00
"execute",
stack_sz,
2018-02-22 22:28:34 +01:00
std::bind(&execute, lines),
ircd::context::DETACH | ircd::context::POST
};
2017-03-21 03:24:41 +01:00
}
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();
}
2017-03-21 03:24:41 +01:00
const char *const console_message
{R"(
***
*** The server is still running in the background. A command line is now available below.
*** This is a client and your commands will originate from the server itself.
***)"};
void
console()
try
{
ircd::runlevel_changed::dock.wait([]
{
return ircd::runlevel == ircd::runlevel::RUN ||
ircd::runlevel == ircd::runlevel::HALT;
});
if(ircd::runlevel == ircd::runlevel::HALT)
return;
2017-03-21 03:24:41 +01:00
const unwind atexit([]
2017-03-21 03:24:41 +01:00
{
2018-02-22 22:28:34 +01:00
console_fini();
2017-03-21 03:24:41 +01:00
});
console_init();
std::cout << console_message << generic_message;
2017-08-23 23:11:40 +02:00
while(1)
2017-03-21 03:24:41 +01:00
{
std::cout << "\n> " << std::flush;
char buf[1024];
string_view line;
2017-03-21 03:24:41 +01:00
// 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);
2017-03-21 03:24:41 +01:00
}
2017-08-23 23:11:40 +02:00
if(line.empty())
continue;
if(!handle_line(line))
break;
2017-03-21 03:24:41 +01:00
}
std::cout << std::endl;
2017-03-21 03:24:41 +01:00
}
catch(const std::exception &e)
{
std::cout << std::endl;
2017-03-21 03:24:41 +01:00
std::cout << "***" << std::endl;
std::cout << "*** The console session has ended: " << e.what() << std::endl;
std::cout << "***" << std::endl;
2017-03-21 03:24:41 +01:00
ircd::log::debug("The console session has ended: %s", e.what());
}
2018-02-22 22:28:34 +01:00
const char *const termstop_message
{R"(
***
*** The server has been paused and will resume when you hit enter.
*** This is a client and your commands will originate from the server itself.
***)"};
void
console_termstop()
try
{
const unwind atexit([]
{
console_fini();
});
2018-02-22 22:28:34 +01:00
console_init();
2018-02-22 22:28:34 +01:00
std::cout << termstop_message << generic_message;
std::string line;
std::cout << "\n> " << std::flush;
std::getline(std::cin, line);
if(std::cin.eof())
{
std::cout << std::endl;
std::cin.clear();
return;
}
handle_line(line);
}
catch(const std::exception &e)
{
ircd::log::error("console_termstop(): %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());
}
2017-08-23 23:11:40 +02:00
bool
2018-02-22 22:28:34 +01:00
handle_line(const string_view &line)
2017-03-21 03:24:41 +01:00
try
{
// _theirs is copied for recursive reentrance
const unwind outwork{[console_inwork_theirs(console_inwork)]
{
console_inwork = console_inwork_theirs;
}};
console_inwork = true;
2017-03-21 03:24:41 +01:00
if(line == "ABORT")
abort();
if(line == "EXIT")
exit(0);
2017-08-23 23:11:40 +02:00
if(line == "exit" || line == "die")
{
ircd::quit();
2017-08-23 23:11:40 +02:00
return false;
}
int ret{-1};
if(console_module) switch((ret = handle_line_bymodule(line)))
2017-03-21 03:24:41 +01:00
{
default: break;
case 0: return false;
case 1: return true;
2018-02-22 22:28:34 +01:00
}
2017-09-25 03:05:42 +02:00
2018-02-22 22:28:34 +01:00
throw bad_command{};
}
catch(const std::out_of_range &e)
{
std::cerr << "missing required arguments. " << std::endl;
return true;
}
catch(const bad_command &e)
{
std::cerr << "Bad command or file name: " << e.what() << std::endl;
return true;
}
catch(const ircd::http::error &e)
{
ircd::log::error("%s %s", e.what(), e.content);
return true;
}
catch(const std::exception &e)
{
ircd::log::error("%s", e.what());
return true;
}
2017-10-17 09:49:33 +02:00
int
handle_line_bymodule(const string_view &line)
{
using prototype = int (std::ostream &, const string_view &, const string_view &);
const ircd::mods::import<prototype> command
{
*console_module, "console_command"
};
thread_local char buf[32_KiB];
std::ostringstream ss;
pubsetbuf(ss, buf);
int ret;
static const string_view opts;
switch((ret = command(ss, line, opts)))
{
case 0:
case 1:
{
const string_view out
{
view(ss, buf)
};
std::cout << out;
if(endswith(out, '\n'))
std::flush(std::cout);
else
std::cout << std::endl;
return ret;
}
// The command was handled but the arguments were bad_command.
// The module has its own class for a bad_command exception which
// is a local and separate symbol from the bad_command here so
// we use this code to translate it.
case -2: throw bad_command
{
"%s", view(ss, buf)
};
// Command isn't handled by the module; continue handling here
default:
break;
}
return ret;
}
2018-02-22 22:28:34 +01:00
void
check_console_active()
{
if(console_active)
throw ircd::error("Console is already active and cannot be reentered");
}
2017-10-17 09:49:33 +02:00
2018-02-22 22:28:34 +01:00
void
console_hangup()
try
{
using log::console_quiet;
2017-10-17 09:49:33 +02:00
2018-02-22 22:28:34 +01:00
console_cancel();
2017-10-17 09:49:33 +02:00
2018-02-22 22:28:34 +01:00
static console_quiet *quieted;
if(!quieted)
{
log::notice("Suppressing console log output after terminal hangup");
quieted = new console_quiet;
return;
}
2017-09-25 03:05:42 +02:00
2018-02-22 22:28:34 +01:00
log::notice("Reactivating console logging after second hangup");
delete quieted;
quieted = nullptr;
}
catch(const std::exception &e)
{
ircd::log::error("console_hangup(): %s", e.what());
}
2017-10-17 09:49:33 +02:00
2018-02-22 22:28:34 +01:00
void
console_cancel()
try
{
if(!console_active)
return;
2017-09-25 03:05:42 +02:00
if(console_ctx)
2018-02-22 22:28:34 +01:00
interrupt(*console_ctx);
}
catch(const std::exception &e)
{
ircd::log::error("Interrupting console: %s", e.what());
}