Teaspeak-Server/server/src/InstanceHandler.cpp
2019-08-25 23:55:55 +02:00

669 lines
27 KiB
C++

#define XMALLOC undefined_malloc /* fix jemalloc and tomcrypt */
#define XCALLOC undefined_calloc
#define XFREE undefined_free
#define XREALLOC undefined_realloc
#include <netdb.h>
#include "../../license/shared/LicenseRequest.h"
#include "src/weblist/WebListManager.h"
#include <log/LogUtils.h>
#include "InstanceHandler.h"
#include "src/client/InternalClient.h"
#include "src/server/QueryServer.h"
#include "src/server/file/FileServer.h"
#include "SignalHandler.h"
#include "src/manager/PermissionNameMapper.h"
#include <ThreadPool/Timer.h>
#include "ShutdownHelper.h"
#include <sys/utsname.h>
#include "build.h"
#include <misc/digest.h>
#include <misc/base64.h>
#include <misc/rnd.h>
#include <jemalloc/jemalloc.h>
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#include <unistd.h>
#undef _POSIX_SOURCE
#include <stdio.h>
using namespace std;
using namespace std::chrono;
using namespace ts;
using namespace ts::server;
#define INSTANCE_TICK_NAME "instance"
#define _STRINGIFY(x) #x
#define STRINGIFY(x) _STRINGIFY(x)
extern bool mainThreadActive;
extern InstanceHandler* serverInstance;
InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) {
serverInstance = this;
this->tick_manager = make_shared<threads::Scheduler>(config::threads::ticking, "tick task ");
this->statistics = make_shared<stats::ConnectionStatistics>(nullptr, true);
this->statistics->measure_bandwidths(true);
this->licenseHelper = make_shared<license::LicenseHelper>();
this->dbHelper = new DatabaseHelper(this->getSql());
this->_properties = new Properties();
this->_properties->register_property_type<property::InstanceProperties>();
this->properties()[property::SERVERINSTANCE_FILETRANSFER_PORT] = ts::config::binding::DefaultFilePort;
this->properties()[property::SERVERINSTANCE_FILETRANSFER_HOST] = ts::config::binding::DefaultFileHost;
this->properties()[property::SERVERINSTANCE_QUERY_PORT] = ts::config::binding::DefaultQueryPort;
this->properties()[property::SERVERINSTANCE_QUERY_HOST] = ts::config::binding::DefaultQueryHost;
auto result = sql::command(this->getSql(), "SELECT * FROM `properties` WHERE `id` = :id AND `type` = :type AND `serverId` = :serverId", variable{":id", 0}, variable{":serverId", 0}, variable{":type", property::PropertyType::PROP_TYPE_INSTANCE})
.query([](InstanceHandler *instance, int length, char **values, char **columns) {
string key, value;
for (int index = 0; index < length; index++) {
if (strcmp(columns[index], "key") == 0) {
key = values[index];
} else if (strcmp(columns[index], "value") == 0) {
value = values[index] == nullptr ? "" : values[index];
}
}
const auto &info = property::impl::info<property::InstanceProperties>(key);
if(*info == property::SERVERINSTANCE_UNDEFINED) {
logError(0, "Got an unknown instance property " + key);
return 0;
}
auto prop = instance->properties()[info];
prop = value;
prop.setDbReference(true);
prop.setModified(false);
return 0;
}, this);
if (!result) cerr << result << endl;
this->_properties->registerNotifyHandler([&](Property &prop) {
if ((prop.type().flags & property::FLAG_SAVE) == 0) {
prop.setModified(false);
return;
}
string sqlQuery;
if (prop.hasDbReference())
sqlQuery = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `key` = :key";
else {
prop.setDbReference(true);
sqlQuery = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)";
}
sql::command(this->getSql(), sqlQuery, variable{":sid", 0}, variable{":type", property::PropertyType::PROP_TYPE_INSTANCE}, variable{":id", 0}, variable{":key", prop.type().name}, variable{":value", prop.value()})
.executeLater().waitAndGetLater(LOG_SQL_CMD, sql::result{1, "future failed"});
});
this->properties()[property::SERVERINSTANCE_DATABASE_VERSION] = this->sql->getVersion();
globalServerAdmin = std::make_shared<ts::server::InternalClient>(this->getSql(), nullptr, "serveradmin", true);
static_pointer_cast<ts::server::InternalClient>(globalServerAdmin)->setSharedLock(globalServerAdmin);
ts::server::DatabaseHelper::assignDatabaseId(this->getSql(), 0, globalServerAdmin);
this->_musicRoot = std::make_shared<InternalClient>(this->getSql(), nullptr, "Music Manager", false);
static_pointer_cast<InternalClient>(this->_musicRoot)->setSharedLock(this->_musicRoot);
{
this->groupManager = std::make_shared<GroupManager>(nullptr, this->getSql());
this->groupManager->loadGroupFormDatabase();
if (this->groupManager->availableServerGroups(false).empty()) {
if(!this->setupDefaultGroups()){
logCritical(LOG_INSTANCE, "Could not setup server instance! Stopping...");
mainThreadActive = false;
return;
}
}
debugMessage(LOG_INSTANCE, "Instance admin group id " + to_string(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as<GroupId>()));
auto instance_server_admin = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as<GroupId>());
if (!instance_server_admin) {
instance_server_admin = this->groupManager->availableServerGroups(false).front();
logCritical(LOG_INSTANCE, "Missing instance server admin group! Using first available (" + (instance_server_admin ? instance_server_admin->name() : "nil") + ")");
}
if(this->groupManager->listGroupAssignments(this->globalServerAdmin->getClientDatabaseId()).empty())
this->groupManager->addServerGroup(this->globalServerAdmin->getClientDatabaseId(), instance_server_admin);
debugMessage(LOG_INSTANCE, "Server guest group id " + to_string(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as<GroupId>()));
auto instance_server_guest = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save<GroupId>());
if (!instance_server_guest) {
instance_server_guest = this->groupManager->availableServerGroups(false).front();
logCritical(LOG_INSTANCE, "Missing instance server guest group! Using first available (" + (instance_server_guest ? instance_server_guest->name() : "nil") + ")");
}
this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] = instance_server_guest->groupId();
debugMessage(LOG_INSTANCE, "Server music group id " + to_string(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as<GroupId>()));
auto instance_server_music = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save<GroupId>());
if (!instance_server_music) {
instance_server_music = instance_server_guest;
logCritical(LOG_INSTANCE, "Missing instance server music group! Using serverguest (" + (instance_server_music ? instance_server_music->name() : "nil") + ")");
}
this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP] = instance_server_music->groupId();
}
{
this->default_tree = make_shared<ServerChannelTree>(nullptr, this->getSql());
this->default_tree->loadChannelsFromDatabase();
this->default_tree->deleteSemiPermanentChannels();
if(this->default_tree->channel_count() == 0){
logMessage(LOG_GENERAL, "Generating default tree");
std::shared_ptr<BasicChannel> ch;
ch = this->default_tree->createChannel(0, 0, "[cspacer01]┏╋━━━━━━◥◣◆◢◤━━━━━━╋┓");
ch = this->default_tree->createChannel(0, ch->channelId(), "[cspacer02] TeaSpeak Server");
ch = this->default_tree->createChannel(0, ch->channelId(), "[cspacer03]┗╋━━━━━━◥◣◆◢◤━━━━━━╋┛");
ch = this->default_tree->createChannel(0, ch->channelId(), "[cspacer04]Default Channel");
this->default_tree->setDefaultChannel(ch);
this->properties()[property::SERVERINSTANCE_UNIQUE_ID] = ""; /* we def got a new instance */
}
if(!this->default_tree->getDefaultChannel()) this->default_tree->setDefaultChannel(this->default_tree->findChannel("[cspacer04]Default Channel", nullptr));
if(!this->default_tree->getDefaultChannel()) this->default_tree->setDefaultChannel(*this->default_tree->channels().begin());
assert(this->default_tree->getDefaultChannel());
}
{
this->default_server_properties = serverInstance->databaseHelper()->loadServerProperties(nullptr);
}
if(this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP].as<int64_t>() == 0) {
debugMessage(LOG_INSTANCE, "Setting up monthly reset timestamp!");
this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
}
this->banMgr = new BanManager(this->getSql());
this->banMgr->loadBans();
this->web_list = make_shared<weblist::WebListManager>();
}
void InstanceHandler::executeTick(TSServer* server) {
auto str = "server_" + to_string(server->getServerId());
if(!this->tick_manager->schedule(str, std::bind(&TSServer::executeServerTick, server), milliseconds(500))) {
logCritical(LOG_INSTANCE, "Could not schedule server ticking task!");
}
}
void InstanceHandler::cancelExecute(TSServer* server) {
auto str = "server_" + to_string(server->getServerId());
if(!this->tick_manager->cancelTask(str)){
logError(LOG_INSTANCE, "Could not stop server tick task!");
}
}
InstanceHandler::~InstanceHandler() {
delete this->_properties;
delete this->banMgr;
delete this->dbHelper;
groupManager = nullptr;
globalServerAdmin = nullptr;
_musicRoot = nullptr;
licenseHelper = nullptr;
statistics = nullptr;
tick_manager = nullptr;
}
inline sockaddr_in* resolveAddress(const string& host, uint16_t port) {
hostent* record = gethostbyname(host.c_str());
if (!record) {
cerr << "Cant resolve bind host! (" << host << ")" << endl;
return nullptr;
}
auto addr = new sockaddr_in{};
addr->sin_addr.s_addr = ((in_addr *) record->h_addr)->s_addr;
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
return addr;
}
bool InstanceHandler::startInstance() {
if (this->active) return false;
active = true;
string errorMessage;
this->web_list->enabled = ts::config::server::enable_teamspeak_weblist;
this->permission_mapper = make_shared<permission::PermissionNameMapper>();
if(!this->permission_mapper->initialize(config::permission_mapping_file, errorMessage)) {
logCritical(LOG_INSTANCE, "Failed to initialize permission name mapping from file {}: {}", config::permission_mapping_file, errorMessage);
return false;
}
this->sslMgr = new ssl::SSLManager();
if(!this->sslMgr->initialize()) {
logCritical("Failed to initialize ssl manager.");
return false;
}
//Startup file server
sockaddr_in *fAddr = resolveAddress(this->properties()[property::SERVERINSTANCE_FILETRANSFER_HOST].as<string>(), this->properties()[property::SERVERINSTANCE_FILETRANSFER_PORT].as<uint16_t>());
if (!fAddr) {
logCritical(LOG_FT, "Could not resolve file server host");
return false;
}
logMessage(LOG_FT, "Starting server on {}:{}", inet_ntoa(fAddr->sin_addr), ntohs(fAddr->sin_port));
fileServer = new ts::server::FileServer();
if (!fileServer->start(*fAddr)) {
logCritical(LOG_FT, "Failed to start file server.");
delete fAddr;
return false;
}
delete fAddr;
if(config::query::sslMode > 0) {
string error;
auto result = this->sslMgr->initializeContext("query", config::query::ssl::keyFile, config::query::ssl::certFile, error, false, make_shared<ssl::SSLGenerator>(ssl::SSLGenerator{
.subjects = {},
.issues = {{"O", "TeaSpeak"}, {"OU", "Query server"}, {"creator", "WolverinDEV"}}
}));
if(!result) {
logCritical(LOG_QUERY, "Failed to initialize query certificate! (" + error + ")");
return false;
}
}
queryServer = new ts::server::QueryServer(this->getSql());
{
auto server_query = queryServer->find_query_account_by_name("serveradmin");
if(!server_query) {
string queryPassword = rnd_string(12);
if((server_query = queryServer->create_query_account("serveradmin", 0, "serveradmin", queryPassword))) {
logMessageFmt(true, LOG_GENERAL, "------------------ [Server Query] ------------------");
logMessageFmt(true, LOG_GENERAL, " New Admin Server Query login credentials generated");
logMessageFmt(true, LOG_GENERAL, " Username: serveradmin");
logMessageFmt(true, LOG_GENERAL, " Password: " + queryPassword);
logMessageFmt(true, LOG_GENERAL, "------------------ [Server Query] ------------------");
} else {
logCriticalFmt(true,LOG_GENERAL,"Failed to create a new server admin query account!");
}
}
}
sockaddr_in *qAddr = resolveAddress(this->properties()[property::SERVERINSTANCE_QUERY_HOST].as<string>(), this->properties()[property::SERVERINSTANCE_QUERY_PORT].as<uint16_t>());
if (!qAddr) {
logCritical(LOG_QUERY, "Could not resolve query server host");
return false;
}
logMessage(LOG_QUERY, "Starting server on {}:{}", inet_ntoa(qAddr->sin_addr), ntohs(qAddr->sin_port));
if (!queryServer->start(*qAddr, errorMessage)) {
logCritical(LOG_QUERY, "Could not start Query server.\nMessage: " + errorMessage);
delete qAddr;
return false;
}
delete qAddr;
#ifdef COMPILE_WEB_CLIENT
if(config::web::activated) {
string error;
for(auto& certificate : config::web::ssl::certificates) {
auto result = this->sslMgr->initializeContext("web_" + get<0>(certificate), get<1>(certificate), get<2>(certificate), error, false, make_shared<ts::ssl::SSLGenerator>(ts::ssl::SSLGenerator{
.subjects = {},
.issues = {{"O", "TeaSpeak"}, {"OU", "Web server"}, {"creator", "WolverinDEV"}}
}));
if(!result) {
logError(LOG_INSTANCE, "Failed to initialize web certificate for servername {}! (Private key: {}, Certificate: {})", get<0>(certificate), get<1>(certificate), get<2>(certificate));
continue;
}
}
auto rsa = this->sslMgr->initializeSSLKey("teaforo_sign", R"(
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsfsTByPTE0aIqi6pJl4f
Xr4UqsIZkU5wYtktKIFpoDGHCHspCTMXF0fOXJkSGaTBtvTUEraRZz0+zshU+aiy
92qZ9DlC6Px3A94WW6mS48q2wEqZuj2q6Is4vf+DdjiqTzcZsqVJQj6WcqPg24pZ
cC9Yg9mys1IoBEoHmUXYVMFC5ibzRwjxfcAan0qSa+h983pL+4hva/+nHK1kaR2w
feTyUopv10ndkg9jxvAt5+roV3ID2fuHZBsEknWwFTTTjzPsf2Y+B6YYh4CW7haw
vf11A3V+xDFIrSbS9pix1jWgztrQbUcHDczQozArcyflE5+rUMuPPRp3IyRuSq/6
FwIDAQAB
-----END PUBLIC KEY-----
)", error, true);
if(!rsa) { //TODO just disable the forum verification
logCritical("Failed to initialize WebClient TeaForum key! (" + error + ")");
return false;
}
this->web_event_loop = make_shared<webio::LoopManager>();
}
#endif
if(config::experimental_31) {
this->teamspeak_license.reset(new TeamSpeakLicense("protocol_key.txt"));
if(!this->teamspeak_license->load(errorMessage)) {
logCritical(LOG_INSTANCE, "§cFailed to load the protocol key chain! ({})", errorMessage);
return false;
}
}
this->voiceServerManager = new ServerManager(this);
if (!this->voiceServerManager->initialize(true)) {
logCritical(LOG_INSTANCE, "Could not load servers!");
delete this->voiceServerManager;
this->voiceServerManager = nullptr;
return false;
}
if (voiceServerManager->serverInstances().empty()) {
logMessage(LOG_INSTANCE, "§aCreating new TeaSpeak server...");
auto server = voiceServerManager->createServer(config::binding::DefaultVoiceHost, config::voice::default_voice_port);
if (!server)
logCritical(LOG_INSTANCE, "§cCould not create a new server!");
else {
string error;
if (!server->start(error)) {
logCritical(LOG_INSTANCE, "Could not start new server. Message: \n" + error);
}
}
}
startTimestamp = system_clock::now();
this->voiceServerManager->executeAutostart();
this->scheduler()->schedule(INSTANCE_TICK_NAME, bind(&InstanceHandler::tickInstance, this), milliseconds(100));
return true;
}
void InstanceHandler::stopInstance() {
{
lock_guard<mutex> lock(this->activeLock);
if(!this->active) return;
this->active = false;
this->activeCon.notify_all();
}
this->web_list->enabled = false;
threads::MutexLock lock_tick(this->lock_tick);
this->scheduler()->cancelTask(INSTANCE_TICK_NAME);
this->save_channel_permissions();
this->save_group_permissions();
debugMessage(LOG_INSTANCE, "Stopping all virtual servers");
if (this->voiceServerManager)
this->voiceServerManager->shutdownAll(ts::config::messages::applicationStopped);
delete this->voiceServerManager;
this->voiceServerManager = nullptr;
debugMessage(LOG_INSTANCE, "All virtual server stopped");
debugMessage(LOG_QUERY, "Stopping query server");
if (this->queryServer) this->queryServer->stop();
delete this->queryServer;
this->queryServer = nullptr;
debugMessage(LOG_QUERY, "Query server stopped");
debugMessage(LOG_FT, "Stopping file server");
if (this->fileServer) this->fileServer->stop();
delete this->fileServer;
this->fileServer = nullptr;
debugMessage(LOG_FT, "File server stopped");
delete this->sslMgr;
this->sslMgr = nullptr;
this->web_event_loop = nullptr;
}
void InstanceHandler::tickInstance() {
threads::MutexLock lock(this->lock_tick);
if(!this->active) return;
auto now = system_clock::now();
if(generalUpdateTimestamp + seconds(5) < now) {
generalUpdateTimestamp = now;
{
ALARM_TIMER(t, "InstanceHandler::tickInstance -> db helper tick", milliseconds(5));
this->dbHelper->tick();
}
{
ALARM_TIMER(t, "InstanceHandler::tickInstance -> license tick", milliseconds(5));
this->licenseHelper->tick();
}
}
{
ALARM_TIMER(t, "InstanceHandler::tickInstance -> flush", milliseconds(5));
//logger::flush();
}
if(statisticsUpdateTimestamp + seconds(5) < now) {
statisticsUpdateTimestamp = now;
{
ALARM_TIMER(t, "InstanceHandler::tickInstance -> statistics tick", milliseconds(5));
this->statistics->tick();
}
{
ALARM_TIMER(t, "InstanceHandler::tickInstance -> statistics tick [monthly]", milliseconds(2));
auto month_timestamp = system_clock::time_point() + seconds(this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP].as<int64_t>());
auto time_t_old = system_clock::to_time_t(month_timestamp);
auto time_t_new = system_clock::to_time_t(system_clock::now());
tm tm_old{}, tm_new{};
gmtime_r(&time_t_old, &tm_old);
gmtime_r(&time_t_new, &tm_new);
if(tm_old.tm_mon != tm_new.tm_mon) {
logMessage(LOG_INSTANCE, "We entered a new month! Resetting monthly stats!");
if(!this->resetMonthlyStats()) logError(LOG_INSTANCE, "Monthly stats reset failed!");
else {
logMessage(LOG_INSTANCE, "Monthly stats reset done!");
this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP] = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
}
}
}
}
if(memcleanTimestamp + minutes(10) < now) {
memcleanTimestamp = now;
{
{
ALARM_TIMER(t, "InstanceHandler::tickInstance -> mem cleanup -> buffer cleanup", milliseconds(5));
auto info = buffer::cleanup_buffers(buffer::cleanmode::CHUNKS_BLOCKS);
if(info.bytes_freed_buffer != 0 || info.bytes_freed_internal != 0)
logMessage(LOG_INSTANCE, "Cleanupped buffers. (Buffer: {}, Internal: {})", info.bytes_freed_buffer, info.bytes_freed_internal);
}
}
}
{
ALARM_TIMER(t, "InstanceHandler::tickInstance -> fileserver tick", milliseconds(5));
if(this->fileServer) this->fileServer->instanceTick();
}
{
ALARM_TIMER(t, "InstanceHandler::tickInstance -> sql_test tick", milliseconds(5));
if(this->sql && this->active) {
if(sqlTestTimestamp + seconds(10) < now) {
sqlTestTimestamp = now;
threads::Thread(THREAD_SAVE_OPERATIONS | THREAD_DETACHED, [&](){
auto command = this->sql->sql()->getType() == sql::TYPE_SQLITE ? "SELECT * FROM `sqlite_master`" : "SHOW TABLES";
auto result = sql::command(this->getSql(), command).query([command](int, string*, string*){ return 0; });
if(!result) {
logCritical(LOG_INSTANCE, "Dummy sql connection test faild! (Failed to execute command \"{}\". Error message: {})", command, result.fmtStr());
logCritical(LOG_INSTANCE, "Stopping instance!");
ts::server::shutdownInstance("invalid sql connection!");
}
//debugMessage(0, "SQL connection still alive!");
});
}
}
}
if(groupSaveTimestamp + minutes(1) < now) {
speachUpdateTimestamp = now;
this->save_group_permissions();
}
if(channelSaveTimestamp + minutes(1) < now) {
speachUpdateTimestamp = now;
this->save_channel_permissions();
}
if(speachUpdateTimestamp + seconds(5) < now) {
speachUpdateTimestamp = now;
this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE] = this->calculateSpokenTime().count();
this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL] =
this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as<uint64_t>() +
this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as<uint64_t>() +
this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as<uint64_t>();
}
this->web_list->tick();
}
void InstanceHandler::save_group_permissions() {
auto groups = this->getGroupManager()->availableGroups(false);
for(auto& group : groups) {
auto permissions = group->permissions();
if(permissions->require_db_updates()) {
auto begin = system_clock::now();
serverInstance->databaseHelper()->saveGroupPermissions(nullptr, group->groupId(), permissions);
auto end = system_clock::now();
debugMessage(0, "Saved instance group permissions for group {} ({}) in {}ms", group->groupId(), group->name(), duration_cast<milliseconds>(end - begin).count());
}
}
}
void InstanceHandler::save_channel_permissions() {
shared_lock tree_lock(this->getChannelTreeLock());
auto channels = this->getChannelTree()->channels();
tree_lock.unlock();
for(auto& channel : channels) {
auto permissions = channel->permissions();
if(permissions->require_db_updates()) {
auto begin = system_clock::now();
serverInstance->databaseHelper()->saveChannelPermissions(nullptr, channel->channelId(), permissions);
auto end = system_clock::now();
debugMessage(0, "Saved instance channel permissions for channel {} ({}) in {}ms", channel->channelId(), channel->name(), duration_cast<milliseconds>(end - begin).count());
}
}
}
std::chrono::milliseconds InstanceHandler::calculateSpokenTime() {
std::chrono::milliseconds result{};
for(const auto& server : this->voiceServerManager->serverInstances())
result += server->spoken_time;
return result;
}
void InstanceHandler::resetSpeechTime() {
this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED] = 0;
this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ] = 0;
for(const auto& server : this->voiceServerManager->serverInstances())
server->properties()[property::VIRTUALSERVER_SPOKEN_TIME] = 0;
}
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
string get_mac_address() {
struct ifreq ifr;
struct ifconf ifc;
char buf[1024];
int success = 0;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1) { return "undefined"; };
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ }
struct ifreq* it = ifc.ifc_req;
const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));
for (; it != end; ++it) {
strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
success = 1;
break;
}
}
}
else { return "undefined"; }
}
return success ? base64::encode(ifr.ifr_hwaddr.sa_data, 6) : "undefined";
}
#define SN_BUFFER 1024
std::shared_ptr<license::LicenseRequestData> InstanceHandler::generateLicenseData() {
auto response = make_shared<license::LicenseRequestData>();
response->license = config::license;
response->servers_online = this->voiceServerManager->runningServers();
auto report = this->voiceServerManager->clientReport();
response->client_online = report.clients_ts;
response->web_clients_online = report.clients_web;
response->bots_online = report.bots;
response->queries_online = report.queries;
response->speach_total = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL].as<uint64_t>();
response->speach_varianz = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as<uint64_t>();
response->speach_online = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as<uint64_t>();
response->speach_dead = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as<uint64_t>();
{
auto info = make_shared<license::ServerInfo>();
info->timestamp = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
info->version = build::version()->string(true);
{ /* uname */
utsname retval{};
if(uname(&retval) < 0) { // <----
info->uname = "unknown (" + string(strerror(errno)) + ")";
} else {
char buffer[SN_BUFFER];
snprintf(buffer, SN_BUFFER, "sys:%s version:%s release:%s", retval.sysname, retval.version, retval.release);
info->uname = string(buffer);
}
}
{ /* unique id */
auto property_unique_id = this->properties()[property::SERVERINSTANCE_UNIQUE_ID];
if(property_unique_id.as<string>().empty())
property_unique_id = rnd_string(64);
auto hash = digest::sha256(info->uname);
hash = digest::sha256(hash + property_unique_id.as<string>() + get_mac_address());
info->unique_identifier = base64::encode(hash);
}
response->info = info;
}
return response;
}
bool InstanceHandler::resetMonthlyStats() {
//serverId` INTEGER DEFAULT -1, `type` INTEGER, `id` INTEGER, `key` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` TEXT
auto result = sql::command(this->getSql(), "UPDATE `properties` SET `value` = 0 WHERE "
"`key` = 'serverinstance_monthly_timestamp' OR "
"`key` = 'virtualserver_month_bytes_downloaded' OR "
"`key` = 'virtualserver_month_bytes_uploaded' OR "
"`key` = 'client_month_bytes_downloaded' OR "
"`key` = 'client_month_bytes_uploaded' OR "
"`key` = 'client_month_online_time'").execute();
if(!result) {
logError(LOG_INSTANCE, "Failed to reset monthly stats ({})", result.fmtStr());
return false;
}
for(const auto& server : this->getVoiceServerManager()->serverInstances()) {
server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED] = 0;
server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED] = 0;
for(const auto& client : server->getClients()) {
client->properties()[property::CLIENT_MONTH_ONLINE_TIME] = 0;
client->properties()[property::CLIENT_MONTH_BYTES_UPLOADED] = 0;
client->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED] = 0;
}
}
return true;
}