1.4.10 updates

This commit is contained in:
WolverinDEV
2020-02-28 11:24:07 +01:00
parent 2b929fd3c0
commit 0d456eea5d
48 changed files with 2267 additions and 781 deletions
+55 -10
View File
@@ -341,7 +341,7 @@ vector<string> config::parseConfig(const std::string& path) {
}
cfgStream.close();
map<string,deque<string>> comments;
std::map<std::string, std::deque<std::string>> comments;
try {
int config_version;
string teaspeak_license;
@@ -351,9 +351,9 @@ vector<string> config::parseConfig(const std::string& path) {
build_comments(comments, bindings);
}
if(config_version > CURRENT_CONFIG_VERSION) {
errors.push_back("Given config version is higher that currently supported config version!");
errors.emplace_back("Given config version is higher that currently supported config version!");
errors.push_back("Decrease the version by hand to " + to_string(CURRENT_CONFIG_VERSION));
errors.push_back("Attention: Decreasing the version could may lead to data loss!");
errors.emplace_back("Attention: Decreasing the version could may lead to data loss!");
return errors;
}
{
@@ -462,15 +462,13 @@ vector<string> config::parseConfig(const std::string& path) {
}
if(!config::license){
logErrorFmt(true, LOG_GENERAL, strobf("The given license isn't valid!").string());
logErrorFmt(true, LOG_GENERAL, strobf("The given license could not be parsed!").string());
logErrorFmt(true, LOG_GENERAL, strobf("Falling back to the default license.").string());
teaspeak_license = "none";
goto license_parsing;
}
if(!config::license){
errors.emplace_back(strobf("Invalid license code!").string());
return errors;
}
/*
if(!config::license->isValid()) {
if(config::license->data.type == license::LicenseType::INVALID) {
errors.emplace_back(strobf("Give license isn't valid!").string());
@@ -482,6 +480,7 @@ vector<string> config::parseConfig(const std::string& path) {
teaspeak_license = "none";
goto license_parsing;
}
*/
}
{
@@ -548,8 +547,7 @@ vector<string> config::parseConfig(const std::string& path) {
}
std::vector<std::string> config::reload() {
vector<string> errors;
std::vector<std::string> errors;
saveConfig = false;
ifstream cfgStream(_config_path);
@@ -589,6 +587,53 @@ std::vector<std::string> config::reload() {
return errors;
}
bool config::update_license(std::string &error, const std::string &new_license) {
std::vector<std::string> lines{};
{
lines.reserve(1024);
std::ifstream icfg_stream{_config_path};
if(!icfg_stream) {
error = "failed to open config file";
return false;
}
std::string line{};
while(std::getline(icfg_stream, line))
lines.push_back(line);
icfg_stream.close();
}
bool license_found{false};
for(auto& line : lines) {
if(!line.starts_with(" license:")) continue;
line = " license: \"" + new_license + "\"";
license_found = true;
break;
}
if(!license_found) {
error = "missing license config key";
return false;
}
{
std::ofstream ocfg_stream{_config_path};
if(!ocfg_stream) {
error = "failed to write to config file";
return false;
}
for(const auto& line : lines)
ocfg_stream << line << "\n";
ocfg_stream << std::flush;
ocfg_stream.close();
}
return true;
}
void bind_string_description(const shared_ptr<EntryBinding>& _entry, std::string& target, const std::string& default_value) {
_entry->default_value = [default_value]() -> std::deque<std::string> { return { default_value }; };
_entry->value_description = [] { return "The value must be a string"; };
+1
View File
@@ -29,6 +29,7 @@ namespace ts::config {
std::function<void(const std::string&)> read_argument;
};
extern bool update_license(std::string& /* error */, const std::string& /* new license */);
extern std::vector<std::string> parseConfig(const std::string& /* path */);
extern std::vector<std::string> reload();
extern std::deque<std::shared_ptr<EntryBinding>> create_bindings();
+25 -23
View File
@@ -44,7 +44,11 @@ InstanceHandler::InstanceHandler(SqlDataManager *sql) : sql(sql) {
this->statistics = make_shared<stats::ConnectionStatistics>(nullptr, true);
this->statistics->measure_bandwidths(true);
this->licenseHelper = make_shared<license::LicenseHelper>();
std::string error_message{};
this->license_service_ = std::make_shared<license::LicenseService>();
if(!this->license_service_->initialize(error_message)) {
logCritical(LOG_INSTANCE, strobf("Failed to the license service: {}").string(), error_message);
}
this->dbHelper = new DatabaseHelper(this->getSql());
this->_properties = new Properties();
@@ -214,7 +218,6 @@ InstanceHandler::~InstanceHandler() {
globalServerAdmin = nullptr;
_musicRoot = nullptr;
licenseHelper = nullptr;
statistics = nullptr;
tick_manager = nullptr;
}
@@ -453,6 +456,8 @@ void InstanceHandler::stopInstance() {
this->sslMgr = nullptr;
this->web_event_loop = nullptr;
this->license_service_->shutdown();
}
void InstanceHandler::tickInstance() {
@@ -470,7 +475,7 @@ void InstanceHandler::tickInstance() {
}
{
ALARM_TIMER(t, strobf("InstanceHandler::tickInstance -> license tick").string(), milliseconds(5));
this->licenseHelper->tick();
this->license_service_->execute_tick();
}
}
{
@@ -637,36 +642,35 @@ string get_mac_address() {
}
#define SN_BUFFER 1024
std::shared_ptr<license::LicenseRequestData> InstanceHandler::generateLicenseData() {
auto request = make_shared<license::LicenseRequestData>();
std::shared_ptr<ts::server::license::InstanceLicenseInfo> InstanceHandler::generateLicenseData() {
auto request = std::make_shared<license::InstanceLicenseInfo>();
request->license = config::license;
request->servers_online = this->voiceServerManager->runningServers();
request->metrics.servers_online = this->voiceServerManager->runningServers();
auto report = this->voiceServerManager->clientReport();
request->client_online = report.clients_ts;
request->web_clients_online = report.clients_web;
request->bots_online = report.bots;
request->queries_online = report.queries;
request->speach_total = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL].as<uint64_t>();
request->speach_varianz = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as<uint64_t>();
request->speach_online = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as<uint64_t>();
request->speach_dead = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as<uint64_t>();
request->metrics.client_online = report.clients_ts;
request->metrics.web_clients_online = report.clients_web;
request->metrics.bots_online = report.bots;
request->metrics.queries_online = report.queries;
request->metrics.speech_total = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_TOTAL].as<uint64_t>();
request->metrics.speech_varianz = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ].as<uint64_t>();
request->metrics.speech_online = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_ALIVE].as<uint64_t>();
request->metrics.speech_dead = this->properties()[property::SERVERINSTANCE_SPOKEN_TIME_DELETED].as<uint64_t>();
static std::string null_str{"\0\0\0\0\0\0\0\0", 8}; /* we need at least some characters */
request->web_certificate_revision = this->web_cert_revision.empty() ? null_str : this->web_cert_revision;
{
auto info = make_shared<license::ServerInfo>();
info->timestamp = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
info->version = build::version()->string(true);
request->info.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
request->info.version = build::version()->string(true);
{ /* uname */
utsname retval{};
if(uname(&retval) < 0) {
info->uname = "unknown (" + string(strerror(errno)) + ")";
request->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);
request->info.uname = string(buffer);
}
}
@@ -676,12 +680,10 @@ std::shared_ptr<license::LicenseRequestData> InstanceHandler::generateLicenseDat
if(property_unique_id.as<string>().empty())
property_unique_id = rnd_string(64);
auto hash = digest::sha256(info->uname);
auto hash = digest::sha256(request->info.uname);
hash = digest::sha256(hash + property_unique_id.as<string>() + get_mac_address());
info->unique_identifier = base64::encode(hash);
request->info.unique_id = base64::encode(hash);
}
request->info = info;
}
return request;
}
+9 -4
View File
@@ -3,9 +3,8 @@
#include <sql/SqlQuery.h>
#include <Properties.h>
#include "VirtualServerManager.h"
#include "../../license/shared/LicenseRequest.h"
#include "lincense/LicenseHelper.h"
#include <ssl/SSLManager.h>
#include <src/lincense/LicenseService.h>
#include "manager/SqlDataManager.h"
#include "lincense/TeamSpeakLicense.h"
#include "server/WebIoManager.h"
@@ -20,6 +19,10 @@ namespace ts {
}
namespace server {
namespace license {
class LicenseService;
}
class InstanceHandler {
public:
explicit InstanceHandler(SqlDataManager*);
@@ -63,7 +66,7 @@ namespace ts {
std::shared_ptr<stats::ConnectionStatistics> getStatistics(){ return statistics; }
std::shared_ptr<threads::Scheduler> scheduler(){ return this->tick_manager; }
std::shared_ptr<license::LicenseRequestData> generateLicenseData();
std::shared_ptr<license::InstanceLicenseInfo> generateLicenseData();
std::shared_ptr<TeamSpeakLicense> getTeamSpeakLicense() { return this->teamspeak_license; }
std::shared_ptr<ts::Properties> getDefaultServerProperties() { return this->default_server_properties; }
@@ -90,6 +93,8 @@ namespace ts {
bool granted = false,
std::shared_ptr<CalculateCache> cache = nullptr
);
[[nodiscard]] inline std::shared_ptr<license::LicenseService> license_service() { return this->license_service_; }
private:
std::mutex activeLock;
std::condition_variable activeCon;
@@ -126,7 +131,7 @@ namespace ts {
std::shared_ptr<ts::server::InternalClient> globalServerAdmin = nullptr;
std::shared_ptr<ConnectedClient> _musicRoot = nullptr;
std::shared_ptr<license::LicenseHelper> licenseHelper = nullptr;
std::shared_ptr<license::LicenseService> license_service_{nullptr};
std::shared_ptr<stats::ConnectionStatistics> statistics = nullptr;
std::shared_ptr<threads::Scheduler> tick_manager = nullptr;
+71 -17
View File
@@ -39,7 +39,7 @@ bool SpeakingClient::shouldReceiveVoiceWhisper(const std::shared_ptr<ConnectedCl
if(!this->shouldReceiveVoice(sender))
return false;
return permission::v2::permission_granted(this->cpmerission_needed_whisper_power, sender->cpmerission_whisper_power);
return permission::v2::permission_granted(this->cpmerission_needed_whisper_power, sender->cpmerission_whisper_power, false);
}
void SpeakingClient::handlePacketVoice(const pipes::buffer_view& data, bool head, bool fragmented) {
@@ -127,6 +127,15 @@ enum WhisperTarget {
CHANNEL_SUBCHANNELS = 6
};
inline bool update_whisper_error(std::chrono::system_clock::time_point& last) {
auto now = std::chrono::system_clock::now();
if(last + std::chrono::milliseconds{500} < now) {
last = now;
return true;
}
return false;
}
//All clients => type := SERVER_GROUP and target_id := 0
//Server group => type := SERVER_GROUP and target_id := <server group id>
//Channel group => type := CHANNEL_GROUP and target_id := <channel group id>
@@ -239,7 +248,27 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
return target->currentChannel->parent() != current;
}), available_clients.end());
}
if(available_clients.empty()) return;
auto self_lock = this->_this.lock();
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
}), available_clients.end());
if(available_clients.empty()) {
if(update_whisper_error(this->speak_last_no_whisper_target)) {
command_result result{error::whisper_no_targets};
this->notifyError(result);
}
return;
}
if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
command_result result{error::whisper_too_many_targets};
this->notifyError(result);
}
return;
}
//Create the packet data
char packet_buffer[OUT_WHISPER_PKT_OFFSET + data_length];
@@ -253,7 +282,6 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
VoicePacketFlags flags{};
auto data = pipes::buffer_view(packet_buffer, OUT_WHISPER_PKT_OFFSET + data_length);
for(const auto& cl : available_clients){
if(cl->shouldReceiveVoiceWhisper(_this.lock()))
cl->send_voice_whisper_packet(data, flags);
}
@@ -275,6 +303,44 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
for(uint8_t index = 0; index < channelCount; index++)
clientIds[index] = be2le16((char*) data.data_ptr(), offset, &offset);
auto available_clients = this->server->getClients();
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
if(!speakingClient || cl == this || !speakingClient->currentChannel) return true;
auto clientChannelId = cl->currentChannel->channelId();
auto clientId = cl->getClientId();
for(uint8_t index = 0; index < clientCount; index++)
if(channelIds[index] == clientChannelId) return false;
for(uint8_t index = 0; index < channelCount; index++)
if(clientIds[index] == clientId) return false;
return true;
}), available_clients.end());
auto self_lock = this->_this.lock();
available_clients.erase(std::remove_if(available_clients.begin(), available_clients.end(), [&](const std::shared_ptr<ConnectedClient>& cl) {
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
return !speakingClient->shouldReceiveVoiceWhisper(self_lock);
}), available_clients.end());
if(available_clients.empty()) {
if(update_whisper_error(this->speak_last_no_whisper_target)) {
command_result result{error::whisper_no_targets};
this->notifyError(result);
}
return;
}
if(available_clients.size() > this->server->properties()[property::VIRTUALSERVER_MIN_CLIENTS_IN_CHANNEL_BEFORE_FORCED_SILENCE].as_save<size_t>()) {
if(update_whisper_error(this->speak_last_too_many_whisper_targets)) {
command_result result{error::whisper_too_many_targets};
this->notifyError(result);
}
return;
}
size_t dataLength = data.length() - offset;
#ifdef PKT_LOG_WHISPER
logTrace(this->getServerId(), "{} Whisper data length: {}. Client count: {}. Channel count: {}.", CLIENT_STR_LOG_PREFIX, dataLength, clientCount, channelCount);
@@ -291,21 +357,9 @@ void SpeakingClient::handlePacketVoiceWhisper(const pipes::buffer_view& data, bo
VoicePacketFlags flags{};
auto data = pipes::buffer_view(packetBuffer, OUT_WHISPER_PKT_OFFSET + dataLength);
for(const auto& cl : this->server->getClients()){ //Faster?
for(const auto& cl : available_clients){ //Faster?
auto speakingClient = dynamic_pointer_cast<SpeakingClient>(cl);
if(!speakingClient || cl == this) continue;
if(!cl->currentChannel) continue;
auto clientChannelId = cl->currentChannel->channelId();
auto clientId = cl->getClientId();
for(uint8_t index = 0; index < clientCount; index++)
if(channelIds[index] == clientChannelId) goto handleSend;
for(uint8_t index = 0; index < channelCount; index++)
if(clientIds[index] == clientId) goto handleSend;
continue;
handleSend:
assert(speakingClient);
if(speakingClient->shouldReceiveVoiceWhisper(_this.lock()))
speakingClient->send_voice_whisper_packet(data, flags);
}
+3
View File
@@ -81,6 +81,9 @@ namespace ts {
std::chrono::system_clock::time_point speak_begin;
std::chrono::system_clock::time_point speak_last_packet;
std::chrono::system_clock::time_point speak_last_no_whisper_target;
std::chrono::system_clock::time_point speak_last_too_many_whisper_targets;
permission::v2::PermissionFlaggedValue max_idle_time{permission::v2::empty_permission_flagged_value};
struct {
HandshakeState state{HandshakeState::BEGIN};
@@ -1,11 +1,11 @@
#include "SpeakingClient.h"
#include <misc/endianness.h>
#include <src/VirtualServerManager.h>
#include <netinet/tcp.h>
#include <src/InstanceHandler.h>
#include <misc/base64.h>
#include <misc/digest.h>
#include <misc/rnd.h>
#include <log/LogUtils.h>
#include "../VirtualServerManager.h"
#include "../InstanceHandler.h"
#if defined(TCP_CORK) && !defined(TCP_NOPUSH)
#define TCP_NOPUSH TCP_CORK
@@ -1247,6 +1247,8 @@ command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) {
if(conversation)
conversation->set_history_length(cmd[key->name]);
}
} else if(*key == property::CHANNEL_NEEDED_TALK_POWER) {
channel->permissions()->set_permission(permission::i_client_needed_talk_power, {cmd[key->name].as<int>(), 0}, permission::v2::set_value, permission::v2::do_nothing);
}
channel->properties()[key] = cmd[key->name].string();
+9 -4
View File
@@ -1906,7 +1906,7 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
}
string command = "cat \"" + log_path + "\"";
command += " | grep -E ";
command += "\"\\] \\[.*\\]( ){0,6}?" + server_identifier + " \\|\"";
command += R"("\] \[.*\]( ){0,6}?)" + server_identifier + " \\|\"";
size_t beginpos = cmd[0].has("begin_pos") ? cmd["begin_pos"].as<size_t>() : 0ULL; //TODO test it?
size_t file_index = 0;
@@ -1927,7 +1927,7 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
if(beginpos != 0 && file_index + read > beginpos) { //We're done we just want to get the size later
line_buffer += string(buffer.data(), beginpos - file_index);
lines.push_back({file_index, line_buffer});
lines.emplace_back(file_index, line_buffer);
if(lines.size() > max_lines) lines.pop_front();
//debugMessage(LOG_GENERAL, "Final line {}", line_buffer);
line_buffer = "";
@@ -1955,7 +1955,7 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
if(length == 0) length = 1;
//debugMessage(LOG_GENERAL, "Got line {}", line_buffer.substr(0, index));
lines.push_back({file_index + cut_offset, line_buffer.substr(0, index)});
lines.emplace_back(file_index + cut_offset, line_buffer.substr(0, index));
if(lines.size() > max_lines) lines.pop_front();
cut_offset += index + length;
@@ -1968,7 +1968,7 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
}
if(!line_buffer.empty()) {
lines.push_back({file_index - line_buffer.length(), line_buffer});
lines.emplace_back(file_index - line_buffer.length(), line_buffer);
if(lines.size() > max_lines) lines.pop_front();
}
}
@@ -2004,8 +2004,13 @@ command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) {
ts += type + " | | |" + line.substr(line.find('|') + 1);
}
if(ts.length() > 1024)
ts = ts.substr(0, 1024) + "...";
result[index++]["l"] = ts;
} else {
if(line.length() > 1024)
line = line.substr(0, 1024) + "...";
result[index++]["l"] = line;
}
}
+1
View File
@@ -7,6 +7,7 @@
#include <misc/memtracker.h>
#include <misc/base64.h>
#include "src/client/ConnectedClient.h"
#include <netinet/tcp.h>
using namespace std;
using namespace std::chrono;
-200
View File
@@ -1,200 +0,0 @@
#include <log/LogUtils.h>
#include <misc/strobf.h>
#include <misc/hex.h>
#include <src/Configuration.h>
#include <arpa/inet.h>
#include <src/ShutdownHelper.h>
#include "src/InstanceHandler.h"
#include "LicenseHelper.h"
using namespace license;
using namespace std;
using namespace std::chrono;
using namespace ts;
using namespace ts::server;
LicenseHelper::LicenseHelper() {
this->scheduled_request = system_clock::now() + seconds(rand() % 30); //Check in one minute
}
LicenseHelper::~LicenseHelper() {
if(this->request.prepare_thread.joinable())
this->request.prepare_thread.join();
{
unique_lock lock(this->request.current_lock);
if(this->request.current) {
auto request = move(this->request.current);
lock.unlock();
request->abortRequest();
request->callback_update_certificate = nullptr;
}
}
}
inline string format_time(const system_clock::time_point& time) {
std::time_t now = system_clock::to_time_t(time);
std::tm * ptm = std::localtime(&now);
char buffer[128];
const auto length = std::strftime(buffer, 128, "%a, %d.%m.%Y %H:%M:%S", ptm);
return string(buffer, length);
}
void LicenseHelper::tick() {
lock_guard tick_lock(this->license_tick_lock);
bool verbose = config::license->isPremium();
{
lock_guard request_lock(this->request.current_lock);
if(this->request.current) {
auto promise = this->request.current->requestInfo();
if(promise.state() != threads::FutureState::WORKING){
auto exception = this->request.current->exception();
if(promise.state() == threads::FutureState::FAILED) {
this->handle_request_failed(verbose, exception ? exception->what() : strobf("unknown").c_str());
this->request.current = nullptr; /* connection should be already closed */
return;
} else {
auto response = promise.waitAndGet(nullptr);
this->request.current = nullptr; /* connection should be already closed */
if(!response){
this->handle_request_failed(verbose, exception ? exception->what() : strobf("invalid result (null)").c_str());
return;
}
if(!response->license_valid || !response->properties_valid){
if(!response->license_valid) {
if(config::license->isPremium()) logCritical(LOG_INSTANCE, strobf("Could not validate license.").c_str());
else logCritical(LOG_INSTANCE, strobf("Your server has been shutdown remotely!").c_str());
} else if(!response->properties_valid) {
logCritical(LOG_INSTANCE, strobf("Property adjustment failed!").c_str());
} else
logCritical(LOG_INSTANCE, strobf("Your license expired!").c_str());
logCritical(LOG_INSTANCE, strobf("Stopping application!").c_str());
ts::server::shutdownInstance();
return;
} else {
this->scheduled_request = this->last_request + hours(2);
logMessage(LOG_INSTANCE, strobf("License successfully validated! Scheduling next check at {}").c_str(), format_time(this->scheduled_request));
if(response->speach_reset)
serverInstance->resetSpeechTime();
serverInstance->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ] = response->speach_varianz_adjustment;
{
lock_guard lock(this->request.info_lock);
this->request.info = response->license;
}
this->request_fail_count = 0;
this->last_successful_request = system_clock::now();
}
}
}
}
}
if(system_clock::now() > scheduled_request){
this->do_request(verbose);
}
}
void LicenseHelper::do_request(bool verbose) {
if(config::license && config::license->isPremium())
logMessage(LOG_INSTANCE, strobf("Validating license").c_str());
else
logMessage(LOG_INSTANCE, strobf("Validating instance integrity").c_str());
this->last_request = system_clock::now();
this->scheduled_request = this->last_request + minutes(10); /* some kind of timeout */
{
unique_lock lock(this->request.current_lock);
if(this->request.current) {
auto request = move(this->request.current);
lock.unlock();
if(verbose) {
auto promise = request->requestInfo();
if(promise.state() == threads::FutureState::WORKING) {
logMessage(LOG_INSTANCE, strobf("Old check timed out (10 min). Running new one!").c_str());
}
}
request->abortRequest();
}
}
if(this->request.prepare_thread.joinable()) /* usually the preparation should not take too long */
this->request.prepare_thread.join();
this->request.prepare_thread = std::thread([&]{
sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
#ifdef DO_LOCAL_REQUEST
auto license_host = gethostbyname(strobf("localhost").c_str());
server_addr.sin_addr.s_addr = ((in_addr*) license_host->h_addr)->s_addr;
#else
auto license_host = gethostbyname(strobf("license.teaspeak.de").c_str());
if(!license_host){
if(verbose) logError(LOG_INSTANCE, strobf("Could not valid license! (1)").c_str());
return;
}
if(!license_host->h_addr){
if(verbose) logError(LOG_INSTANCE, strobf("Could not valid license! (2)").c_str());
return;
}
server_addr.sin_addr.s_addr = ((in_addr*) license_host->h_addr)->s_addr;
int first = server_addr.sin_addr.s_addr >> 24;
if(first == 0 || first == 127 || first == 255) {
if(config::license->isPremium()) {
logError(LOG_INSTANCE, strobf("You tried to nullroot 'license.teaspeak.de'!").c_str());
logCritical(LOG_INSTANCE, strobf("Could not validate license!").c_str());
logCritical(LOG_INSTANCE, strobf("Stopping server!").c_str());
ts::server::shutdownInstance();
return;
}
return;
}
#endif
server_addr.sin_port = htons(27786);
auto license_data = serverInstance->generateLicenseData();
auto request = make_shared<license::LicenceRequest>(license_data, server_addr);
request->verbose = false;
request->callback_update_certificate = [&](const auto& update) { this->callback_certificate_update(update); };
{
lock_guard lock(this->request.current_lock);
this->request.current = request;
request->requestInfo();
}
});
}
void LicenseHelper::handle_request_failed(bool verbose, const std::string& error) {
if(config::license && config::license->isPremium())
logError(LOG_INSTANCE, strobf("License validation failed: {}").c_str(), error);
else
logError(LOG_INSTANCE, strobf("Instance integrity check failed: {}").c_str(), error);
this->request_fail_count++;
milliseconds next_request;
if(this->request_fail_count <= 1)
next_request = minutes(1);
else if(this->request_fail_count <= 5)
next_request = minutes(5);
else if(this->request_fail_count <= 10)
next_request = minutes(10);
else
next_request = minutes(30);
this->scheduled_request = this->last_request + next_request;
if(verbose)
logMessage(LOG_INSTANCE, strobf("Scheduling next check at {}").c_str(), format_time(this->scheduled_request));
}
void LicenseHelper::callback_certificate_update(const license::WebCertificate &certificate) {
serverInstance->setWebCertRoot(certificate.key, certificate.certificate, certificate.revision);
}
-40
View File
@@ -1,40 +0,0 @@
#pragma once
#include <ThreadPool/Mutex.h>
#include "../../../license/shared/License.h"
#include "../../../license/shared/LicenseRequest.h"
namespace license {
class LicenseHelper {
public:
LicenseHelper();
~LicenseHelper();
void tick();
std::shared_ptr<license::LicenseInfo> getLicenseInfo() {
std::lock_guard lock(this->request.info_lock);
return this->request.info;
}
private:
std::mutex license_tick_lock;
std::chrono::system_clock::time_point scheduled_request;
std::chrono::system_clock::time_point last_request;
std::chrono::system_clock::time_point last_successful_request;
size_t request_fail_count = 0;
struct {
std::shared_ptr<license::LicenceRequest> current = nullptr;
std::mutex current_lock;
std::shared_ptr<license::LicenseInfo> info;
std::mutex info_lock;
std::thread prepare_thread;
} request;
void do_request(bool /* verbose */);
void handle_request_failed(bool /* verbose */, const std::string& /* error */);
void callback_certificate_update(const license::WebCertificate&);
};
}
+540
View File
@@ -0,0 +1,540 @@
//
// Created by WolverinDEV on 27/02/2020.
//
#include <netdb.h>
#include <cassert>
#include <misc/strobf.h>
#include <log/LogUtils.h>
#include <src/Configuration.h>
#include <src/ShutdownHelper.h>
#include <misc/base64.h>
#include "../../../license/shared/LicenseServerClient.h"
#include "../../../cmake-build-debug-wsl/license/LicenseRequest.pb.h"
#include "src/InstanceHandler.h"
#include "../../../license/shared/License.h"
#define DO_LOCAL_REQUEST
using namespace ts::server::license;
LicenseService::LicenseService() {
this->dns.lock = std::make_shared<std::recursive_mutex>();
}
LicenseService::~LicenseService() {
{
std::lock_guard lock{this->request_lock};
this->abort_request(lock, "");
}
}
bool LicenseService::initialize(std::string &error) {
//this->verbose_ = true;
this->startup_timepoint_ = std::chrono::steady_clock::now();
this->timings.next_request = std::chrono::system_clock::now() + std::chrono::seconds(rand() % 20);
return true;
}
bool LicenseService::execute_request_sync(const std::chrono::milliseconds& timeout) {
std::unique_lock slock{this->sync_request_lock};
this->begin_request();
if(this->sync_request_cv.wait_for(slock, timeout) == std::cv_status::timeout)
return false;
return this->timings.failed_count == 0;
}
void LicenseService::shutdown() {
std::lock_guard lock{this->request_lock};
if(this->request_state_ == request_state::empty) return;
this->abort_request(lock, "shutdown");
}
void LicenseService::begin_request() {
std::lock_guard lock{this->request_lock};
if(this->request_state_ != request_state::empty)
this->abort_request(lock, "last request has been aborted");
if(this->verbose_)
debugMessage(LOG_INSTANCE, strobf("Executing license request.").string());
this->timings.last_request = std::chrono::system_clock::now();
this->request_state_ = request_state::dns_lookup;
this->execute_dns_request();
}
void LicenseService::abort_request(std::lock_guard<std::recursive_timed_mutex> &, const std::string& reason) {
if(this->request_state_ == request_state::dns_lookup) {
this->abort_dns_request();
return;
} else if(this->current_client) {
this->current_client->callback_connected = nullptr;
this->current_client->callback_message = nullptr;
this->current_client->callback_disconnected = nullptr;
if(!reason.empty()) {
this->current_client->disconnect(reason, std::chrono::system_clock::now() + std::chrono::seconds{1});
/* Lets not wait here because we might be within the event loop. */
//if(!this->current_client->await_disconnect())
// this->current_client->close_connection();
} else {
this->current_client->close_connection();
}
this->current_client.release();
}
}
void LicenseService::abort_dns_request() {
std::unique_lock llock{*this->dns.lock};
if(!this->dns.current_lookup) return;
this->dns.current_lookup->handle = nullptr;
this->dns.current_lookup = nullptr;
}
void LicenseService::execute_dns_request() {
std::unique_lock llock{*this->dns.lock};
assert(!this->dns.current_lookup);
auto lookup = new _dns::_lookup{};
lookup->lock = this->dns.lock;
lookup->handle = this;
lookup->thread = std::thread([lookup] {
bool success{false};
std::string error{};
sockaddr_in server_addr{};
{
server_addr.sin_family = AF_INET;
#ifdef DO_LOCAL_REQUEST
auto license_host = gethostbyname(strobf("localhost").c_str());
#else
auto license_host = gethostbyname(strobf("license.teaspeak.de").c_str());
#endif
if(!license_host) {
error = strobf("result is null").string();
goto handle_result;
}
if(!license_host->h_addr){
error = strobf("missing h_addr in result").string();
goto handle_result;
}
server_addr.sin_addr.s_addr = ((in_addr*) license_host->h_addr)->s_addr;
#ifndef DO_LOCAL_REQUEST
int first = server_addr.sin_addr.s_addr >> 24;
if(first == 0 || first == 127 || first == 255) {
error = strobf("local response address").string();
goto handle_result;
}
#endif
server_addr.sin_port = htons(27786);
success = true;
}
handle_result:
{
std::unique_lock llock{*lookup->lock};
if(lookup->handle) {
lookup->handle->dns.current_lookup = nullptr;
if(success) {
debugMessage(LOG_INSTANCE, strobf("Successfully resolved the hostname to {}").string(), net::to_string(server_addr.sin_addr));
lookup->handle->handle_dns_lookup_result(true, server_addr);
} else {
debugMessage(LOG_INSTANCE, strobf("Failed to resolve hostname for license server: {}").string(), error);
lookup->handle->handle_dns_lookup_result(false, error);
}
}
assert(lookup->thread.get_id() == std::this_thread::get_id());
if(lookup->thread.joinable())
lookup->thread.detach();
delete lookup;
}
});
this->dns.current_lookup = lookup;
}
void LicenseService::handle_check_succeeded() {
{
std::lock_guard rlock{this->request_lock};
this->abort_request(rlock, strobf("request succeeded").string());
this->schedule_next_request(true);
this->request_state_ = request_state::empty;
if(config::license->isPremium()) {
logMessage(LOG_INSTANCE, strobf("License has been validated.").string());
} else {
logMessage(LOG_INSTANCE, strobf("Instance integrity has been validated.").string());
}
}
{
std::unique_lock slock{this->sync_request_lock};
this->sync_request_cv.notify_all();
}
}
void LicenseService::handle_check_fail(const std::string &error) {
{
std::lock_guard rlock{this->request_lock};
this->abort_request(rlock, "request failed");
if(config::license->isPremium()) {
logCritical(LOG_INSTANCE, strobf("Failed to validate license:").string());
logCritical(LOG_INSTANCE, error);
logCritical(LOG_INSTANCE, strobf("Stopping server!").string());
ts::server::shutdownInstance();
} else {
logError(LOG_INSTANCE, strobf("Failed to validate instance integrity:").string());
logError(LOG_INSTANCE, error);
}
this->schedule_next_request(false);
this->request_state_ = request_state::empty;
}
{
std::unique_lock slock{this->sync_request_lock};
this->sync_request_cv.notify_all();
}
}
void LicenseService::handle_dns_lookup_result(bool success, const std::variant<std::string, sockaddr_in> &result) {
if(!success) {
this->handle_check_fail(std::get<std::string>(result));
return;
}
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::dns_lookup) {
logError(LOG_INSTANCE, strobf("Request state isn't dns lookup anymore. Aborting dns lookup result callback.").string());
return;
}
this->request_state_ = request_state::connecting;
assert(!this->current_client);
this->current_client = std::make_unique<::license::client::LicenseServerClient>(std::get<sockaddr_in>(result), 3);
this->current_client->callback_connected = [&]{ this->handle_client_connected(); };
this->current_client->callback_disconnected = [&](bool expected, const std::string& error) {
this->handle_client_disconnected(error);
};
this->current_client->callback_message = [&](auto a, auto b, auto c) {
this->handle_message(a, b, c);
};
std::string error{};
if(!this->current_client->start_connection(error))
this->handle_check_fail(strobf("connect failed: ").string() + error);
}
void LicenseService::client_send_message(::license::protocol::PacketType type, ::google::protobuf::Message &message) {
auto buffer = message.SerializeAsString();
assert(this->current_client);
this->current_client->send_message(type, buffer.data(), buffer.length());
}
void LicenseService::handle_client_connected() {
{
if(this->verbose_)
debugMessage(LOG_INSTANCE, strobf("License client connected").string());
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::connecting) {
logError(LOG_INSTANCE, strobf("Request state isn't connecting anymore. Aborting client connect callback.").string());
return;
}
this->request_state_ = request_state::license_validate;
}
this->send_license_validate_request();
}
void LicenseService::handle_message(::license::protocol::PacketType type, const void *buffer, size_t size) {
switch (type) {
case ::license::protocol::PACKET_SERVER_VALIDATION_RESPONSE:
this->handle_message_license_info(buffer, size);
return;
case ::license::protocol::PACKET_SERVER_PROPERTY_ADJUSTMENT:
this->handle_message_property_adjustment(buffer, size);
return;
case ::license::protocol::PACKET_SERVER_LICENSE_UPGRADE_RESPONSE:
this->handle_message_license_update(buffer, size);
return;
default:
this->handle_check_fail(strobf("received unknown packet").string());
return;
}
}
void LicenseService::handle_client_disconnected(const std::string& message) {
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::finishing) {
this->handle_check_fail(strobf("unexpected disconnect: ").string() + message);
return;
}
this->abort_request(rlock, "");
}
void LicenseService::send_license_validate_request() {
this->license_request_data = serverInstance->generateLicenseData();
ts::proto::license::ServerValidation request{};
if(this->license_request_data->license) {
request.set_licensed(true);
request.set_license_info(true);
request.set_license(exportLocalLicense(this->license_request_data->license));
} else {
request.set_licensed(false);
request.set_license_info(false);
}
request.mutable_info()->set_uname(this->license_request_data->info.uname);
request.mutable_info()->set_version(this->license_request_data->info.version);
request.mutable_info()->set_timestamp(this->license_request_data->info.timestamp.count());
request.mutable_info()->set_unique_id(this->license_request_data->info.unique_id);
this->client_send_message(::license::protocol::PACKET_CLIENT_SERVER_VALIDATION, request);
}
void LicenseService::handle_message_license_info(const void *buffer, size_t buffer_length) {
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::license_validate) {
this->handle_check_fail(strobf("finvalid request state for license response packet").string());
return;
}
ts::proto::license::LicenseResponse response{};
if(!response.ParseFromArray(buffer, buffer_length)) {
this->handle_check_fail(strobf("failed to parse license response packet").string());
return;
}
if(response.has_blacklist()) {
auto blacklist_state = response.blacklist().state();
if(blacklist_state == ::ts::proto::license::BLACKLISTED) {
this->abort_request(rlock, strobf("blacklist action").string());
logCritical(LOG_INSTANCE, strobf("This TeaSpeak-Server instance has been blacklisted by TeaSpeak.").string());
logCritical(LOG_INSTANCE, strobf("Stopping server!").string());
ts::server::shutdownInstance();
return;
}
}
if(!response.valid()) {
std::string reason{};
if(response.has_invalid_reason())
reason = response.invalid_reason();
else
reason = strobf("no reason given").string();
license_invalid_reason = reason;
} else {
license_invalid_reason.reset();
}
if(response.has_update_pending() && response.update_pending()) {
if(this->send_license_update_request()) {
this->request_state_ = request_state::license_upgrade;
return;
}
}
if(this->license_invalid_reason.has_value()) {
this->handle_check_fail(strobf("Failed to verify license (").string() + *this->license_invalid_reason + ")");
return;
}
this->send_property_update_request();
this->request_state_ = request_state::property_update;
}
void LicenseService::send_property_update_request() {
auto data = this->license_request_data;
if(!data) {
this->handle_check_fail(strobf("missing property data").string());
return;
}
ts::proto::license::PropertyUpdateRequest infos{};
infos.set_speach_total(this->license_request_data->metrics.speech_total);
infos.set_speach_dead(this->license_request_data->metrics.speech_dead);
infos.set_speach_online(this->license_request_data->metrics.speech_online);
infos.set_speach_varianz(this->license_request_data->metrics.speech_varianz);
infos.set_clients_online(this->license_request_data->metrics.client_online);
infos.set_bots_online(this->license_request_data->metrics.bots_online);
infos.set_queries_online(this->license_request_data->metrics.queries_online);
infos.set_servers_online(this->license_request_data->metrics.servers_online);
infos.set_web_clients_online(this->license_request_data->metrics.web_clients_online);
infos.set_web_cert_revision(this->license_request_data->web_certificate_revision);
this->client_send_message(::license::protocol::PACKET_CLIENT_PROPERTY_ADJUSTMENT, infos);
}
void LicenseService::handle_message_property_adjustment(const void *buffer, size_t buffer_length) {
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::property_update) {
this->handle_check_fail(strobf("invalid request state for property update packet").string());
return;
}
ts::proto::license::PropertyUpdateResponse response{};
if(!response.ParseFromArray(buffer, buffer_length)) {
this->handle_check_fail(strobf("failed to parse property update packet").string());
return;
}
if(response.has_web_certificate()) {
auto& certificate = response.web_certificate();
serverInstance->setWebCertRoot(certificate.key(), certificate.certificate(), certificate.revision());
}
if(response.has_reset_speach())
serverInstance->resetSpeechTime();
serverInstance->properties()[property::SERVERINSTANCE_SPOKEN_TIME_VARIANZ] = response.speach_varianz_corrector();
this->request_state_ = request_state::finishing;
this->handle_check_succeeded();
}
bool LicenseService::send_license_update_request() {
ts::proto::license::RequestLicenseUpgrade request{};
this->client_send_message(::license::protocol::PACKET_CLIENT_LICENSE_UPGRADE, request);
return true;
}
inline std::string format_time(const std::chrono::system_clock::time_point& time);
void LicenseService::handle_message_license_update(const void *buffer, size_t buffer_length) {
std::lock_guard rlock{this->request_lock};
if(this->request_state_ != request_state::license_upgrade) {
this->handle_check_fail(strobf("invalid request state for license upgrade packet").string());
return;
}
ts::proto::license::LicenseUpgradeResponse response{};
if(!response.ParseFromArray(buffer, buffer_length)) {
this->handle_check_fail(strobf("failed to parse license upgrade packet").string());
return;
}
if(!response.valid()) {
logError(LOG_INSTANCE, strobf("Failed to upgrade license: {}").string(), response.error_message());
goto error_exit;
} else {
std::string error{};
auto license_data = response.license_key();
auto license = ::license::readLocalLicence(license_data, error);
if(!license) {
logError(LOG_INSTANCE, strobf("Failed to parse received upgraded license key: {}").string(), error);
goto error_exit;
}
if(!license->isValid()) {
logError(LOG_INSTANCE, strobf("Received license seems to be invalid.").string());
goto error_exit;
}
auto end = std::chrono::system_clock::time_point{} + std::chrono::milliseconds{license->data.endTimestamp};
logMessage(LOG_INSTANCE, strobf("Received new license registered to {}, valid until {}").string(), license->data.licenceOwner, format_time(end));
if(!config::update_license(error, license_data))
logError(LOG_INSTANCE, strobf("Failed to write new license key to config file: {}").string(), error);
config::license = license;
this->send_license_validate_request();
this->request_state_ = request_state::license_validate;
}
return;
error_exit:
logError(LOG_INSTANCE, strobf("License upgrade failed. Using old key.").string());
if(this->license_invalid_reason.has_value()) {
this->handle_check_fail(strobf("Failed to verify license (").string() + *this->license_invalid_reason + ")");
return;
}
this->send_property_update_request();
this->request_state_ = request_state::property_update;
}
/* request scheduler */
inline std::string format_time(const std::chrono::system_clock::time_point& time) {
std::time_t now = std::chrono::system_clock::to_time_t(time);
std::tm * ptm = std::localtime(&now);
char buffer[128];
const auto length = std::strftime(buffer, 128, "%a, %d.%m.%Y %H:%M:%S", ptm);
return std::string{buffer, length};
}
void LicenseService::schedule_next_request(bool request_success) {
auto& fail_count = this->timings.failed_count;
if(request_success)
fail_count = 0;
else
fail_count++;
std::chrono::milliseconds next_request;
if(fail_count == 0)
next_request = std::chrono::hours{2};
if(fail_count <= 1)
next_request = std::chrono::minutes(1);
else if(fail_count <= 5)
next_request = std::chrono::minutes(5);
else if(fail_count <= 10)
next_request = std::chrono::minutes(10);
else
next_request = std::chrono::minutes(30);
#ifdef DO_LOCAL_REQUEST
next_request = std::chrono::seconds(30);
#endif
this->timings.next_request = this->timings.last_request + next_request;
if(this->verbose_)
logMessage(LOG_INSTANCE, strobf("Scheduling next check at {}").c_str(), format_time(this->timings.next_request));
}
void LicenseService::execute_tick() {
std::unique_lock rlock{this->request_lock, std::try_to_lock}; /* It will be slightly blocking when its within the message hendeling */
if(!rlock) return;
/* do it not above because if we might have a deadlock here we don't want to punish the user */
if(this->timings.last_succeeded.time_since_epoch().count() == 0) {
auto difference = config::license->isPremium() ? std::chrono::hours{24 * 4} : std::chrono::hours{24 * 7};
if(std::chrono::steady_clock::now() - difference > this->startup_timepoint_) {
this->startup_timepoint_ = std::chrono::steady_clock::now(); /* shut down only once */
if(config::license->isPremium())
logCritical(LOG_INSTANCE, strobf("Failed to validate license within 4 days.").string());
else
logCritical(LOG_INSTANCE, strobf("Failed to validate instance integrity within 7 days.").string());
logCritical(LOG_INSTANCE, strobf("Stopping server!").string());
ts::server::shutdownInstance();
return;
}
}
auto now = std::chrono::system_clock::now();
if(this->request_state_ != request_state::empty) {
if(this->timings.last_request + std::chrono::minutes{5} < now) {
this->handle_check_fail(strobf("Scheduling next check at {}").string());
} else {
return;
}
}
if(std::chrono::system_clock::now() > this->timings.next_request)
this->begin_request();
}
+134
View File
@@ -0,0 +1,134 @@
#pragma once
#include <variant>
#include <thread>
#include <mutex>
#include <memory>
namespace license::client {
class LicenseServerClient;
}
namespace google::protobuf {
class Message;
}
namespace ts::server::license {
struct InstanceLicenseInfo {
std::shared_ptr<::license::License> license{nullptr};
std::string web_certificate_revision{};
struct metrics_ {
size_t servers_online{0};
size_t client_online{0};
size_t web_clients_online{0};
size_t bots_online{0};
size_t queries_online{0};
size_t speech_total{0};
size_t speech_varianz{0};
size_t speech_online{0};
size_t speech_dead{0};
} metrics;
struct info_ {
std::chrono::milliseconds timestamp{};
std::string version{};
std::string uname{};
std::string unique_id{};
} info;
};
class LicenseService {
public:
LicenseService();
~LicenseService();
[[nodiscard]] bool initialize(std::string& /* error */);
void shutdown();
/* whatever it failed/succeeded */
bool execute_request_sync(const std::chrono::milliseconds& /* timeout */);
[[nodiscard]] inline bool verbose() const { return this->verbose_; }
void execute_tick(); /* should not be essential to the core functionality! */
private:
std::chrono::steady_clock::time_point startup_timepoint_;
enum struct request_state {
empty,
/* initializing */
dns_lookup,
connecting,
/* connected states */
license_validate,
license_upgrade,
property_update,
/* disconnecting */
finishing
};
bool verbose_{false};
std::recursive_timed_mutex request_lock{};
request_state request_state_{request_state::empty};
std::unique_ptr<::license::client::LicenseServerClient> current_client{nullptr};
std::shared_ptr<InstanceLicenseInfo> license_request_data{nullptr};
std::condition_variable sync_request_cv;
std::mutex sync_request_lock;
struct _timings {
std::chrono::system_clock::time_point last_request{};
std::chrono::system_clock::time_point next_request{};
std::chrono::system_clock::time_point last_succeeded{};
size_t failed_count{0};
} timings;
struct _dns {
std::shared_ptr<std::recursive_mutex> lock{nullptr};
struct _lookup {
std::shared_ptr<std::recursive_mutex> lock{nullptr};
std::thread thread{};
LicenseService* handle{nullptr}; /* may be null, locked via lock */
}* current_lookup{nullptr};
} dns;
std::optional<std::string> license_invalid_reason{}; /* set if the last license is invalid */
void schedule_next_request(bool /* last request succeeded */);
void begin_request();
void client_send_message(::license::protocol::PacketType /* type */, ::google::protobuf::Message& /* message */);
void handle_check_fail(const std::string& /* error */); /* might be called form the DNS loop */
void handle_check_succeeded();
/* if not disconnect message has been set it will just close the connection */
void abort_request(std::lock_guard<std::recursive_timed_mutex>& /* request lock */, const std::string& /* disconnect message */);
void abort_dns_request();
void execute_dns_request();
/* will be called while dns lock has been locked! */
void handle_dns_lookup_result(bool /* success */, const std::variant<std::string, sockaddr_in>& /* data */);
/* all callbacks bellow are called from the current_client. It will not be null while being within the callback. */
void handle_client_connected();
void handle_client_disconnected(const std::string& /* error */);
void handle_message(::license::protocol::PacketType /* type */, const void* /* buffer */, size_t /* length */);
void handle_message_license_info(const void* /* buffer */, size_t /* length */);
void handle_message_license_update(const void* /* buffer */, size_t /* length */);
void handle_message_property_adjustment(const void* /* buffer */, size_t /* length */);
void send_license_validate_request();
bool send_license_update_request();
void send_property_update_request();
};
}
+11 -8
View File
@@ -1,17 +1,20 @@
#include "./CommandHandler.h"
#include <csignal>
#include <src/SignalHandler.h>
#include <src/VirtualServer.h>
#include <src/client/ConnectedClient.h>
#include <src/VirtualServerManager.h>
#include <src/InstanceHandler.h>
#include <log/LogUtils.h>
#include <src/ShutdownHelper.h>
#include <misc/time.h>
#include <misc/memtracker.h>
#include <sql/sqlite/SqliteSQL.h>
#include <sys/resource.h>
#include "CommandHandler.h"
#include "src/server/QueryServer.h"
#include <protocol/buffers.h>
#include "../SignalHandler.h"
#include "../client/ConnectedClient.h"
#include "../InstanceHandler.h"
#include "../VirtualServerManager.h"
#include "../VirtualServer.h"
#include "../ShutdownHelper.h"
#include "../server/QueryServer.h"
#ifdef HAVE_JEMALLOC
#include <jemalloc/jemalloc.h>