#include "i2p-controller.h" #include "prog-constains.h" #include "locales.h" #if defined(Q_OS_LINUX) #include #include #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::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(); }); }