416 lines
17 KiB
C++
416 lines
17 KiB
C++
#include <client/linux/handler/exception_handler.h>
|
|
#include <iostream>
|
|
#include <misc/endianness.h>
|
|
#include <misc/strobf.h>
|
|
#include <CXXTerminal/QuickTerminal.h>
|
|
#include <event2/thread.h>
|
|
#include <log/LogUtils.h>
|
|
#include <ThreadPool/Timer.h>
|
|
|
|
#include "src/Configuration.h"
|
|
#include "src/TSServer.h"
|
|
#include "src/InstanceHandler.h"
|
|
#include "src/server/QueryServer.h"
|
|
#include "src/server/file/FileServer.h"
|
|
#include "src/terminal/CommandHandler.h"
|
|
#include "src/client/InternalClient.h"
|
|
#include "src/music/MusicBotManager.h"
|
|
#include "src/SignalHandler.h"
|
|
#include "src/build.h"
|
|
|
|
using namespace std;
|
|
using namespace std::chrono;
|
|
|
|
#define BUILD_CREATE_TABLE(tblName, types) "CREATE TABLE IF NOT EXISTS `" tblName "` (" types ")"
|
|
|
|
#define CREATE_TABLE(table, types) \
|
|
result = sql::command(sqlData, BUILD_CREATE_TABLE(table, types)).execute();\
|
|
if(!result){\
|
|
logger::logger(0)->critical("Could not setup sql tables. Command '{}' returns {}", BUILD_CREATE_TABLE(table, types), result.fmtStr());\
|
|
goto stopApp;\
|
|
}
|
|
|
|
bool mainThreadActive = true;
|
|
bool mainThreadDone = false;
|
|
|
|
|
|
ts::server::InstanceHandler* serverInstance = nullptr;
|
|
|
|
extern void testTomMath();
|
|
|
|
#ifndef FUCK_CLION
|
|
#define DB_NAME_BEG "TeaData"
|
|
#define DB_NAME_END ".sqlite"
|
|
#define DB_NAME DB_NAME_BEG DB_NAME_END
|
|
#else
|
|
#define DB_NAME "TeaData.sqlite"
|
|
#endif
|
|
|
|
#include <regex>
|
|
#include <codecvt>
|
|
#include "src/client/music/internal_provider/channel_replay/ChannelProvider.h"
|
|
|
|
class CLIParser{
|
|
public:
|
|
CLIParser (int &argc, char **argv){
|
|
for (int i = 1; i < argc; i++)
|
|
this->tokens.emplace_back(argv[i]);
|
|
}
|
|
|
|
std::deque<std::string> getCmdOptions(const std::string &option) const {
|
|
std::deque<std::string> result;
|
|
|
|
auto itr = this->tokens.begin();
|
|
while(true) {
|
|
itr = std::find(itr, this->tokens.end(), option);
|
|
if (itr != this->tokens.end() && ++itr != this->tokens.end()){
|
|
result.push_back(*itr);
|
|
itr++;
|
|
} else break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::deque<std::string> getCmdOptionsBegins(const std::string &option) const {
|
|
std::deque<std::string> result;
|
|
|
|
for(const auto& token : this->tokens)
|
|
if(token.find(option) == 0)
|
|
result.push_back(token);
|
|
|
|
return result;
|
|
}
|
|
|
|
const std::string& get_option(const std::string &option) const {
|
|
auto itr = std::find(this->tokens.begin(), this->tokens.end(), option);
|
|
if (itr != this->tokens.end() && ++itr != this->tokens.end()){
|
|
return *itr;
|
|
}
|
|
static const std::string empty_string;
|
|
return empty_string;
|
|
}
|
|
|
|
bool cmdOptionExists(const std::string &option) const{
|
|
return std::find(this->tokens.begin(), this->tokens.end(), option) != this->tokens.end();
|
|
}
|
|
private:
|
|
std::vector <std::string> tokens;
|
|
};
|
|
|
|
/* addr is where the exception identifier is stored
|
|
id is the exception identifier. */
|
|
void __raise_exception (void **addr, void *id);
|
|
|
|
#define T(address) \
|
|
std::cout << "Testing: " << address << " => "; \
|
|
{\
|
|
sockaddr_storage storage;\
|
|
net::resolve_address(address, storage);\
|
|
std::cout << manager.contains(storage) << std::endl;\
|
|
}
|
|
|
|
#define CONFIG_NAME "config.yml"
|
|
const char *malloc_conf = ""; //retain:false"; //,dirty_decay_ms:0";
|
|
int main(int argc, char** argv) {
|
|
#ifdef HAVE_JEMALLOC
|
|
(void*) malloc_conf;
|
|
#endif
|
|
|
|
CLIParser arguments(argc, argv);
|
|
SSL_load_error_strings();
|
|
OpenSSL_add_ssl_algorithms();
|
|
ts::permission::setup_permission_resolve();
|
|
|
|
{
|
|
auto evthread_use_pthreads_result = evthread_use_pthreads();
|
|
assert(evthread_use_pthreads_result == 0);
|
|
(void) evthread_use_pthreads_result;
|
|
}
|
|
terminal::install();
|
|
if(!terminal::active()){ cerr << "could not setup terminal!" << endl; return -1; }
|
|
assert(ts::property::impl::validateUnique());
|
|
|
|
if(arguments.cmdOptionExists("--help") || arguments.cmdOptionExists("-h")) {
|
|
#define HELP_FMT " {} {} | {}"
|
|
logMessageFmt(true, LOG_GENERAL, "Available command line parameters:");
|
|
logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-h", "--help", "Shows this page");
|
|
logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-q", "--set_query_password", "Changed the server admin query password");
|
|
logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-P<property>=<value>", "--property:<property>=<value>", "Override a config value manual");
|
|
logMessageFmt(true, LOG_GENERAL, HELP_FMT, "-l", "--property-list", "List all available properties");
|
|
terminal::uninstall();
|
|
return 0;
|
|
}
|
|
if(arguments.cmdOptionExists("--property-list") || arguments.cmdOptionExists("-l")) {
|
|
logMessageFmt(true, LOG_GENERAL, "Available properties:");
|
|
auto properties = ts::config::create_bindings();
|
|
for(const auto& property : properties) {
|
|
logMessageFmt(true, LOG_GENERAL, " " + property->key);
|
|
for(const auto& entry : property->description) {
|
|
if(entry.first.empty()) {
|
|
for(const auto& line : entry.second)
|
|
logMessageFmt(true, LOG_GENERAL, " " + line);
|
|
} else {
|
|
logMessageFmt(true, LOG_GENERAL, " " + entry.first + ":");
|
|
for(const auto& line : entry.second)
|
|
logMessageFmt(true, LOG_GENERAL, " " + line);
|
|
}
|
|
}
|
|
logMessageFmt(true, LOG_GENERAL, " " + property->value_description());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if(!arguments.cmdOptionExists("--valgrind")) {
|
|
ts::syssignal::setup();
|
|
}
|
|
ts::syssignal::setup_threads();
|
|
|
|
map<string, string> override_settings;
|
|
{
|
|
auto short_override = arguments.getCmdOptionsBegins("-P");
|
|
for(const auto& entry : short_override) {
|
|
if(entry.length() < 2) continue;
|
|
auto ei = entry.find('=');
|
|
if(ei == string::npos || ei == 2) {
|
|
logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")");
|
|
return 1;
|
|
}
|
|
|
|
auto key = entry.substr(2, ei - 2);
|
|
auto value = entry.substr(ei + 1);
|
|
override_settings[key] = value;
|
|
}
|
|
}
|
|
{
|
|
auto short_override = arguments.getCmdOptionsBegins("--property:");
|
|
for(const auto& entry : short_override) {
|
|
if(entry.length() < 11) continue;
|
|
auto ei = entry.find('=');
|
|
if(ei == string::npos || ei == 11) {
|
|
logErrorFmt(true, LOG_GENERAL, "Invalid command line parameter. (\"" + entry + "\")");
|
|
return 1;
|
|
}
|
|
|
|
auto key = entry.substr(11, ei - 11);
|
|
auto value = entry.substr(ei + 1);
|
|
override_settings[key] = value;
|
|
}
|
|
}
|
|
|
|
{
|
|
auto bindings = ts::config::create_bindings();
|
|
for(const auto& setting : bindings) {
|
|
for(auto it = override_settings.begin(); it != override_settings.end(); it++) {
|
|
if(it->first == setting->key) {
|
|
try {
|
|
setting->read_argument(it->second);
|
|
} catch(const std::exception& ex) {
|
|
logErrorFmt(true, LOG_GENERAL, "Failed to apply value for given property '" + it->first + "': " + ex.what());
|
|
}
|
|
override_settings.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for(const auto& entry : override_settings) {
|
|
logMessageFmt(true, LOG_GENERAL, "Missing property " + entry.first + ". Value unused!");
|
|
}
|
|
}
|
|
/*
|
|
std::string error;
|
|
if(!interaction::waitForAttach(error)){
|
|
cerr << "Rsult: " << error << endl;
|
|
}
|
|
|
|
while(interaction::memoryInfo()){
|
|
usleep(1 * 1000 * 1000);
|
|
logMessage("Current instances: " + to_string(interaction::memoryInfo()->instanceCount) + "/" + to_string(interaction::memoryInfo()->maxInstances));
|
|
}
|
|
|
|
interaction::removeMemoryHook();
|
|
if(true) return 0;
|
|
*/
|
|
|
|
//debugMessage(LOG_GENERAL, "Sizeof ViewEntry {} Sizeof LinkedTreeEntry {} Sizeof shared_ptr<ViewEntry> {} Sizeof ClientChannelView {}", sizeof(ts::ViewEntry), sizeof(ts::TreeView::LinkedTreeEntry), sizeof(shared_ptr<ts::ViewEntry>), sizeof(ts::ClientChannelView));
|
|
{
|
|
//http://git.mcgalaxy.de/WolverinDEV/tomcrypt/blob/develop/src/misc/crypt/crypt_inits.c#L40-86
|
|
std::string descriptors = "LTGE";
|
|
bool crypt_init = false;
|
|
for(const auto& c : descriptors)
|
|
if((crypt_init |= crypt_mp_init(&c)))
|
|
break;
|
|
if(!crypt_init) {
|
|
logCritical(LOG_GENERAL, "Could not initialise libtomcrypt mp descriptors!");
|
|
return 1;
|
|
}
|
|
if(register_prng(&sprng_desc) == -1) {
|
|
logCritical(LOG_GENERAL, "could not setup prng");
|
|
return EXIT_FAILURE;
|
|
}
|
|
if (register_cipher(&rijndael_desc) == -1) {
|
|
logCritical(LOG_GENERAL, "could not setup rijndael");
|
|
return EXIT_FAILURE;
|
|
}
|
|
testTomMath();
|
|
}
|
|
|
|
ts::server::SqlDataManager* sql = nullptr;
|
|
std::string errorMessage;
|
|
shared_ptr<logger::LoggerConfig> logConfig = nullptr;
|
|
std::string line;
|
|
|
|
logMessageFmt(true, LOG_GENERAL, "Loading configuration");
|
|
auto cfgErrors = ts::config::parseConfig(CONFIG_NAME);
|
|
if(!cfgErrors.empty()){
|
|
logErrorFmt(true, LOG_GENERAL, "Could not load configuration. Errors: (" + to_string(cfgErrors.size()) + ")");
|
|
for(const auto& entry : cfgErrors)
|
|
logError(true, LOG_GENERAL, " - {}", entry);
|
|
logErrorFmt(true, LOG_GENERAL, "Stopping server...");
|
|
goto stopApp;
|
|
}
|
|
|
|
logMessage(LOG_GENERAL, "Setting up logging");
|
|
logConfig = make_shared<logger::LoggerConfig>();
|
|
logConfig->logfileLevel = (spdlog::level::level_enum) ts::config::log::logfileLevel;
|
|
logConfig->terminalLevel = (spdlog::level::level_enum) ts::config::log::terminalLevel;
|
|
logConfig->file_colored = ts::config::log::logfileColored;
|
|
logConfig->logPath = ts::config::log::path;
|
|
logConfig->vs_group_size = ts::config::log::vs_size;
|
|
logger::setup(logConfig);
|
|
threads::timer::function_log = [](const std::string& message, bool debug) {
|
|
if(debug)
|
|
debugMessage(LOG_GENERAL, message);
|
|
else
|
|
logWarning(LOG_GENERAL, message);
|
|
};
|
|
|
|
logger::updateLogLevels();
|
|
if(ts::config::license_original && ts::config::license_original->data.type != license::LicenseType::DEMO){
|
|
logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string());
|
|
logMessageFmt(true, LOG_GENERAL, strobf(" §aThank you for buying the TeaSpeak-§lPremium-§aSoftware! ").string());
|
|
logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense information:").string());
|
|
logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense owner : §e").string() + ts::config::license_original->owner());
|
|
logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense type : §e").string() + license::LicenseTypeNames[ts::config::license_original->data.type]);
|
|
|
|
if(ts::config::license_original->end().time_since_epoch().count() == 0){
|
|
logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense expires: §enever").string());
|
|
} else {
|
|
char timeBuffer[32];
|
|
time_t t = duration_cast<seconds>(ts::config::license_original->end().time_since_epoch()).count();
|
|
tm* stime = localtime(&t);
|
|
strftime(timeBuffer, 32, "%c", stime);
|
|
logMessageFmt(true, LOG_GENERAL, strobf(" §aLicense expires: §e").string() + string(timeBuffer));
|
|
}
|
|
logMessageFmt(true, LOG_GENERAL, string() + strobf(" §aLicense valid : ").string() + (ts::config::license_original->isValid() ? strobf("§ayes").string() : strobf("§cno").string()));
|
|
logMessageFmt(true, LOG_GENERAL, strobf("[]---------------------------------------------------------[]").string());
|
|
}
|
|
|
|
logMessage(LOG_GENERAL, "Starting TeaSpeak-Server v{}", build::version()->string(true));
|
|
logMessage(LOG_GENERAL, "Starting music providers");
|
|
|
|
terminal::instance()->setPrompt("§aStarting server. §7[§aloading music§7]");
|
|
if(ts::config::music::enabled && !arguments.cmdOptionExists("--no-providers")) {
|
|
::music::manager::loadProviders("providers");
|
|
::music::manager::register_provider(::music::provider::ChannelProvider::create_provider());
|
|
}
|
|
|
|
terminal::instance()->setPrompt("§aStarting server. §7[§aloading geoloc§7]");
|
|
|
|
if(!ts::config::geo::staticFlag) {
|
|
if(ts::config::geo::type == geoloc::PROVIDER_SOFTWARE77)
|
|
geoloc::provider = new geoloc::Software77Provider(ts::config::geo::mappingFile);
|
|
else if(ts::config::geo::type == geoloc::PROVIDER_IP2LOCATION)
|
|
geoloc::provider = new geoloc::IP2LocationProvider(ts::config::geo::mappingFile);
|
|
else {
|
|
logCritical(LOG_GENERAL,"Invalid geo resolver type!");
|
|
}
|
|
if(geoloc::provider && !geoloc::provider->load(errorMessage)) {
|
|
logCritical(LOG_GENERAL,"Could not setup geoloc! Fallback to default flag!");
|
|
logCritical(LOG_GENERAL,"Message: {}", errorMessage);
|
|
geoloc::provider = nullptr;
|
|
errorMessage = "";
|
|
}
|
|
}
|
|
if(ts::config::geo::vpn_block) {
|
|
geoloc::provider_vpn = new geoloc::IPCatBlocker(ts::config::geo::vpn_file);
|
|
|
|
if(geoloc::provider_vpn && !geoloc::provider_vpn->load(errorMessage)) {
|
|
logCritical(LOG_GENERAL,"Could not setup vpn detector!");
|
|
logCritical(LOG_GENERAL,"Message: {}", errorMessage);
|
|
geoloc::provider_vpn = nullptr;
|
|
errorMessage = "";
|
|
}
|
|
}
|
|
terminal::instance()->setPrompt("§aStarting server. §7[§aloading sql§7]");
|
|
|
|
sql = new ts::server::SqlDataManager();
|
|
if(!sql->initialize(errorMessage)) {
|
|
logCriticalFmt(true, LOG_GENERAL, "Could not initialize SQL!");
|
|
if(errorMessage.find("database is locked") != string::npos) {
|
|
logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------");
|
|
logCriticalFmt(true, LOG_GENERAL, "{:^69}", "You're database is already in use!");
|
|
logCriticalFmt(true, LOG_GENERAL, "{:^69}", "Stop the other instance first!");
|
|
logCriticalFmt(true, LOG_GENERAL, "----------------------------[ ATTENTION ]----------------------------");
|
|
} else {
|
|
logCriticalFmt(true, LOG_GENERAL, errorMessage);
|
|
}
|
|
goto stopApp;
|
|
}
|
|
|
|
terminal::instance()->setPrompt("§aStarting server. §7[§astarting instance§7]");
|
|
|
|
serverInstance = new ts::server::InstanceHandler(sql); //if error than mainThreadActive = false
|
|
if(!mainThreadActive || !serverInstance->startInstance())
|
|
goto stopApp;
|
|
|
|
if(arguments.cmdOptionExists("-q") || arguments.cmdOptionExists("--set_query_password")) {
|
|
auto password = arguments.cmdOptionExists("-q") ? arguments.get_option("-q") : arguments.get_option("--set_query_password");
|
|
if(!password.empty()) {
|
|
logMessageFmt(true, LOG_GENERAL, "Updating server admin query password to \"{}\"", password);
|
|
auto accounts = serverInstance->getQueryServer()->find_query_accounts_by_unique_id(serverInstance->getInitialServerAdmin()->getUid());
|
|
bool found = false;
|
|
for(const auto& account : accounts) {
|
|
if(account->bound_server != 0) continue;
|
|
if(!serverInstance->getQueryServer()->change_query_password(account, password)) {
|
|
logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! (Internal error)");
|
|
}
|
|
found = true;
|
|
break;
|
|
}
|
|
if(!found) {
|
|
logErrorFmt(true, LOG_GENERAL, "Failed to update server admin query password! Login does not exists!");
|
|
}
|
|
}
|
|
}
|
|
|
|
terminal::instance()->setPrompt("§7> §f");
|
|
while(mainThreadActive) {
|
|
usleep(5 * 1000);
|
|
|
|
if(terminal::instance()->linesAvailable() > 0){
|
|
while(!(line = terminal::instance()->readLine("§7> §f")).empty())
|
|
threads::Thread(THREAD_DETACHED, [line](){ terminal::chandler::handleCommand(line); });
|
|
}
|
|
}
|
|
|
|
|
|
stopApp:
|
|
logMessageFmt(true, LOG_GENERAL, "Stopping application");
|
|
::music::manager::finalizeProviders();
|
|
|
|
if(serverInstance)
|
|
serverInstance->stopInstance();
|
|
delete serverInstance;
|
|
serverInstance = nullptr;
|
|
|
|
ts::music::MusicBotManager::shutdown();
|
|
if(sql)
|
|
sql->finalize();
|
|
delete sql;
|
|
logMessageFmt(true, LOG_GENERAL, "Application suspend successful!");
|
|
|
|
logger::uninstall();
|
|
terminal::uninstall();
|
|
mainThreadDone = true;
|
|
return 0;
|
|
} |