Servatrice: refactor signal handling and permit config reloading

This commit is contained in:
Fabio Bas 2015-06-30 22:21:26 +02:00
parent 9947af7be9
commit 6cf3db7e6b
12 changed files with 163 additions and 95 deletions

View file

@ -58,10 +58,13 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool
connect(updateServerMessageButton, SIGNAL(clicked()), this, SLOT(actUpdateServerMessage())); connect(updateServerMessageButton, SIGNAL(clicked()), this, SLOT(actUpdateServerMessage()));
shutdownServerButton = new QPushButton; shutdownServerButton = new QPushButton;
connect(shutdownServerButton, SIGNAL(clicked()), this, SLOT(actShutdownServer())); connect(shutdownServerButton, SIGNAL(clicked()), this, SLOT(actShutdownServer()));
reloadConfigButton = new QPushButton;
connect(reloadConfigButton, SIGNAL(clicked()), this, SLOT(actReloadConfig()));
QVBoxLayout *vbox = new QVBoxLayout; QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(updateServerMessageButton); vbox->addWidget(updateServerMessageButton);
vbox->addWidget(shutdownServerButton); vbox->addWidget(shutdownServerButton);
vbox->addWidget(reloadConfigButton);
vbox->addStretch(); vbox->addStretch();
adminGroupBox = new QGroupBox; adminGroupBox = new QGroupBox;
@ -87,6 +90,7 @@ void TabAdmin::retranslateUi()
{ {
updateServerMessageButton->setText(tr("Update server &message")); updateServerMessageButton->setText(tr("Update server &message"));
shutdownServerButton->setText(tr("&Shut down server")); shutdownServerButton->setText(tr("&Shut down server"));
reloadConfigButton->setText(tr("&Reload configuration"));
adminGroupBox->setTitle(tr("Server administration functions")); adminGroupBox->setTitle(tr("Server administration functions"));
unlockButton->setText(tr("&Unlock functions")); unlockButton->setText(tr("&Unlock functions"));
@ -110,6 +114,12 @@ void TabAdmin::actShutdownServer()
} }
} }
void TabAdmin::actReloadConfig()
{
Command_ReloadConfig cmd;
client->sendCommand(client->prepareAdminCommand(cmd));
}
void TabAdmin::actUnlock() void TabAdmin::actUnlock()
{ {
if (QMessageBox::question(this, tr("Unlock administration functions"), tr("Do you really want to unlock the administration functions?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { if (QMessageBox::question(this, tr("Unlock administration functions"), tr("Do you really want to unlock the administration functions?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {

View file

@ -28,7 +28,7 @@ private:
bool locked; bool locked;
AbstractClient *client; AbstractClient *client;
bool fullAdmin; bool fullAdmin;
QPushButton *updateServerMessageButton, *shutdownServerButton; QPushButton *updateServerMessageButton, *shutdownServerButton, *reloadConfigButton;
QGroupBox *adminGroupBox; QGroupBox *adminGroupBox;
QPushButton *unlockButton, *lockButton; QPushButton *unlockButton, *lockButton;
signals: signals:
@ -36,6 +36,7 @@ signals:
private slots: private slots:
void actUpdateServerMessage(); void actUpdateServerMessage();
void actShutdownServer(); void actShutdownServer();
void actReloadConfig();
void actUnlock(); void actUnlock();
void actLock(); void actLock();

View file

@ -2,6 +2,7 @@ message AdminCommand {
enum AdminCommandType { enum AdminCommandType {
UPDATE_SERVER_MESSAGE = 1000; UPDATE_SERVER_MESSAGE = 1000;
SHUTDOWN_SERVER = 1001; SHUTDOWN_SERVER = 1001;
RELOAD_CONFIG = 1002;
} }
extensions 100 to max; extensions 100 to max;
} }
@ -19,3 +20,9 @@ message Command_ShutdownServer {
optional string reason = 1; optional string reason = 1;
optional uint32 minutes = 2; optional uint32 minutes = 2;
} }
message Command_ReloadConfig {
extend AdminCommand {
optional Command_ReloadConfig ext = 1002;
}
}

View file

@ -14,6 +14,7 @@ SET(servatrice_SOURCES
src/serversocketinterface.cpp src/serversocketinterface.cpp
src/settingscache.cpp src/settingscache.cpp
src/isl_interface.cpp src/isl_interface.cpp
src/signalhandler.cpp
${VERSION_STRING_CPP} ${VERSION_STRING_CPP}
src/smtp/emailaddress.cpp src/smtp/emailaddress.cpp
src/smtp/mimeattachment.cpp src/smtp/mimeattachment.cpp

View file

@ -28,21 +28,16 @@
#include "servatrice.h" #include "servatrice.h"
#include "server_logger.h" #include "server_logger.h"
#include "settingscache.h" #include "settingscache.h"
#include "signalhandler.h"
#include "rng_sfmt.h" #include "rng_sfmt.h"
#include "version_string.h" #include "version_string.h"
#include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/common.h>
#ifdef Q_OS_UNIX
#include <signal.h>
#include <execinfo.h>
#include <unistd.h>
#endif
#define SIGSEGV_TRACE_LINES 40
RNG_Abstract *rng; RNG_Abstract *rng;
ServerLogger *logger; ServerLogger *logger;
QThread *loggerThread; QThread *loggerThread;
SettingsCache *settingsCache; SettingsCache *settingsCache;
SignalHandler *signalhandler;
/* Prototypes */ /* Prototypes */
@ -55,9 +50,6 @@ void myMessageOutput2(QtMsgType type, const char *msg);
void myMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg); void myMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg);
void myMessageOutput2(QtMsgType type, const QMessageLogContext &, const QString &msg); void myMessageOutput2(QtMsgType type, const QMessageLogContext &, const QString &msg);
#endif #endif
#ifdef Q_OS_UNIX
void sigSegvHandler(int sig);
#endif
/* Implementations */ /* Implementations */
@ -130,32 +122,6 @@ void myMessageOutput2(QtMsgType /*type*/, const QMessageLogContext &, const QStr
} }
#endif #endif
#ifdef Q_OS_UNIX
void sigSegvHandler(int sig)
{
void *array[SIGSEGV_TRACE_LINES];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, SIGSEGV_TRACE_LINES);
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
if (sig == SIGSEGV)
logger->logMessage("CRASH: SIGSEGV");
else if (sig == SIGABRT)
logger->logMessage("CRASH: SIGABRT");
logger->deleteLater();
loggerThread->wait();
delete loggerThread;
raise(sig);
}
#endif
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
@ -202,23 +168,8 @@ int main(int argc, char *argv[])
qInstallMessageHandler(myMessageOutput2); qInstallMessageHandler(myMessageOutput2);
#endif #endif
#ifdef Q_OS_UNIX signalhandler = new SignalHandler();
struct sigaction hup;
hup.sa_handler = ServerLogger::hupSignalHandler;
sigemptyset(&hup.sa_mask);
hup.sa_flags = 0;
hup.sa_flags |= SA_RESTART;
sigaction(SIGHUP, &hup, 0);
struct sigaction segv;
segv.sa_handler = sigSegvHandler;
segv.sa_flags = SA_RESETHAND;
sigemptyset(&segv.sa_mask);
sigaction(SIGSEGV, &segv, 0);
sigaction(SIGABRT, &segv, 0);
signal(SIGPIPE, SIG_IGN);
#endif
rng = new RNG_SFMT; rng = new RNG_SFMT;
std::cerr << "Servatrice " << VERSION_STRING << " starting." << std::endl; std::cerr << "Servatrice " << VERSION_STRING << " starting." << std::endl;
@ -250,6 +201,7 @@ int main(int argc, char *argv[])
} }
delete rng; delete rng;
delete signalhandler;
delete settingsCache; delete settingsCache;
logger->deleteLater(); logger->deleteLater();

