From 06b0163319f4ec1787a5c4c9a857b5be808380b4 Mon Sep 17 00:00:00 2001 From: arm64v8a <48624112+arm64v8a@users.noreply.github.com> Date: Sun, 11 Sep 2022 11:38:48 +0800 Subject: [PATCH] refactor core_process (1) --- db/ConfigBuilder.cpp | 7 +- main/NekoRay_DataStore.hpp | 2 + rpc/gRPC.cpp | 4 +- sys/ExternalProcess.cpp | 131 +++++++++++++++++++++++++------------ sys/ExternalProcess.hpp | 22 +++++-- ui/mainwindow.cpp | 52 ++------------- ui/mainwindow.h | 5 +- 7 files changed, 120 insertions(+), 103 deletions(-) diff --git a/db/ConfigBuilder.cpp b/db/ConfigBuilder.cpp index 8d0fd0e4f..40b92cfcb 100644 --- a/db/ConfigBuilder.cpp +++ b/db/ConfigBuilder.cpp @@ -399,8 +399,11 @@ namespace NekoRay { outbound["settings"] = settings; // EXTERNAL PROCESS - auto extC = new sys::ExternalProcess(ent->bean->DisplayType(), - extR.program, extR.arguments, extR.env); + auto extC = new sys::ExternalProcess(); + extC->tag = ent->bean->DisplayType(); + extC->program = extR.program; + extC->arguments = extR.arguments; + extC->env = extR.env; status->result->ext += extC; } else { coreR = ent->bean->BuildCoreObj(); diff --git a/main/NekoRay_DataStore.hpp b/main/NekoRay_DataStore.hpp index 94ef76453..1b3dd1bd6 100644 --- a/main/NekoRay_DataStore.hpp +++ b/main/NekoRay_DataStore.hpp @@ -41,6 +41,8 @@ namespace NekoRay { QString core_token; int core_port = 19810; int started_id = -1919; + bool core_running = false; + bool core_prepare_exit = false; Routing *routing = new Routing; int imported_count = 0; diff --git a/rpc/gRPC.cpp b/rpc/gRPC.cpp index c72fa02d8..5dd5e3dd1 100644 --- a/rpc/gRPC.cpp +++ b/rpc/gRPC.cpp @@ -23,8 +23,6 @@ namespace QtGrpc { const char *GrpcStatusMessage = "grpc-message"; const int GrpcMessageSizeHeaderSize = 5; - bool core_crashed = false; - class Http2GrpcChannelPrivate : public QObject { private: QString url_base; @@ -135,7 +133,7 @@ namespace QtGrpc { QNetworkReply::NetworkError Call(const QString &methodName, const google::protobuf::Message &req, google::protobuf::Message *rsp, int timeout_ms = 0) { - if (core_crashed) return QNetworkReply::NetworkError(-1919); + if (!NekoRay::dataStore->core_running) return QNetworkReply::NetworkError(-1919); std::string reqStr; req.SerializeToString(&reqStr); diff --git a/sys/ExternalProcess.cpp b/sys/ExternalProcess.cpp index 3e9b26978..2ba8e6f30 100644 --- a/sys/ExternalProcess.cpp +++ b/sys/ExternalProcess.cpp @@ -1,57 +1,46 @@ #include "ExternalProcess.hpp" +#include + namespace NekoRay::sys { - ExternalProcess::ExternalProcess(const QString &tag, - const QString &program, - const QStringList &arguments, - const QStringList &env) - : QProcess() { - this->tag = tag; - this->program = program; - this->arguments = arguments; - this->env = env; - this->running_list = &running_ext; - } + ExternalProcess::ExternalProcess() : QProcess() {} void ExternalProcess::Start() { if (started) return; started = true; - *running_list += this; + if (managed) running_ext.push_back(this); if (show_log) { - connect(this, &QProcess::readyReadStandardOutput, this, - [&]() { - showLog_ext_vt100(readAllStandardOutput().trimmed()); - }); - connect(this, &QProcess::readyReadStandardError, this, - [&]() { - showLog_ext_vt100(readAllStandardError().trimmed()); - }); + connect(this, &QProcess::readyReadStandardOutput, this, [&]() { + showLog_ext_vt100(readAllStandardOutput().trimmed()); + }); + connect(this, &QProcess::readyReadStandardError, this, [&]() { + showLog_ext_vt100(readAllStandardError().trimmed()); + }); } - connect(this, &QProcess::errorOccurred, this, - [&](QProcess::ProcessError error) { - if (!killed) { + if (managed) { + connect(this, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) { + if (!killed) { + crashed = true; + showLog_ext(tag, "errorOccurred:" + errorString()); + dialog_message("ExternalProcess", "Crashed"); + } + }); + connect(this, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) { + if (state == QProcess::NotRunning) { + if (killed) { // 用户命令退出 + showLog_ext(tag, "Stopped"); + } else if (!crashed) { // 异常退出 crashed = true; - showLog_ext(tag, "[Error] Crashed:" + QProcess::errorString()); + showLog_ext(tag, "[Error] Program exited accidentally: " + errorString()); + Kill(); dialog_message("ExternalProcess", "Crashed"); } - }); - connect(this, &QProcess::stateChanged, this, - [&](QProcess::ProcessState state) { - if (state == QProcess::NotRunning) { - if (killed) { - showLog_ext(tag, "Stopped"); - } else if (!crashed) { - crashed = true; - Kill(); - showLog_ext(tag, "[Error] Program exited accidentally"); - dialog_message("ExternalProcess", "Crashed"); - } - } - }); - - showLog_ext(tag, "[Starting] " + env.join(" ") + " " + program + " " + arguments.join(" ")); + } + }); + showLog_ext(tag, "[Starting] " + env.join(" ") + " " + program + " " + arguments.join(" ")); + } QProcess::setEnvironment(env); QProcess::start(program, arguments); @@ -60,11 +49,71 @@ namespace NekoRay::sys { void ExternalProcess::Kill() { if (killed) return; killed = true; - running_list->removeAll(this); + if (managed) running_ext.removeAll(this); + if (!crashed) { QProcess::kill(); QProcess::waitForFinished(500); } } + CoreProcess::CoreProcess(const QString &core_path, const QStringList &args) { + ExternalProcess::managed = false; + ExternalProcess::show_log = false; + ExternalProcess::program = core_path; + ExternalProcess::arguments = args; + + connect(this, &QProcess::readyReadStandardOutput, this, [&]() { + showLog(readAllStandardOutput().trimmed()); + }); + connect(this, &QProcess::readyReadStandardError, this, [&]() { + auto log = readAllStandardError().trimmed(); + if (show_stderr) { + showLog(log); + return; + } + if (log.contains("token is set")) { + show_stderr = true; + } + }); + connect(this, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) { + if (error == QProcess::ProcessError::FailedToStart) { + failed_to_start = true; + showLog("start nekoray_core errorOccurred: " + errorString() + "\n"); + } + }); + connect(this, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) { + NekoRay::dataStore->core_running = state == QProcess::Running; + + if (!dataStore->core_prepare_exit && state == QProcess::NotRunning) { + if (failed_to_start) return; // no retry + + showLog("[Error] nekoray_core exited, restarting.\n"); + + // Restart + auto t = new QTimer; + connect(t, &QTimer::timeout, this, [=] { + Kill(); + ExternalProcess::started = false; + Start(); + t->deleteLater(); + }); + t->setSingleShot(true); + t->setInterval(1000); + t->start(); + } + }); + } + + void CoreProcess::Start() { + show_stderr = false; + if (!dataStore->v2ray_asset_dir.isEmpty()) { + setEnvironment(QStringList{ + "V2RAY_LOCATION_ASSET=" + dataStore->v2ray_asset_dir + }); + } + ExternalProcess::Start(); + write((dataStore->core_token + "\n").toUtf8()); + } + } diff --git a/sys/ExternalProcess.hpp b/sys/ExternalProcess.hpp index 3350ed732..1304cd3d0 100644 --- a/sys/ExternalProcess.hpp +++ b/sys/ExternalProcess.hpp @@ -12,26 +12,34 @@ namespace NekoRay::sys { QStringList arguments; QStringList env; + bool managed = true; // running_ext & stateChanged bool show_log = true; - QList *running_list; - ExternalProcess(const QString &tag, - const QString &program, - const QStringList &arguments, - const QStringList &env); + ExternalProcess(); // start & kill is one time - void Start(); + virtual void Start(); void Kill(); - private: + protected: bool started = false; bool killed = false; bool crashed = false; }; + class CoreProcess : public ExternalProcess { + public: + CoreProcess(const QString &core_path, const QStringList &args); + + void Start() override; + + private: + bool show_stderr = false; + bool failed_to_start = false; + }; + // start & kill change this list inline QList running_ext; } diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 7e51cae42..09ab2da0a 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -52,10 +52,6 @@ #include #include -namespace QtGrpc { - extern bool core_crashed; -} - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { mainwindow = this; @@ -343,21 +339,14 @@ MainWindow::MainWindow(QWidget *parent) connect(ui->menu_full_test, &QAction::triggered, this, [=]() { speedtest_current_group(2); }); refresh_status(); - // Start Core + // Prepare core NekoRay::dataStore->core_token = GetRandomString(32); NekoRay::dataStore->core_port = MkPort(); if (NekoRay::dataStore->core_port <= 0) NekoRay::dataStore->core_port = 19810; - QString starting_info; auto core_path = NekoRay::dataStore->core_path; -#ifdef Q_OS_WIN - if (!core_path.endsWith(".exe")) core_path += ".exe"; -#endif if (NekoRay::dataStore->flag_use_appdata) { core_path = QApplication::applicationDirPath() + "/nekoray_core"; - } else if (!QFile(core_path).exists()) { - starting_info = "(not found)"; - core_path = ""; } QStringList args; @@ -368,40 +357,9 @@ MainWindow::MainWindow(QWidget *parent) args.push_back("-debug"); #endif - showLog("Starting nekoray core " + starting_info + "\n"); - - if (!core_path.isEmpty()) { - core_process = new QProcess; - core_process_show_stderr = false; - - connect(core_process, &QProcess::readyReadStandardOutput, this, [&]() { - showLog(core_process->readAllStandardOutput().trimmed()); - }); - connect(core_process, &QProcess::readyReadStandardError, this, [&]() { - auto log = core_process->readAllStandardError().trimmed(); - if (core_process_show_stderr) { - showLog(log); - return; - } - if (log.contains("token is set")) { - core_process_show_stderr = true; - } - }); - connect(core_process, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) { - if (!prepare_exit_core && state == QProcess::NotRunning) { - QtGrpc::core_crashed = true; - showLog("[Error] nekoray_core crashed, please restart the program."); - } - }); - - if (!NekoRay::dataStore->v2ray_asset_dir.isEmpty()) { - core_process->setEnvironment(QStringList{ - "V2RAY_LOCATION_ASSET=" + NekoRay::dataStore->v2ray_asset_dir - }); - } - core_process->start(core_path, args); - core_process->write((NekoRay::dataStore->core_token + "\n").toUtf8()); - } + // Start core + core_process = new NekoRay::sys::CoreProcess(core_path, args); + core_process->Start(); setup_grpc(); @@ -563,7 +521,7 @@ void MainWindow::on_menu_exit_triggered() { // on_commitDataRequest(); // - prepare_exit_core = true; + NekoRay::dataStore->core_prepare_exit = true; hide(); ExitNekorayCore(); // diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 85795ecf1..64f43c67a 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -15,6 +15,7 @@ #include "main/GuiUtils.hpp" class QFileSystemWatcher; +namespace NekoRay::sys { class CoreProcess; } QT_BEGIN_NAMESPACE namespace Ui { @@ -120,9 +121,7 @@ private slots: QShortcut *shortcut_ctrl_f = new QShortcut(QKeySequence("Ctrl+F"), this); QShortcut *shortcut_esc = new QShortcut(QKeySequence("Esc"), this); // - QProcess *core_process; - bool prepare_exit_core = false; - bool core_process_show_stderr = false; + NekoRay::sys::CoreProcess *core_process; qint64 vpn_pid = 0; QFileSystemWatcher *watcher = nullptr; //