Teaspeak-Server/server/src/ShutdownHelper.cpp

130 lines
4.2 KiB
C++
Raw Normal View History

//
// Created by wolverindev on 01.04.18.
//
#include <log/LogUtils.h>
#include <StringVariable.h>
#include "ShutdownHelper.h"
#include "InstanceHandler.h"
using namespace std;
using namespace std::chrono;
using namespace ts;
using namespace ts::server;
extern bool mainThreadActive;
extern ts::server::InstanceHandler* serverInstance;
bool shuttingDown = false;
void ts::server::shutdownInstance(const std::string& message) {
if(shuttingDown) return;
shuttingDown = true;
threads::Thread(THREAD_EXECUTE_LATER, [](){
threads::self::sleep_for(chrono::seconds(30));
logCritical("Could not shutdown server within 30 seconds! (Hangup!)");
logCritical("Killing server!");
threads::Thread(THREAD_EXECUTE_LATER, [](){
threads::self::sleep_for(chrono::seconds(5));
logCritical("Failed to exit normally!");
logCritical("executing raise(SIGKILL);");
raise(SIGKILL);
}).name("Stop exit controller").execute().detach();
exit(2);
}).name("Stop controller").execute().detach();
logMessage("Stopping all server instances!");
if(serverInstance && serverInstance->getVoiceServerManager())
serverInstance->getVoiceServerManager()->shutdownAll(message);
mainThreadActive = false;
}
std::shared_ptr<server::ShutdownData> currentShutdown = nullptr;
std::shared_ptr<server::ShutdownData> server::scheduledShutdown() { return currentShutdown; }
inline void broadcastMessage(const std::string& message) {
if(!serverInstance || !serverInstance->getVoiceServerManager());
for(const auto &server : serverInstance->getVoiceServerManager()->serverInstances()) {
if(server->running()) {
server->broadcastMessage(server->getServerRoot(), message);
}
}
}
void executeScheduledShutdown(const std::shared_ptr<ShutdownData>& data);
bool server::scheduleShutdown(const std::chrono::system_clock::time_point& time, const std::string& reason) {
server::cancelShutdown(false); //Cancel old shutdown
auto data = std::make_shared<ShutdownData>();
data->active = true;
data->time_point = time;
data->reason = reason;
data->shutdownThread = new threads::Thread(THREAD_EXECUTE_LATER | THREAD_SAVE_OPERATIONS, [data](){ executeScheduledShutdown(data); });
data->shutdownThread->name("Shutdown Executor").execute();
currentShutdown = data;
return true;
}
void server::cancelShutdown(bool notify) {
if(!currentShutdown) return;
if(notify && !config::messages::shutdown::canceled.empty()) {
broadcastMessage(config::messages::shutdown::canceled);
}
auto current = server::scheduledShutdown();
current->active = false;
current->shutdownNotify.notify_all();
if(current->shutdownThread->join(seconds(3)) != 0) {
logCritical("Could not terminal shutdown thread!");
}
delete current->shutdownThread;
current->shutdownThread = nullptr;
currentShutdown = nullptr;
}
void executeScheduledShutdown(const shared_ptr<ShutdownData>& data) {
std::time_t time_point = system_clock::to_time_t(data->time_point);
{
auto message = strvar::transform(config::messages::shutdown::scheduled, strvar::FunctionValue("time", (strvar::FunctionValue::FValueFNEasy) [&](std::deque<std::string> value) {
auto pattern = !value.empty() ? value[0] : "%Y-%m-%d_%H:%M:%S";
tm* tm_info = localtime(&time_point);
char timeBuffer[1024];
if(strftime(timeBuffer, 1024, pattern.c_str(), tm_info) == 0) {
return string("string is longer than the buffer");
}
return string(timeBuffer);
}));
broadcastMessage(message);
}
while(data->time_point > system_clock::now()) {
auto per = data->time_point - system_clock::now();
pair<seconds, string> best_period = {seconds(0), ""};
for(const auto& period : config::messages::shutdown::intervals) {
if(period.first > per) continue;
if(period.first > best_period.first) best_period = period;
}
{
std::unique_lock<std::mutex> lock(data->shutdownMutex);
data->shutdownNotify.wait_until(lock, data->time_point - best_period.first, [data](){ return !data->active; });
if(!data->active) return;
}
if(best_period.first.count() == 0)
broadcastMessage(config::messages::shutdown::now);
else
broadcastMessage(strvar::transform(config::messages::shutdown::interval, strvar::StringValue{"interval", best_period.second}));
}
ts::server::shutdownInstance(data->reason);
//No need to delete own task
}