View file

@ -2,6 +2,11 @@
#define MAIN_H #define MAIN_H
class ServerLogger; class ServerLogger;
class QThread;
class SettingsCache;
extern ServerLogger *logger; extern ServerLogger *logger;
extern QThread *loggerThread;
extern SettingsCache *settingsCache;
#endif #endif

View file

@ -1,17 +1,11 @@
#include "server_logger.h" #include "server_logger.h"
#include "settingscache.h" #include "settingscache.h"
#include <QSocketNotifier>
#include <QFile> #include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QDir> #include <QDir>
#include <QTextStream> #include <QTextStream>
#include <QDateTime> #include <QDateTime>
#include <iostream> #include <iostream>
#ifdef Q_OS_UNIX
# include <sys/types.h>
# include <sys/socket.h>
# include <unistd.h>
#endif
ServerLogger::ServerLogger(bool _logToConsole, QObject *parent) ServerLogger::ServerLogger(bool _logToConsole, QObject *parent)
: QObject(parent), logToConsole(_logToConsole), flushRunning(false) : QObject(parent), logToConsole(_logToConsole), flushRunning(false)
@ -44,13 +38,6 @@ void ServerLogger::startLog(const QString &logFileName)
logFile = 0; logFile = 0;
return; return;
} }
#ifdef Q_OS_UNIX
::socketpair(AF_UNIX, SOCK_STREAM, 0, sigHupFD);
snHup = new QSocketNotifier(sigHupFD[1], QSocketNotifier::Read, this);
connect(snHup, SIGNAL(activated(int)), this, SLOT(handleSigHup()));
#endif
} else } else
logFile = 0; logFile = 0;
@ -119,35 +106,13 @@ void ServerLogger::flushBuffer()
} }
} }
void ServerLogger::hupSignalHandler(int /*unused*/) void ServerLogger::rotateLogs()
{ {
#ifdef Q_OS_UNIX
if (!logFile) if (!logFile)
return; return;
char a = 1;
ssize_t writeValue = ::write(sigHupFD[0], &a, sizeof(a));
Q_UNUSED(writeValue);
#endif
}
void ServerLogger::handleSigHup()
{
#ifdef Q_OS_UNIX
if (!logFile)
return;
snHup->setEnabled(false);
char tmp;
ssize_t readValue = ::read(sigHupFD[1], &tmp, sizeof(tmp));
Q_UNUSED(readValue);
logFile->close(); logFile->close();
logFile->open(QIODevice::Append); logFile->open(QIODevice::Append);
snHup->setEnabled(true);
#endif
} }
QFile *ServerLogger::logFile; QFile *ServerLogger::logFile;
int ServerLogger::sigHupFD[2];

