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()));
shutdownServerButton = new QPushButton;
connect(shutdownServerButton, SIGNAL(clicked()), this, SLOT(actShutdownServer()));
reloadConfigButton = new QPushButton;
connect(reloadConfigButton, SIGNAL(clicked()), this, SLOT(actReloadConfig()));
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(updateServerMessageButton);
vbox->addWidget(shutdownServerButton);
vbox->addWidget(reloadConfigButton);
vbox->addStretch();
adminGroupBox = new QGroupBox;
@ -87,6 +90,7 @@ void TabAdmin::retranslateUi()
{
updateServerMessageButton->setText(tr("Update server &message"));
shutdownServerButton->setText(tr("&Shut down server"));
reloadConfigButton->setText(tr("&Reload configuration"));
adminGroupBox->setTitle(tr("Server administration 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()
{
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;
AbstractClient *client;
bool fullAdmin;
QPushButton *updateServerMessageButton, *shutdownServerButton;
QPushButton *updateServerMessageButton, *shutdownServerButton, *reloadConfigButton;
QGroupBox *adminGroupBox;
QPushButton *unlockButton, *lockButton;
signals:
@ -36,6 +36,7 @@ signals:
private slots:
void actUpdateServerMessage();
void actShutdownServer();
void actReloadConfig();
void actUnlock();
void actLock();

View file

@ -2,6 +2,7 @@ message AdminCommand {
enum AdminCommandType {
UPDATE_SERVER_MESSAGE = 1000;
SHUTDOWN_SERVER = 1001;
RELOAD_CONFIG = 1002;
}
extensions 100 to max;
}
@ -19,3 +20,9 @@ message Command_ShutdownServer {
optional string reason = 1;
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/settingscache.cpp
src/isl_interface.cpp
src/signalhandler.cpp
${VERSION_STRING_CPP}
src/smtp/emailaddress.cpp
src/smtp/mimeattachment.cpp

View file

@ -28,21 +28,16 @@
#include "servatrice.h"
#include "server_logger.h"
#include "settingscache.h"
#include "signalhandler.h"
#include "rng_sfmt.h"
#include "version_string.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;
ServerLogger *logger;
QThread *loggerThread;
SettingsCache *settingsCache;
SignalHandler *signalhandler;
/* Prototypes */
@ -55,9 +50,6 @@ void myMessageOutput2(QtMsgType type, const char *msg);
void myMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg);
void myMessageOutput2(QtMsgType type, const QMessageLogContext &, const QString &msg);
#endif
#ifdef Q_OS_UNIX
void sigSegvHandler(int sig);
#endif
/* Implementations */
@ -130,32 +122,6 @@ void myMessageOutput2(QtMsgType /*type*/, const QMessageLogContext &, const QStr
}
#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[])
{
QCoreApplication app(argc, argv);
@ -202,23 +168,8 @@ int main(int argc, char *argv[])
qInstallMessageHandler(myMessageOutput2);
#endif
#ifdef Q_OS_UNIX
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
signalhandler = new SignalHandler();
rng = new RNG_SFMT;
std::cerr << "Servatrice " << VERSION_STRING << " starting." << std::endl;
@ -250,6 +201,7 @@ int main(int argc, char *argv[])
}
delete rng;
delete signalhandler;
delete settingsCache;
logger->deleteLater();

View file

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

View file

@ -1,17 +1,11 @@
#include "server_logger.h"
#include "settingscache.h"
#include <QSocketNotifier>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QTextStream>
#include <QDateTime>
#include <iostream>
#ifdef Q_OS_UNIX
# include <sys/types.h>
# include <sys/socket.h>
# include <unistd.h>
#endif
ServerLogger::ServerLogger(bool _logToConsole, QObject *parent)
: QObject(parent), logToConsole(_logToConsole), flushRunning(false)
@ -44,13 +38,6 @@ void ServerLogger::startLog(const QString &logFileName)
logFile = 0;
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
logFile = 0;
@ -119,35 +106,13 @@ void ServerLogger::flushBuffer()
}
}
void ServerLogger::hupSignalHandler(int /*unused*/)
void ServerLogger::rotateLogs()
{
#ifdef Q_OS_UNIX
if (!logFile)
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->open(QIODevice::Append);
snHup->setEnabled(true);
#endif
}
QFile *ServerLogger::logFile;
int ServerLogger::sigHupFD[2];

View file

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

View file

@ -289,6 +289,7 @@ Response::ResponseCode ServerSocketInterface::processExtendedAdminCommand(int cm
switch ((AdminCommand::AdminCommandType) cmdType) {
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::RELOAD_CONFIG: return cmdReloadConfig(cmd.GetExtension(Command_ReloadConfig::ext), rc);
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()));
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_UpdateServerMessage;
class Command_ShutdownServer;
class Command_ReloadConfig;
class ServerSocketInterface : public Server_ProtocolHandler
{
@ -94,6 +95,7 @@ private:
Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc);
Response::ResponseCode cmdRegisterAccount(const Command_Register &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 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