launcher/i2p-controller.cpp

155 lines
3.9 KiB
C++

#include "i2p-controller.h"
#include "prog-constains.h"
#include "locales.h"
#if defined(Q_OS_LINUX)
#include <sys/prctl.h>
#include <signal.h>
#endif
I2PController::I2PController(QObject *parent)
: QObject(parent)
{
i2p.setWorkingDirectory(I2P_INSTALL_PATH);
this->isFatalErrored = false;
connect(&i2p, &QProcess::errorOccurred,
this, &I2PController::onProcessError);
connect(&i2p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &I2PController::onProcessFinished);
connect(&i2p, &QProcess::readyReadStandardOutput, this, [this]() {
// Logging I2P
//qDebug() << "[i2pd]" << i2p.readAllStandardOutput();
});
connect(&i2p, &QProcess::readyReadStandardError, this, [this]() {
QString msg = i2p.readAllStandardError();
qDebug() << "[i2pd ERROR]" << msg;
if (msg.contains("missing/unreadable config file:")) {
emit fatalError(msg);
}
});
// Close the launcher
connect(this, &I2PController::fatalError, [this](QString msg) {
this->isFatalErrored = true;
qCritical() << msg;
LocaleMap* locale = getLocale();
QMessageBox::critical(
nullptr,
QString::fromStdString(locale->at("i2pd.errorTitle")),
QString::fromStdString(locale->at("i2pd.errorDescription.p1")) + msg +
QString::fromStdString(locale->at("i2pd.errorDescription.p2")),
QMessageBox::Ok
);
qApp->exit(1);
});
}
I2PController::~I2PController()
{
if (i2p.state() != QProcess::NotRunning)
{
i2p.terminate();
i2p.waitForFinished(3000);
if (i2p.state() != QProcess::NotRunning)
i2p.kill();
}
}
void I2PController::start() {
if (i2p.state() != QProcess::NotRunning)
{
qDebug() << "i2pd already running";
return;
}
#if defined(Q_OS_LINUX)
// If SIGKILL on Linux
i2p.setChildProcessModifier([]() {
prctl(PR_SET_PDEATHSIG, SIGTERM);
});
#endif
QStringList args;
#if defined(Q_OS_WIN)
this->i2p.start(QDir(I2P_INSTALL_PATH).filePath("i2pd.exe"), args);
#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
QString configsFolder = QDir(I2P_INSTALL_PATH).filePath("configs");
QString logsFolder = QDir(I2P_INSTALL_PATH).filePath("logs");
args << "--datadir" << QDir(I2P_INSTALL_PATH).filePath("data")
<< "--conf" << QDir(configsFolder).filePath("i2pd.conf")
<< "--tunconf" << QDir(configsFolder).filePath("tunnels.conf")
<< "--log" << QDir(logsFolder).filePath("i2pd.log");
//qDebug() << "i2pd args:" << args;
this->i2p.start(QDir(I2P_INSTALL_PATH).filePath("i2pd"), args);
#else
#endif
}
void I2PController::onProcessError(QProcess::ProcessError error)
{
qDebug() << "I2P process error:" << error;
switch (error)
{
case QProcess::FailedToStart:
qCritical() << "i2pd failed to start";
emit fatalError("Failed to start i2pd");
break;
case QProcess::Crashed:
qWarning() << "i2pd crashed";
restartI2P();
break;
default:
break;
}
}
void I2PController::onProcessFinished(int exitCode, QProcess::ExitStatus status)
{
qDebug() << "i2pd finished" << exitCode << status;
if (status == QProcess::CrashExit)
{
qWarning() << "i2pd crashed, restarting...";
restartI2P();
return;
}
if (exitCode != 0)
{
qWarning() << "i2pd exited with error";
restartI2P();
}
}
void I2PController::restartI2P()
{
if (isFatalErrored) return;
static int restartCount = 0;
if (restartCount > 5)
{
qCritical() << "i2pd crashed too many times";
emit fatalError("i2pd keeps crashing");
return;
}
restartCount++;
QTimer::singleShot(3000, [this]() {
start();
});
}