View file

@ -7,7 +7,6 @@
#include <QWaitCondition> #include <QWaitCondition>
#include <QStringList> #include <QStringList>
class QSocketNotifier;
class QFile; class QFile;
class Server_ProtocolHandler; class Server_ProtocolHandler;
@ -16,19 +15,16 @@ class ServerLogger : public QObject {
public: public:
ServerLogger(bool _logToConsole, QObject *parent = 0); ServerLogger(bool _logToConsole, QObject *parent = 0);
~ServerLogger(); ~ServerLogger();
static void hupSignalHandler(int unused);
public slots: public slots:
void startLog(const QString &logFileName); void startLog(const QString &logFileName);
void logMessage(QString message, void *caller = 0); void logMessage(QString message, void *caller = 0);
void rotateLogs();
private slots: private slots:
void handleSigHup();
void flushBuffer(); void flushBuffer();
signals: signals:
void sigFlushBuffer(); void sigFlushBuffer();
private: private:
bool logToConsole; bool logToConsole;
static int sigHupFD[2];
QSocketNotifier *snHup;
static QFile *logFile; static QFile *logFile;
bool flushRunning; bool flushRunning;
QStringList buffer; QStringList buffer;

View file

@ -289,6 +289,7 @@ Response::ResponseCode ServerSocketInterface::processExtendedAdminCommand(int cm
switch ((AdminCommand::AdminCommandType) cmdType) { switch ((AdminCommand::AdminCommandType) cmdType) {
case AdminCommand::SHUTDOWN_SERVER: return cmdShutdownServer(cmd.GetExtension(Command_ShutdownServer::ext), rc); case AdminCommand::SHUTDOWN_SERVER: return cmdShutdownServer(cmd.GetExtension(Command_ShutdownServer::ext), rc);
case AdminCommand::UPDATE_SERVER_MESSAGE: return cmdUpdateServerMessage(cmd.GetExtension(Command_UpdateServerMessage::ext), rc); case AdminCommand::UPDATE_SERVER_MESSAGE: return cmdUpdateServerMessage(cmd.GetExtension(Command_UpdateServerMessage::ext), rc);
case AdminCommand::RELOAD_CONFIG: return cmdReloadConfig(cmd.GetExtension(Command_ReloadConfig::ext), rc);
default: return Response::RespFunctionNotAllowed; default: return Response::RespFunctionNotAllowed;
} }
} }
@ -956,3 +957,9 @@ Response::ResponseCode ServerSocketInterface::cmdShutdownServer(const Command_Sh
QMetaObject::invokeMethod(server, "scheduleShutdown", Q_ARG(QString, QString::fromStdString(cmd.reason())), Q_ARG(int, cmd.minutes())); QMetaObject::invokeMethod(server, "scheduleShutdown", Q_ARG(QString, QString::fromStdString(cmd.reason())), Q_ARG(int, cmd.minutes()));
return Response::RespOk; return Response::RespOk;
} }
Response::ResponseCode ServerSocketInterface::cmdReloadConfig(const Command_ReloadConfig &cmd, ResponseContainer & /*rc*/)
{
settingsCache->sync();
return Response::RespOk;
}

