From c431d376f8a6f9723380e909f0b38a72771e11e3 Mon Sep 17 00:00:00 2001 From: Stefan Hacker Date: Sat, 13 Jun 2015 00:06:03 +0200 Subject: [PATCH] Add self-restart capability to client For some option changes a client restart is required to fully apply the changes. This patch enabled Mumble to restart itself. This is accomplished by adding a special MUMBLE_EXIT_CODE_RESTART exit code that is cought right before the application terminates. Mumble then uses available session information to launch another instance of itself right before it exits. As this happens after the vast majority of cleanup multiple client restrictions and other resource contention issues do not come into play. Launching another client deviates from the usually recommended way of not exiting the process but simply doing a re-initialization of the application after the cleanup. With Mumble this is tricky as we have some objects for which we do not control the lifetime after we initialized them once and others might be managed sloppily as the design never expected to have to re-initialize. We should strive to clean up these weaknesses in resource management but for now the approach taken here works around them. --- src/mumble/Global.h | 3 +++ src/mumble/ServerHandler.cpp | 19 +++++++++++++++++++ src/mumble/ServerHandler.h | 3 +++ src/mumble/main.cpp | 35 +++++++++++++++++++++++++++++++++-- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/mumble/Global.h b/src/mumble/Global.h index 7962a7b82c..093da56230 100644 --- a/src/mumble/Global.h +++ b/src/mumble/Global.h @@ -138,6 +138,9 @@ class DeferInit { static void run_destroyers(); }; +/// Special exit code which causes mumble to restart itself. The outward facing return code with be 0 +const int MUMBLE_EXIT_CODE_RESTART = 64738; + // -Wshadow is bugged. If an inline function of a class uses a variable or // parameter named 'g', that will generate a warning even if the class header // is included long before this definition. diff --git a/src/mumble/ServerHandler.cpp b/src/mumble/ServerHandler.cpp index 97e99bdbe4..f4434bd1ff 100644 --- a/src/mumble/ServerHandler.cpp +++ b/src/mumble/ServerHandler.cpp @@ -46,6 +46,7 @@ #include "RichTextEditor.h" #include "SSL.h" #include "User.h" +#include "Net.h" ServerHandlerMessageEvent::ServerHandlerMessageEvent(const QByteArray &msg, unsigned int mtype, bool flush) : QEvent(static_cast(SERVERSEND_EVENT)) { qbaMsg = msg; @@ -848,3 +849,21 @@ void ServerHandler::announceRecordingState(bool recording) { sendMessage(mpus); } +QUrl ServerHandler::getServerURL(bool withPassword) const { + QUrl url; + + url.setScheme(QLatin1String("mumble")); + url.setHost(qsHostName); + if (usPort != DEFAULT_MUMBLE_PORT) { + url.setPort(usPort); + } + + url.setUserName(qsUserName); + + if (withPassword && !qsPassword.isEmpty()) { + url.setPassword(qsPassword); + } + + return url; +} + diff --git a/src/mumble/ServerHandler.h b/src/mumble/ServerHandler.h index 98f36f6609..56e22565ef 100644 --- a/src/mumble/ServerHandler.h +++ b/src/mumble/ServerHandler.h @@ -146,6 +146,9 @@ class ServerHandler : public QThread { void requestChannelPermissions(unsigned int channel); void setSelfMuteDeafState(bool mute, bool deaf); void announceRecordingState(bool recording); + + /// Return connection information as a URL + QUrl getServerURL(bool withPassword = false) const; void disconnect(); void run() Q_DECL_OVERRIDE; diff --git a/src/mumble/main.cpp b/src/mumble/main.cpp index c159fde8f3..c4fdeaa361 100644 --- a/src/mumble/main.cpp +++ b/src/mumble/main.cpp @@ -128,6 +128,7 @@ int main(int argc, char **argv) { #endif bool bAllowMultiple = false; + bool suppressIdentity = false; bool bRpcMode = false; QString rpcCommand; QUrl url; @@ -193,6 +194,7 @@ int main(int argc, char **argv) { } else if (args.at(i) == QLatin1String("-m") || args.at(i) == QLatin1String("--multiple")) { bAllowMultiple = true; } else if (args.at(i) == QLatin1String("-n") || args.at(i) == QLatin1String("--noidentity")) { + suppressIdentity = true; g.s.bSuppressIdentity = true; } else if (args.at(i) == QLatin1String("rpc")) { bRpcMode = true; @@ -508,10 +510,13 @@ int main(int argc, char **argv) { g.s.save(); + url.clear(); + ServerHandlerPtr sh = g.sh; - - if (sh && sh->isRunning()) + if (sh && sh->isRunning()) { + url = sh->getServerURL(); Database::setShortcuts(g.sh->qbaDigest, g.s.qlShortcuts); + } Audio::stop(); @@ -553,6 +558,32 @@ int main(int argc, char **argv) { google::protobuf::ShutdownProtobufLibrary(); #endif #endif + + // At this point termination of our process is immenent. We can safely + // launch another version of Mumble. The reason we do an actual + // restart instead of re-creating our data structures is that making + // sure we won't leave state is quite tricky. Mumble has quite a + // few spots which might not consider seeing to basic initializations. + // Until we invest the time to verify this, rather be safe (and a bit slower) + // than sorry (and crash/bug out). Also take care to reconnect if possible. + if (res == MUMBLE_EXIT_CODE_RESTART) { + QStringList arguments; + + if (bAllowMultiple) arguments << QLatin1String("--multiple"); + if (suppressIdentity) arguments << QLatin1String("--noidentity"); + if (!url.isEmpty()) arguments << url.toString(); + + qWarning() << "Triggering restart of Mumble with arguments: " << arguments; + + if(!QProcess::startDetached(qApp->applicationFilePath(), arguments)) { + QMessageBox::warning(NULL, + QApplication::tr("Failed to restart mumble"), + QApplication::tr("Mumble failed to restart itself. Please restart it manually.") + ); + return 1; + } + return 0; + } return res; }