diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 34eb533b6..3a777feb0 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -88,6 +88,7 @@ Application::Application(int &argc, char **argv) : _gui(0), _theme(Theme::instance()), _helpOnly(false), + _versionOnly(false), _showLogWindow(false), _logExpire(0), _logFlush(false), @@ -104,7 +105,7 @@ Application::Application(int &argc, char **argv) : #endif parseOptions(arguments()); //no need to waste time; - if ( _helpOnly ) return; + if ( _helpOnly || _versionOnly ) return; if (isRunning()) return; @@ -174,6 +175,11 @@ Application::Application(int &argc, char **argv) : // Cleanup at Quit. connect (this, SIGNAL(aboutToQuit()), SLOT(slotCleanup())); + // remember the version of the currently running binary. On Linux it might happen that the + // package management updates the package while the app is running. This is detected in the + // updater slot: If the installed binary on the hd has a different version than the one + // running, the running app is restart. That happens in folderman. + _runningAppVersion = Utility::versionOfInstalledBinary(); } Application::~Application() @@ -211,6 +217,16 @@ void Application::slotCleanup() _gui->deleteLater(); } + + if( Utility::isLinux() ) { + // on linux, check if the installed binary is still the same version + // as the one that is running. If not, restart if possible. + const QByteArray fsVersion = Utility::versionOfInstalledBinary(); + + if( !(fsVersion.isEmpty() || _runningAppVersion.isEmpty()) && fsVersion != _runningAppVersion ) { + _folderManager->slotScheduleAppRestart(); + } + } void Application::slotCheckConnection() { auto list = AccountManager::instance()->accounts(); @@ -339,6 +355,8 @@ void Application::parseOptions(const QStringList &options) } } else if (option == QLatin1String("--debug")) { _debugMode = true; + } else if (option == QLatin1String("--version")) { + _versionOnly = true; } else { showHint("Unrecognized option '" + option.toStdString() + "'"); } @@ -389,6 +407,17 @@ void Application::showHelp() displayHelpText(helpText); } +void Application::showVersion() +{ + QString helpText; + QTextStream stream(&helpText); + stream << _theme->appName().toLatin1().constData() + << QLatin1String(" version ") + << _theme->version().toLatin1().constData() << endl; + + displayHelpText(helpText); +} + void Application::showHint(std::string errorHint) { static QString binName = QFileInfo(QCoreApplication::applicationFilePath()).fileName(); @@ -485,6 +514,11 @@ bool Application::giveHelp() return _helpOnly; } +bool Application::versionOnly() +{ + return _versionOnly; +} + void Application::showSettingsDialog() { _gui->slotShowSettings(); diff --git a/src/gui/application.h b/src/gui/application.h index aa6406428..b09895c4f 100644 --- a/src/gui/application.h +++ b/src/gui/application.h @@ -58,6 +58,8 @@ public: void showHelp(); void showHint(std::string errorHint); bool debugMode(); + bool versionOnly(); // only display the version? + void showVersion(); void showSettingsDialog(); @@ -93,6 +95,7 @@ private: Theme *_theme; bool _helpOnly; + bool _versionOnly; // options from command line: bool _showLogWindow; @@ -102,6 +105,7 @@ private: bool _logFlush; bool _userTriggeredConnect; bool _debugMode; + QByteArray _runningAppVersion; ClientProxy _proxy; diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index 193819cc4..d57c0ccd3 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -54,7 +54,8 @@ static qint64 msBetweenRequestAndSync = 2000; FolderMan::FolderMan(QObject *parent) : QObject(parent), _currentSyncFolder(0), - _syncEnabled( true ) + _syncEnabled( true ), + _appRestartRequired(false) { Q_ASSERT(!_instance); _instance = this; @@ -473,6 +474,12 @@ void FolderMan::slotScheduleAllFolders() } } +void FolderMan::slotScheduleAppRestart() +{ + _appRestartRequired = true; + qDebug() << "## Application restart requested!"; +} + /* * if a folder wants to be synced, it calls this slot and is added * to the queue. The slot to actually start a sync is called afterwards. @@ -540,6 +547,11 @@ void FolderMan::slotRunOneEtagJob() } if (_currentEtagJob.isNull()) { qDebug() << "No more remote ETag check jobs to schedule."; + + /* now it might be a good time to check for restarting... */ + if( _currentSyncFolder == NULL && _appRestartRequired ) { + restartApplication(); + } } else { qDebug() << "Scheduling" << alias << "to check remote ETag"; _currentEtagJob->start(); // on destroy/end it will continue the queue via slotEtagJobDestroyed @@ -1164,5 +1176,19 @@ QString FolderMan::checkPathValidityForNewFolder(const QString& path, bool forNe } +void FolderMan::restartApplication() +{ + if( Utility::isLinux() ) { + // restart: + qDebug() << "### Restarting application NOW, PID" << qApp->applicationPid() << "is ending."; + qApp->quit(); + QStringList args = qApp->arguments(); + QString prg = args.takeFirst(); + + QProcess::startDetached(prg, args); + } else { + qDebug() << "On this platform we do not restart."; + } +} } // namespace OCC diff --git a/src/gui/folderman.h b/src/gui/folderman.h index e1ca9f97c..167b82f5f 100644 --- a/src/gui/folderman.h +++ b/src/gui/folderman.h @@ -148,6 +148,11 @@ public slots: */ void slotAccountStateChanged(); + /** + * restart the client as soon as it is possible, ie. no folders syncing. + */ + void slotScheduleAppRestart(); + private slots: // slot to take the next folder from queue and start syncing. void slotStartScheduledFolderSync(); @@ -176,6 +181,9 @@ private: QString getBackupName( QString fullPathName ) const; void registerFolderMonitor( Folder *folder ); + // restarts the application (Linux only) + void restartApplication(); + QString unescapeAlias( const QString& ) const; QSet _disabledFolders; @@ -196,6 +204,8 @@ private: /** When the timer expires one of the scheduled syncs will be started. */ QTimer _startScheduledSyncTimer; + bool _appRestartRequired; + static FolderMan *_instance; explicit FolderMan(QObject *parent = 0); friend class OCC::Application; diff --git a/src/gui/main.cpp b/src/gui/main.cpp index c1f13fe8e..49cd4af40 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -59,6 +59,10 @@ int main(int argc, char **argv) app.showHelp(); return 0; } + if( app.versionOnly() ) { + app.showVersion(); + return 0; + } // check a environment variable for core dumps #ifdef Q_OS_UNIX diff --git a/src/libsync/utility.cpp b/src/libsync/utility.cpp index bd818ceb6..91963071b 100644 --- a/src/libsync/utility.cpp +++ b/src/libsync/utility.cpp @@ -393,6 +393,35 @@ void Utility::crash() *a = 1; } +// read the output of the owncloud --version command from the owncloud +// version that is on disk. This works for most versions of the client, +// because clients that do not yet know the --version flag return the +// version in the first line of the help output :-) +// +// This version only delivers output on linux, as Mac and Win get their +// restarting from the installer. +QByteArray Utility::versionOfInstalledBinary( const QString& command ) +{ + QByteArray re; + if( isLinux() ) { + QString binary(command); + if( binary.isEmpty() ) { + binary = qApp->arguments()[0]; + } + QStringList params; + params << QLatin1String("--version"); + QProcess process; + process.start(binary, params); + process.waitForFinished(); // sets current thread to sleep and waits for pingProcess end + re = process.readAllStandardOutput(); + int newline = re.indexOf(QChar('\n')); + if( newline > 0 ) { + re.truncate( newline ); + } + } + return re; +} + static const char STOPWATCH_END_TAG[] = "_STOPWATCH_END"; void Utility::StopWatch::start() diff --git a/src/libsync/utility.h b/src/libsync/utility.h index 957f5e215..40e7961cd 100644 --- a/src/libsync/utility.h +++ b/src/libsync/utility.h @@ -98,6 +98,13 @@ namespace Utility // if false, the two cases are two different files. OWNCLOUDSYNC_EXPORT bool fsCasePreserving(); + // Call the given command with the switch --version and retrun the first line + // of the output. + // If command is empty, the function calls the running application which, on + // Linux, might have changed while this one is running. + // For Mac and Windows, it returns QString() + OWNCLOUDSYNC_EXPORT QByteArray versionOfInstalledBinary(const QString& command = QString() ); + class OWNCLOUDSYNC_EXPORT StopWatch { private: QHash _lapTimes; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5d817b42c..9a1cb2fd7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,6 +26,8 @@ if( UNIX AND NOT APPLE ) owncloud_add_test(InotifyWatcher "${FolderWatcher_SRC}") endif(UNIX AND NOT APPLE) +configure_file(oc_bin.h.in oc_bin.h) + owncloud_add_test(CSyncSqlite "") owncloud_add_test(NetrcParser ../src/cmd/netrcparser.cpp) owncloud_add_test(OwnSql "") diff --git a/test/oc_bin.h.in b/test/oc_bin.h.in new file mode 100644 index 000000000..4af6176ea --- /dev/null +++ b/test/oc_bin.h.in @@ -0,0 +1,3 @@ + +#define OWNCLOUD_BIN "@CMAKE_BINARY_DIR@/bin/owncloud" + diff --git a/test/testutility.h b/test/testutility.h index 16b12499c..db3045d92 100644 --- a/test/testutility.h +++ b/test/testutility.h @@ -11,6 +11,8 @@ #include "utility.h" +#include "oc_bin.h" + using namespace OCC::Utility; class TestUtility : public QObject @@ -100,6 +102,20 @@ private slots: } + + void testVersionOfInstalledBinary() + { + if( isLinux() ) { + QString ver = versionOfInstalledBinary(OWNCLOUD_BIN); + qDebug() << "Version of installed ownCloud Binary: " << ver; + QVERIFY( !ver.isEmpty()); + + QRegExp rx( "ownCloud version \\d+\\.\\d+\\.\\d+.+" ); + QVERIFY( rx.exactMatch(ver)); + } else { + QVERIFY( versionOfInstalledBinary().isEmpty()); + } + } }; #endif