View file

@ -47,6 +47,7 @@ class Command_ReplayDeleteMatch;
class Command_BanFromServer; class Command_BanFromServer;
class Command_UpdateServerMessage; class Command_UpdateServerMessage;
class Command_ShutdownServer; class Command_ShutdownServer;
class Command_ReloadConfig;
class ServerSocketInterface : public Server_ProtocolHandler class ServerSocketInterface : public Server_ProtocolHandler
{ {
@ -94,6 +95,7 @@ private:
Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc);
Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc);
Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */); Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */);
Response::ResponseCode cmdReloadConfig(const Command_ReloadConfig &cmd, ResponseContainer & /*rc*/);
Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc); Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc);
Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc); Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc);

View file

@ -0,0 +1,98 @@
#include <QSocketNotifier>
#include "signalhandler.h"
#include "server_logger.h"
#include "settingscache.h"
#include "main.h"
#ifdef Q_OS_UNIX
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <signal.h>
#include <execinfo.h>
#include <iostream>
#include <cstdio>
#endif
#define SIGSEGV_TRACE_LINES 40
int SignalHandler::sigHupFD[2];
SignalHandler::SignalHandler(QObject *parent)
: QObject(parent)
{
#ifdef Q_OS_UNIX
::socketpair(AF_UNIX, SOCK_STREAM, 0, sigHupFD);
snHup = new QSocketNotifier(sigHupFD[1], QSocketNotifier::Read, this);
connect(snHup, SIGNAL(activated(int)), this, SLOT(internalSigHupHandler()));
struct sigaction hup;
hup.sa_handler = SignalHandler::sigHupHandler;
sigemptyset(&hup.sa_mask);
hup.sa_flags = 0;
hup.sa_flags |= SA_RESTART;
sigaction(SIGHUP, &hup, 0);
struct sigaction segv;
segv.sa_handler = SignalHandler::sigSegvHandler;
segv.sa_flags = SA_RESETHAND;
sigemptyset(&segv.sa_mask);
sigaction(SIGSEGV, &segv, 0);
sigaction(SIGABRT, &segv, 0);
signal(SIGPIPE, SIG_IGN);
#endif
}
void SignalHandler::sigHupHandler(int /* sig */)
{
char a = 1;
ssize_t writeValue = ::write(sigHupFD[0], &a, sizeof(a));
Q_UNUSED(writeValue);
}
void SignalHandler::internalSigHupHandler()
{
snHup->setEnabled(false);
char tmp;
ssize_t readValue = ::read(sigHupFD[1], &tmp, sizeof(tmp));
Q_UNUSED(readValue);
#ifdef Q_OS_UNIX
std::cerr << "Received SIGHUP" << std::endl;
#endif
logger->logMessage("Received SIGHUP");
logger->rotateLogs();
settingsCache->sync();
snHup->setEnabled(true);
}
void SignalHandler::sigSegvHandler(int sig)
{
#ifdef Q_OS_UNIX
void *array[SIGSEGV_TRACE_LINES];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, SIGSEGV_TRACE_LINES);
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
#endif
if (sig == SIGSEGV)
logger->logMessage("CRASH: SIGSEGV");
else if (sig == SIGABRT)
logger->logMessage("CRASH: SIGABRT");
logger->deleteLater();
loggerThread->wait();
delete loggerThread;
raise(sig);
}

View file

@ -0,0 +1,24 @@
#ifndef SIGNALHANDLER_H
#define SIGNALHANDLER_H
#include <QObject>
class QSocketNotifier;
class SignalHandler: public QObject
{
Q_OBJECT
public:
SignalHandler(QObject *parent = 0);
~SignalHandler() { };
static void sigHupHandler(int /* sig */);
static void sigSegvHandler(int sig);
private:
static int sigHupFD[2];
QSocketNotifier *snHup;
private slots:
void internalSigHupHandler();
};
#endif