From f33c198d5d003140f622c207ccf066d351c2b44c Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Thu, 16 Nov 2017 02:18:46 +0100 Subject: [PATCH] Ref T195, application restart functions * allow to get current arguments joined with new ones * removed old inheritedArguments function * unregisterAsRunning (for restart) * restart function --- src/blackcore/application.cpp | 131 +++++++++++++++++++------ src/blackcore/application.h | 24 ++++- src/blackcore/vatsim/networkvatlib.cpp | 9 +- src/blackcore/vatsim/networkvatlib.h | 2 +- 4 files changed, 129 insertions(+), 37 deletions(-) diff --git a/src/blackcore/application.cpp b/src/blackcore/application.cpp index e9958dcef..0e1a7250d 100644 --- a/src/blackcore/application.cpp +++ b/src/blackcore/application.cpp @@ -48,9 +48,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -164,7 +166,7 @@ namespace BlackCore connect(m_setupReader.data(), &CSetupReader::distributionInfoAvailable, this, &CApplication::distributionInfoAvailable, Qt::QueuedConnection); connect(m_setupReader.data(), &CSetupReader::successfullyReadSharedUrl, m_networkWatchDog.data(), &CNetworkWatchdog::setWorkingSharedUrl, Qt::QueuedConnection); - m_parser.addOptions(m_setupReader->getCmdLineOptions()); // add options from reader + this->addParserOptions(m_setupReader->getCmdLineOptions()); // add options from reader // startup done connect(this, &CApplication::startUpCompleted, this, &CApplication::onStartUpCompleted, Qt::QueuedConnection); @@ -177,6 +179,7 @@ namespace BlackCore bool CApplication::registerAsRunning() { + //! \fixme KB 2017-11 maybe this code can be encapsulated somewhere CApplicationInfoList apps = CApplication::getRunningApplications(); const CApplicationInfo myself = CApplication::instance()->getApplicationInfo(); if (!apps.contains(myself)) { apps.insert(myself); } @@ -185,6 +188,18 @@ namespace BlackCore return ok; } + bool CApplication::unregisterAsRunning() + { + //! \fixme KB 2017-11 maybe this code can be encapsulated somewhere + CApplicationInfoList apps = CApplication::getRunningApplications(); + const CApplicationInfo myself = CApplication::instance()->getApplicationInfo(); + if (!apps.contains(myself)) { return true; } + apps.remove(myself); + const bool ok = CFileUtils::writeStringToLockedFile(apps.toJsonString(), CFileUtils::appendFilePaths(swiftDataRoot(), "apps.json")); + if (!ok) { CLogMessage(static_cast(nullptr)).error("Failed to write to application list file"); } + return ok; + } + int CApplication::exec() { Q_ASSERT_X(instance(), Q_FUNC_INFO, "missing application"); @@ -192,11 +207,12 @@ namespace BlackCore return QCoreApplication::exec(); } - void CApplication::restartApplication() + void CApplication::restartApplication(const QStringList &newArguments, const QStringList &removeArguments) { - this->gracefulShutdown(); + CApplication::unregisterAsRunning(); const QString prg = QCoreApplication::applicationFilePath(); - const QStringList args = CApplication::arguments(); + const QStringList args = this->argumentsJoined(newArguments, removeArguments); + this->gracefulShutdown(); QProcess::startDetached(prg, args); this->exit(0); } @@ -208,7 +224,7 @@ namespace BlackCore CApplicationInfo CApplication::getApplicationInfo() const { - CApplicationInfo::ApplicationMode mode; + CApplicationInfo::ApplicationMode mode = CApplicationInfo::None; if (isRunningInDeveloperEnvironment()) { mode |= CApplicationInfo::Developer; } if (CBuildConfig::isDevBranch()) { mode |= CApplicationInfo::BetaTest; } return { CApplication::getSwiftApplication(), mode, QCoreApplication::applicationFilePath(), CBuildConfig::getVersionString(), CProcessInfo::currentProcess() }; @@ -298,10 +314,19 @@ namespace BlackCore static const QString launcher = CApplication::getExecutableForApplication(CApplicationInfo::Application::Laucher); if (launcher.isEmpty() || CApplication::isApplicationRunning(CApplicationInfo::Laucher)) { return false; } - const QStringList args = this->inheritedArguments(true); + const QStringList args = this->argumentsJoined({}, { "--dbus" }); return QProcess::startDetached(launcher, args); } + bool CApplication::startLauncherAndQuit() + { + const bool started = CApplication::startLauncher(); + if (!started) { return false; } + this->gracefulShutdown(); + CApplication::exit(); + return true; + } + bool CApplication::isUnitTest() const { return m_unitTest; @@ -752,6 +777,34 @@ namespace BlackCore return QCoreApplication::arguments(); } + int CApplication::indexOfCommandLineOption(const QCommandLineOption &option, const QStringList &args) + { + const QStringList names = option.names(); + if (names.isEmpty() || args.isEmpty()) { return -1; } + int i = -1; + for (const QString &arg : args) + { + i++; + QString a; + if (arg.startsWith("--")) { a = arg.mid(2); } + else if (arg.startsWith("-")) { a = arg.mid(1); } + else { continue; } + + if (names.contains(a, Qt::CaseInsensitive)) { return i; } + } + return -1; + } + + void CApplication::argumentsWithoutOption(const QCommandLineOption &option, QStringList &args) + { + const int index = indexOfCommandLineOption(option, args); + if (index < 0) { return; } + + // remove argument and its value + args.removeAt(index); + if (!option.valueName().isEmpty() && args.size() > index) { args.removeAt(index); } + } + void CApplication::processEventsFor(int milliseconds) { // sApp check allows to use in test cases without sApp @@ -857,6 +910,8 @@ namespace BlackCore m_parser.setApplicationDescription(m_applicationName); m_cmdHelp = m_parser.addHelpOption(); m_cmdVersion = m_parser.addVersionOption(); + m_allOptions.append(m_cmdHelp); + m_allOptions.append(m_cmdVersion); // dev. system m_cmdDevelopment = QCommandLineOption({ "dev", "development" }, @@ -1067,11 +1122,13 @@ namespace BlackCore bool CApplication::addParserOption(const QCommandLineOption &option) { + m_allOptions.append(option); return m_parser.addOption(option); } bool CApplication::addParserOptions(const QList &options) { + m_allOptions.append(options); return m_parser.addOptions(options); } @@ -1090,16 +1147,10 @@ namespace BlackCore QString CApplication::getCmdDBusAddressValue() const { - if (this->isParserOptionSet(m_cmdDBusAddress)) - { - const QString v(this->getParserValue(m_cmdDBusAddress)); - const QString dBusAddress(CDBusServer::normalizeAddress(v)); - return dBusAddress; - } - else - { - return ""; - } + if (!this->isParserOptionSet(m_cmdDBusAddress)) { return ""; } + const QString v(this->getParserValue(m_cmdDBusAddress)); + const QString dBusAddress(CDBusServer::normalizeAddress(v)); + return dBusAddress; } QString CApplication::getCmdSwiftPrivateSharedDir() const @@ -1139,7 +1190,7 @@ namespace BlackCore // checks if (CBuildConfig::isLifetimeExpired()) { - this->cmdLineErrorMessage("Program exired " + CBuildConfig::getEol().toString()); + this->cmdLineErrorMessage("Program expired " + CBuildConfig::getEol().toString()); return false; } @@ -1216,18 +1267,6 @@ namespace BlackCore return false; } - QStringList CApplication::inheritedArguments(bool withVatlibArgs) const - { - QStringList args; - if (this->isSet(m_cmdDevelopment)) - { - args.append("--" + m_cmdDevelopment.names().first()); - args.append("true"); - } - if (withVatlibArgs) { args.append(CNetworkVatlib::inheritedArguments()); } - return args; - } - QString CApplication::cmdLineArgumentsAsString(bool withExecutable) { QStringList args = QCoreApplication::arguments(); @@ -1247,6 +1286,40 @@ namespace BlackCore printf("%s %s\n", qPrintable(QCoreApplication::applicationName()), qPrintable(QCoreApplication::applicationVersion())); } + QStringList CApplication::argumentsJoined(const QStringList &newArguments, const QStringList &removeArguments) const + { + QStringList joinedArguments = CApplication::arguments(); + QStringList newArgumentsChecked = newArguments; + + // remove the executable argument if it exists at position 0 + if (!joinedArguments.isEmpty() && !joinedArguments.at(0).startsWith("-")) { joinedArguments.removeFirst(); } // was cmd line argument + if (!newArgumentsChecked.isEmpty() && !newArgumentsChecked.at(0).startsWith("-")) { newArgumentsChecked.removeFirst(); } // was cmd line argument + + // remove all values before checking options + static const QRegularExpression regExp("^-"); + QStringList toBeRemoved(newArgumentsChecked.filter(regExp)); + toBeRemoved.append(removeArguments.filter(regExp)); + toBeRemoved.append("--installer"); + toBeRemoved.removeDuplicates(); + + if (!joinedArguments.isEmpty() && !toBeRemoved.isEmpty()) + { + // remove all options from removeArguments + // consider alias names, that is why we check on option + for (const QCommandLineOption &option : m_allOptions) + { + const int n = indexOfCommandLineOption(option, toBeRemoved); + if (n >= 0) + { + argumentsWithoutOption(option, joinedArguments); + } + } + } + + joinedArguments.append(newArgumentsChecked); + return joinedArguments; + } + // --------------------------------------------------------------------------------- // Contexts // --------------------------------------------------------------------------------- diff --git a/src/blackcore/application.h b/src/blackcore/application.h index dcf95d47f..5f9ed05cc 100644 --- a/src/blackcore/application.h +++ b/src/blackcore/application.h @@ -144,6 +144,9 @@ namespace BlackCore //! Start the launcher bool startLauncher(); + //! Start the launcher and quit + bool startLauncherAndQuit(); + //! Unit test? bool isUnitTest() const; @@ -247,12 +250,19 @@ namespace BlackCore QString getTemporaryDirectory() const; //! Stop and restart application - void restartApplication(); + void restartApplication(const QStringList &newArguments = {}, const QStringList &removeArguments = {}); + + //! Current parameters replaced by new arguments without the cmd line argument + virtual QStringList argumentsJoined(const QStringList &newArguments = {}, const QStringList &removeArguments = {}) const; //! Register as running //! \note Normally done automatically when CApplication::exec is called static bool registerAsRunning(); + //! Unregister from running + //! \note Normally done automatically, needed for restart + static bool unregisterAsRunning(); + //! Run event loop static int exec(); @@ -264,6 +274,7 @@ namespace BlackCore //! Process all events for some time //! \remark unlike QCoreApplication::processEvents this will spend at least the given time in the function, using QThread::msleep + //! \remark using processEventsFor can lead to undesired behaviour: A function may be called again before it is finished, even with only one thread //! \sa BlackMisc::CEventLoop static void processEventsFor(int milliseconds); @@ -326,9 +337,6 @@ namespace BlackCore //! Display error message virtual bool cmdLineErrorMessage(const BlackMisc::CStatusMessageList &msgs, bool retry = false) const; - //! Arguments to be passed to another swift appplication - QStringList inheritedArguments(bool withVatlibArgs = true) const; - //! cmd line arguments as string virtual QString cmdLineArgumentsAsString(bool withExecutable = true); //! @} @@ -408,7 +416,6 @@ namespace BlackCore // ------------------------- network ----------------------------------------------- - public: static constexpr int NoRedirects = -1; //!< network request not allowing redirects static constexpr int NoLogRequestId = -1; //!< network request without logging static constexpr int DefaultMaxRedirects = 2; //!< network request, default for max.redirects @@ -503,6 +510,12 @@ namespace BlackCore //! Display version message virtual void cmdLineVersionMessage() const; + //! Is the command line option represented in the given arguments? + static int indexOfCommandLineOption(const QCommandLineOption &option, const QStringList &args = CApplication::arguments()); + + //! Arguments without that given option + static void argumentsWithoutOption(const QCommandLineOption &option, QStringList &args); + //! Can be used to parse specialized arguments virtual bool parsingHookIn() { return true; } @@ -534,6 +547,7 @@ namespace BlackCore static void registerMetadata(); // cmd parsing + QList m_allOptions; //!< all registered options QCommandLineParser m_parser; //!< cmd parser QCommandLineOption m_cmdHelp {"help"}; //!< help option QCommandLineOption m_cmdVersion {"version"}; //!< version option diff --git a/src/blackcore/vatsim/networkvatlib.cpp b/src/blackcore/vatsim/networkvatlib.cpp index 6a9696707..273460134 100644 --- a/src/blackcore/vatsim/networkvatlib.cpp +++ b/src/blackcore/vatsim/networkvatlib.cpp @@ -545,12 +545,12 @@ namespace BlackCore return m_interimPositionReceivers; } - QStringList CNetworkVatlib::inheritedArguments() + QStringList CNetworkVatlib::vatlibArguments() { QStringList args; int id = 0; QString key; - if (!getCmdLineClientIdAndKey(id, key)) return args; + if (!getCmdLineClientIdAndKey(id, key)) { return args; } args << "--idAndKey"; args << sApp->getParserValue("clientIdAndKey"); // as typed in return args; @@ -723,6 +723,11 @@ namespace BlackCore bool CNetworkVatlib::getCmdLineClientIdAndKey(int &id, QString &key) { + // init values + id = 0; + key = ""; + + // split parser values const QString clientIdAndKey = sApp->getParserValue("clientIdAndKey").toLower(); if (clientIdAndKey.isEmpty() || !clientIdAndKey.contains(':')) { return false; } const auto stringList = clientIdAndKey.split(':'); diff --git a/src/blackcore/vatsim/networkvatlib.h b/src/blackcore/vatsim/networkvatlib.h index 1d2988746..288dece52 100644 --- a/src/blackcore/vatsim/networkvatlib.h +++ b/src/blackcore/vatsim/networkvatlib.h @@ -117,7 +117,7 @@ namespace BlackCore //! @} //! Arguments to be passed to another swift appplication - static QStringList inheritedArguments(); + static QStringList vatlibArguments(); //! \name Weather functions //! @{