#ifdef QV2RAY_HAS_BACKWARD #include "3rdparty/backward-cpp/backward.hpp" #endif #ifdef QV2RAY_CLI #include "ui/cli/Qv2rayCliApplication.hpp" #else #include #endif #ifdef QV2RAY_GUI_QWIDGETS #include "ui/widgets/Qv2rayWidgetApplication.hpp" #endif #ifdef QV2RAY_GUI_QML #include "ui/qml/Qv2rayQMLApplication.hpp" #endif #include "utils/QvHelpers.hpp" #include #include #ifndef Q_OS_WIN #include #endif int globalArgc; char **globalArgv; void BootstrapMessageBox(const QString &title, const QString &text) { #ifdef QV2RAY_GUI if (qApp) { QMessageBox::warning(nullptr, title, text); } else { QApplication p(globalArgc, globalArgv); QMessageBox::warning(nullptr, title, text); } #else std::cout << title.toStdString() << NEWLINE << text.toStdString() << std::endl; #endif } const QString SayLastWords() noexcept { QStringList msg; msg << "------- BEGIN QV2RAY CRASH REPORT -------"; #ifdef QV2RAY_HAS_BACKWARD { backward::StackTrace st; backward::TraceResolver resolver; st.load_here(); resolver.load_stacktrace(st); // for (size_t i = 0; i < st.size(); i++) { const auto &trace = resolver.resolve(st[i]); const auto line = QString("#%1: [%2] %3 in %4") .arg(i) .arg(reinterpret_cast(trace.addr)) .arg(trace.object_function.c_str()) .arg(trace.object_filename.c_str()); if (!trace.source.filename.empty()) { const auto sourceFile = QString("%0:[%1:%2]").arg(trace.source.filename.c_str()).arg(trace.source.line).arg(trace.source.col); msg << line + " --> " + sourceFile; } else { msg << line; } } } #endif if (KernelInstance) { msg << "Active Kernel Instances:"; const auto kernels = KernelInstance->GetActiveKernelProtocols(); msg << JsonToString(JsonStructHelper::Serialize(static_cast>(kernels)).toArray(), QJsonDocument::Compact); msg << "Current Connection:"; // const auto currentConnection = KernelInstance->CurrentConnection(); msg << JsonToString(currentConnection.toJson(), QJsonDocument::Compact); msg << NEWLINE; // if (ConnectionManager && !currentConnection.isEmpty()) { msg << "Active Connection Settings:"; const auto connection = ConnectionManager->GetConnectionMetaObject(currentConnection.connectionId); auto group = ConnectionManager->GetGroupMetaObject(currentConnection.groupId); // // Do not collect private data. // msg << NEWLINE; // msg << JsonToString(ConnectionManager->GetConnectionRoot(currentConnection.connectionId)); group.subscriptionOption.address = "HIDDEN"; // msg << JsonToString(connection.toJson(), QJsonDocument::Compact); msg << NEWLINE; msg << "Group:"; msg << JsonToString(group.toJson(), QJsonDocument::Compact); msg << NEWLINE; } } if (PluginHost) { msg << "Plugins:"; const auto plugins = PluginHost->AllPlugins(); for (const auto &plugin : plugins) { const auto data = PluginHost->GetPlugin(plugin)->metadata; QList dataList; dataList << data.Name; dataList << data.Author; dataList << data.InternalName; dataList << data.Description; msg << JsonToString(JsonStructHelper::Serialize(dataList).toArray(), QJsonDocument::Compact); } msg << NEWLINE; } msg << "GlobalConfig:"; msg << JsonToString(GlobalConfig.toJson(), QJsonDocument::Compact); msg << "------- END OF QV2RAY CRASH REPORT -------"; return msg.join(NEWLINE); } void signalHandler(int signum) { #ifndef Q_OS_WIN if (signum == SIGTRAP) { exit(-99); return; } #endif std::cout << "Qv2ray: Interrupt signal (" << signum << ") received." << std::endl; if (signum == SIGTERM) { if (qApp) qApp->exit(); return; } std::cout << "Collecting StackTrace" << std::endl; const auto msg = "Signal: " + QSTRN(signum) + NEWLINE + SayLastWords(); const auto filePath = QV2RAY_CONFIG_DIR + "bugreport/QvBugReport_" + QSTRN(system_clock::to_time_t(system_clock::now())) + ".stacktrace"; { std::cout << msg.toStdString() << std::endl; QDir().mkpath(QV2RAY_CONFIG_DIR + "bugreport/"); StringToFile(msg, filePath); std::cout << "Backtrace saved in: " + filePath.toStdString() << std::endl; } if (qApp) { // qApp->clipboard()->setText(filePath); QString message = QObject::tr("Qv2ray has encountered an uncaught exception: ") + NEWLINE + // QObject::tr("Please report a bug via Github with the file located here: ") + NEWLINE NEWLINE + // filePath; BootstrapMessageBox("UNCAUGHT EXCEPTION", message); } #if defined Q_OS_WIN || defined QT_DEBUG exit(-99); #else kill(getpid(), SIGTRAP); #endif } #ifdef Q_OS_WIN LONG WINAPI TopLevelExceptionHandler(PEXCEPTION_POINTERS) { signalHandler(-1); return EXCEPTION_CONTINUE_SEARCH; } #endif int main(int argc, char *argv[]) { globalArgc = argc; globalArgv = argv; // Register signal handlers. signal(SIGABRT, signalHandler); signal(SIGSEGV, signalHandler); signal(SIGTERM, signalHandler); #ifndef Q_OS_WIN signal(SIGHUP, signalHandler); signal(SIGKILL, signalHandler); #else // AddVectoredExceptionHandler(0, TopLevelExceptionHandler); #endif // // This line must be called before any other ones, since we are using these // values to identify instances. QCoreApplication::setApplicationVersion(QV2RAY_VERSION_STRING); // #ifdef QT_DEBUG QCoreApplication::setApplicationName("qv2ray_debug"); // QApplication::setApplicationDisplayName("Qv2ray - " + QObject::tr("Debug version")); #else QCoreApplication::setApplicationName("qv2ray"); #ifdef QV2RAY_GUI QApplication::setApplicationDisplayName("Qv2ray"); #endif #endif LOG("LICENCE", NEWLINE // "This program comes with ABSOLUTELY NO WARRANTY." NEWLINE // "This is free software, and you are welcome to redistribute it" NEWLINE // "under certain conditions." NEWLINE NEWLINE // "Copyright (c) 2019-2020 Qv2ray Development Group." NEWLINE // "Third-party libraries that have been used in Qv2ray can be found in the About page." NEWLINE) #ifdef QT_DEBUG std::cerr << "WARNING: ================ This is a debug build, many features are not stable enough. ================" << std::endl; #endif // parse the command line before starting as a Qt application switch (Qv2rayApplicationManager::PreInitialize(argc, argv)) { case PRE_INIT_RESULT_QUIT: return QVEXIT_NORMAL; case PRE_INIT_RESULT_CONTINUE: break; case PRE_INIT_RESULT_ERROR: { BootstrapMessageBox("Cannot Start Qv2ray!", "Early initialization failed!"); return QVEXIT_PRE_INITIALIZE_FAIL; } default: Q_UNREACHABLE(); } #ifndef QV2RAY_QT6 QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); #endif // noScaleFactors = disable HiDPI if (StartupOption.noScaleFactor) { LOG(MODULE_INIT, "Force set QT_SCALE_FACTOR to 1.") DEBUG(MODULE_UI, "Original QT_SCALE_FACTOR was: " + qEnvironmentVariable("QT_SCALE_FACTOR")) qputenv("QT_SCALE_FACTOR", "1"); } else { DEBUG(MODULE_INIT, "High DPI scaling is enabled.") #ifndef QV2RAY_QT6 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) #ifdef QV2RAY_GUI QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); #endif #endif } // Check OpenSSL version for auto-update and subscriptions auto osslReqVersion = QSslSocket::sslLibraryBuildVersionString(); auto osslCurVersion = QSslSocket::sslLibraryVersionString(); LOG(MODULE_NETWORK, "Current OpenSSL version: " + osslCurVersion) if (!QSslSocket::supportsSsl()) { LOG(MODULE_NETWORK, "Required OpenSSL version: " + osslReqVersion) LOG(MODULE_NETWORK, "OpenSSL library MISSING, Quitting.") BootstrapMessageBox(QObject::tr("Dependency Missing"), QObject::tr("Cannot find openssl libs") + NEWLINE + QObject::tr("This could be caused by a missing of `openssl` package in your system.") + NEWLINE + QObject::tr("If you are using an AppImage from Github Action, please report a bug.") + NEWLINE + // NEWLINE + QObject::tr("Technical Details") + NEWLINE + // "OSsl.Rq.V=" + osslReqVersion + NEWLINE + // "OSsl.Cr.V=" + osslCurVersion); BootstrapMessageBox(QObject::tr("Cannot start Qv2ray"), QObject::tr("Cannot start Qv2ray without OpenSSL")); return QVEXIT_SSL_FAIL; } Qv2rayApplication app(argc, argv); switch (app.Initialize()) { case NORMAL: break; case SINGLE_APPLICATION: return QVEXIT_SECONDARY_INSTANCE; case FAILED: { app.MessageBoxWarn(nullptr, app.tr("Cannot start Qv2ray"), app.tr("Qv2ray early initialization failed.")); return QVEXIT_EARLY_SETUP_FAIL; } } // // Qv2ray Initialize, find possible config paths and verify them. if (!app.FindAndCreateInitialConfiguration()) { LOG(MODULE_INIT, "Cannot load or create initial configuration file.") app.MessageBoxWarn(nullptr, app.tr("Cannot start Qv2ray"), app.tr("Cannot load config file.")); return QVEXIT_CONFIG_FILE_FAIL; } #ifndef Q_OS_WIN signal(SIGUSR1, [](int) { ConnectionManager->RestartConnection(); }); signal(SIGUSR2, [](int) { ConnectionManager->StopConnection(); }); #endif const auto rcode = app.RunQv2ray(); if (rcode == QVEXIT_NEW_VERSION) { LOG(MODULE_INIT, "Starting new version of Qv2ray: " + Qv2rayProcessArgument._qvNewVersionPath) QProcess::startDetached(Qv2rayProcessArgument._qvNewVersionPath, {}); } return rcode; }