#define XMALLOC undefined_malloc /* fix jemalloc and tomcrypt */ #define XCALLOC undefined_calloc #define XFREE undefined_free #define XREALLOC undefined_realloc #include #include "../../license/shared/LicenseRequest.h" #include "src/weblist/WebListManager.h" #include #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 #include "ShutdownHelper.h" #include #include "build.h" #include #include #include #include #include #ifndef _POSIX_SOURCE #define _POSIX_SOURCE #endif #include #undef _POSIX_SOURCE #include 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(config::threads::ticking, "tick task "); this->statistics = make_shared(nullptr, true); this->statistics->measure_bandwidths(true); this->licenseHelper = make_shared(); this->dbHelper = new DatabaseHelper(this->getSql()); this->_properties = new Properties(); this->_properties->register_property_type(); 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(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->get_database_version(); this->properties()[property::SERVERINSTANCE_PERMISSIONS_VERSION] = this->sql->get_permissions_version(); globalServerAdmin = std::make_shared(this->getSql(), nullptr, "serveradmin", true); static_pointer_cast(globalServerAdmin)->setSharedLock(globalServerAdmin); ts::server::DatabaseHelper::assignDatabaseId(this->getSql(), 0, globalServerAdmin); this->_musicRoot = std::make_shared(this->getSql(), nullptr, "Music Manager", false); static_pointer_cast(this->_musicRoot)->setSharedLock(this->_musicRoot); { this->groupManager = std::make_shared(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())); auto instance_server_admin = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_ADMIN_SERVERQUERY_GROUP].as()); 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())); auto instance_server_guest = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP].as_save()); 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())); auto instance_server_music = this->groupManager->findGroup(this->properties()[property::SERVERINSTANCE_TEMPLATE_MUSICDEFAULT_GROUP].as_save()); 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(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 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() == 0) { debugMessage(LOG_INSTANCE, "Setting up monthly reset timestamp!"); this->properties()[property::SERVERINSTANCE_MONTHLY_TIMESTAMP] = duration_cast(system_clock::now().time_since_epoch()).count(); } this->banMgr = new BanManager(this->getSql()); this->banMgr->loadBans(); this->web_list = make_shared(); } 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 string strip(std::string message) { while(!message.empty()) { if(message[0] == ' ') message = message.substr(1); else if(message[message.length() - 1] == ' ') message = message.substr(0, message.length() - 1); else break; } return message; } 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; } inline vector split_hosts(const std::string& message, char delimiter) { vector result; size_t found, index = 0; do { found = message.find(delimiter, index); result.push_back(strip(message.substr(index, found - index))); index = found + 1; } while(index != 0); return result; } 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(); 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; } this->conversation_io = make_shared("conv io #"); if(!this->conversation_io->initialize(1)) { //TODO: Make the conversation IO loop thread size configurable logCritical(LOG_GENERAL, "Failed to initialize conversation io write loop"); return false; } fileServer = new ts::server::FileServer(); { auto bindings_string = this->properties()[property::SERVERINSTANCE_FILETRANSFER_HOST].as(); auto port = this->properties()[property::SERVERINSTANCE_FILETRANSFER_PORT].as(); auto ft_bindings = net::resolve_bindings(bindings_string, port); deque> bindings; for(auto& binding : ft_bindings) { if(!get<2>(binding).empty()) { logError(LOG_FT, "Failed to resolve binding for {}: {}", get<0>(binding), get<2>(binding)); continue; } auto entry = make_shared(); memcpy(&entry->address, &get<1>(binding), sizeof(sockaddr_storage)); entry->file_descriptor = -1; entry->event_accept = nullptr; bindings.push_back(entry); } logMessage(LOG_FT, "Starting server on {}:{}", bindings_string, port); if(!fileServer->start(bindings, errorMessage)) { logCritical(LOG_FT, "Failed to start server: {}", errorMessage); return false; } } 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{ .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!"); } } } { auto query_bindings_string = this->properties()[property::SERVERINSTANCE_QUERY_HOST].as(); auto query_port = this->properties()[property::SERVERINSTANCE_QUERY_PORT].as(); auto query_bindings = net::resolve_bindings(query_bindings_string, query_port); deque> bindings; for(auto& binding : query_bindings) { if(!get<2>(binding).empty()) { logError(LOG_QUERY, "Failed to resolve binding for {}: {}", get<0>(binding), get<2>(binding)); continue; } auto entry = make_shared(); memcpy(&entry->address, &get<1>(binding), sizeof(sockaddr_storage)); entry->file_descriptor = -1; entry->event_accept = nullptr; bindings.push_back(entry); } logMessage(LOG_QUERY, "Starting server on {}:{}", query_bindings_string, query_port); if(!queryServer->start(bindings, errorMessage)) { logCritical(LOG_QUERY, "Failed to start query server: {}", errorMessage); return false; } } #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{ .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(); } #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 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, strobf("InstanceHandler::tickInstance -> license tick").string(), 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()); 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(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() + this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as() + this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as(); } 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(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(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 #include #include #include #include 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 InstanceHandler::generateLicenseData() { auto response = make_shared(); 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(); response->speach_varianz = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as(); response->speach_online = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as(); response->speach_dead = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as(); { auto info = make_shared(); info->timestamp = duration_cast(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().empty()) property_unique_id = rnd_string(64); auto hash = digest::sha256(info->uname); hash = digest::sha256(hash + property_unique_id.as() + 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; }