Add high-level abstraction for launching Syncthing
Add new SyncthingLauncher class which lauches Syncthing under the hood via external SyncthingProcess or using libsyncthing. Note: Launching via libsyncthing is still experimental.
This commit is contained in:
parent
278ba521d9
commit
0ceb8d5e79
|
@ -29,9 +29,9 @@ SyncthingNotifier::SyncthingNotifier(const SyncthingConnection &connection, QObj
|
|||
: QObject(parent)
|
||||
, m_connection(connection)
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
, m_service(syncthingService())
|
||||
, m_service(SyncthingService::mainInstance())
|
||||
#endif
|
||||
, m_process(syncthingProcess())
|
||||
, m_process(SyncthingProcess::mainInstance())
|
||||
, m_enabledNotifications(SyncthingHighLevelNotification::None)
|
||||
, m_previousStatus(SyncthingStatus::Disconnected)
|
||||
, m_ignoreInavailabilityAfterStart(15)
|
||||
|
@ -74,22 +74,22 @@ bool SyncthingNotifier::isDisconnectRelevant() const
|
|||
}
|
||||
|
||||
// consider process/launcher or systemd unit status
|
||||
if (m_process.isManuallyStopped()) {
|
||||
if (m_process && m_process->isManuallyStopped()) {
|
||||
return false;
|
||||
}
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
const SyncthingService &service(syncthingService());
|
||||
if (m_service.isManuallyStopped()) {
|
||||
if (m_service && m_service->isManuallyStopped()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ignore inavailability after start or standby-wakeup
|
||||
if (m_ignoreInavailabilityAfterStart) {
|
||||
if (m_process.isRunning()
|
||||
if ((m_process && m_process->isRunning())
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
&& ((m_service.isSystemdAvailable() && !service.isActiveWithoutSleepFor(m_process.activeSince(), m_ignoreInavailabilityAfterStart))
|
||||
|| !m_process.isActiveFor(m_ignoreInavailabilityAfterStart))
|
||||
&& ((m_service && m_service->isSystemdAvailable()
|
||||
&& !m_service->isActiveWithoutSleepFor(m_process->activeSince(), m_ignoreInavailabilityAfterStart))
|
||||
|| !m_process->isActiveFor(m_ignoreInavailabilityAfterStart))
|
||||
#else
|
||||
&& !m_process.isActiveFor(m_ignoreInavailabilityAfterStart)
|
||||
#endif
|
||||
|
@ -97,7 +97,7 @@ bool SyncthingNotifier::isDisconnectRelevant() const
|
|||
return false;
|
||||
}
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
if (m_service.isRunning() && !m_service.isActiveWithoutSleepFor(m_ignoreInavailabilityAfterStart)) {
|
||||
if (m_service->isRunning() && !m_service->isActiveWithoutSleepFor(m_ignoreInavailabilityAfterStart)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -81,9 +81,9 @@ private:
|
|||
|
||||
const SyncthingConnection &m_connection;
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
const SyncthingService &m_service;
|
||||
const SyncthingService *const m_service;
|
||||
#endif
|
||||
const SyncthingProcess &m_process;
|
||||
const SyncthingProcess *const m_process;
|
||||
SyncthingHighLevelNotification m_enabledNotifications;
|
||||
SyncthingStatus m_previousStatus;
|
||||
unsigned int m_ignoreInavailabilityAfterStart;
|
||||
|
|
|
@ -6,6 +6,8 @@ using namespace ChronoUtilities;
|
|||
|
||||
namespace Data {
|
||||
|
||||
SyncthingProcess *SyncthingProcess::s_mainInstance = nullptr;
|
||||
|
||||
SyncthingProcess::SyncthingProcess(QObject *parent)
|
||||
: QProcess(parent)
|
||||
, m_manuallyStopped(false)
|
||||
|
@ -89,10 +91,4 @@ void SyncthingProcess::killToRestart()
|
|||
}
|
||||
}
|
||||
|
||||
SyncthingProcess &syncthingProcess()
|
||||
{
|
||||
static SyncthingProcess process;
|
||||
return process;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -22,6 +22,8 @@ public:
|
|||
ChronoUtilities::DateTime activeSince() const;
|
||||
bool isActiveFor(unsigned int atLeastSeconds) const;
|
||||
bool isManuallyStopped() const;
|
||||
static SyncthingProcess *mainInstance();
|
||||
static void setMainInstance(SyncthingProcess *mainInstance);
|
||||
|
||||
Q_SIGNALS:
|
||||
void confirmKill();
|
||||
|
@ -42,6 +44,7 @@ private:
|
|||
ChronoUtilities::DateTime m_activeSince;
|
||||
QTimer m_killTimer;
|
||||
bool m_manuallyStopped;
|
||||
static SyncthingProcess *s_mainInstance;
|
||||
};
|
||||
|
||||
inline bool SyncthingProcess::isRunning() const
|
||||
|
@ -64,7 +67,15 @@ inline bool SyncthingProcess::isManuallyStopped() const
|
|||
return m_manuallyStopped;
|
||||
}
|
||||
|
||||
SyncthingProcess LIB_SYNCTHING_CONNECTOR_EXPORT &syncthingProcess();
|
||||
inline SyncthingProcess *SyncthingProcess::mainInstance()
|
||||
{
|
||||
return s_mainInstance;
|
||||
}
|
||||
|
||||
inline void SyncthingProcess::setMainInstance(SyncthingProcess *mainInstance)
|
||||
{
|
||||
s_mainInstance = mainInstance;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ constexpr DateTime dateTimeFromSystemdTimeStamp(qulonglong timeStamp)
|
|||
return DateTime(DateTime::unixEpochStart().totalTicks() + timeStamp * 10);
|
||||
}
|
||||
|
||||
SyncthingService *SyncthingService::s_mainInstance = nullptr;
|
||||
OrgFreedesktopSystemd1ManagerInterface *SyncthingService::s_manager = nullptr;
|
||||
OrgFreedesktopLogin1ManagerInterface *SyncthingService::s_loginManager = nullptr;
|
||||
DateTime SyncthingService::s_lastWakeUp = DateTime();
|
||||
|
@ -324,12 +325,6 @@ void SyncthingService::setProperties(
|
|||
}
|
||||
}
|
||||
|
||||
SyncthingService &syncthingService()
|
||||
{
|
||||
static SyncthingService service;
|
||||
return service;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
#endif
|
||||
|
|
|
@ -63,6 +63,8 @@ public:
|
|||
bool isRunning() const;
|
||||
bool isEnabled() const;
|
||||
bool isManuallyStopped() const;
|
||||
static SyncthingService *mainInstance();
|
||||
static void setMainInstance(SyncthingService *mainInstance);
|
||||
|
||||
public Q_SLOTS:
|
||||
void setUnitName(const QString &unitName);
|
||||
|
@ -110,6 +112,7 @@ private:
|
|||
static OrgFreedesktopLogin1ManagerInterface *s_loginManager;
|
||||
static bool s_fallingAsleep;
|
||||
static ChronoUtilities::DateTime s_lastWakeUp;
|
||||
static SyncthingService *s_mainInstance;
|
||||
QString m_unitName;
|
||||
QDBusServiceWatcher *m_serviceWatcher;
|
||||
OrgFreedesktopSystemd1UnitInterface *m_unit;
|
||||
|
@ -209,7 +212,15 @@ inline void SyncthingService::disable()
|
|||
setEnabled(false);
|
||||
}
|
||||
|
||||
SyncthingService &syncthingService();
|
||||
inline SyncthingService *SyncthingService::mainInstance()
|
||||
{
|
||||
return s_mainInstance;
|
||||
}
|
||||
|
||||
inline void SyncthingService::setMainInstance(SyncthingService *mainInstance)
|
||||
{
|
||||
s_mainInstance = mainInstance;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
|
|
|
@ -67,6 +67,9 @@ SyncthingApplet::~SyncthingApplet()
|
|||
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
|
||||
delete m_webViewDlg;
|
||||
#endif
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
SyncthingService::setMainInstance(nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SyncthingApplet::init()
|
||||
|
@ -99,10 +102,10 @@ void SyncthingApplet::init()
|
|||
|
||||
// initialize systemd service support
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
SyncthingService &service = syncthingService();
|
||||
service.setUnitName(Settings::values().systemd.syncthingUnit);
|
||||
connect(&service, &SyncthingService::systemdAvailableChanged, this, &SyncthingApplet::handleSystemdStatusChanged);
|
||||
connect(&service, &SyncthingService::errorOccurred, this, &SyncthingApplet::handleSystemdServiceError);
|
||||
SyncthingService::setMainInstance(&m_service);
|
||||
m_service.setUnitName(Settings::values().systemd.syncthingUnit);
|
||||
connect(&m_service, &SyncthingService::systemdAvailableChanged, this, &SyncthingApplet::handleSystemdStatusChanged);
|
||||
connect(&m_service, &SyncthingService::errorOccurred, this, &SyncthingApplet::handleSystemdServiceError);
|
||||
#endif
|
||||
|
||||
m_initialized = true;
|
||||
|
|
|
@ -141,6 +141,9 @@ private:
|
|||
Dialogs::AboutDialog *m_aboutDlg;
|
||||
Data::SyncthingConnection m_connection;
|
||||
Data::SyncthingNotifier m_notifier;
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
Data::SyncthingService m_service;
|
||||
#endif
|
||||
QtGui::StatusInfo m_statusInfo;
|
||||
Data::SyncthingDirectoryModel m_dirModel;
|
||||
Data::SyncthingDeviceModel m_devModel;
|
||||
|
@ -179,7 +182,7 @@ inline Data::SyncthingDownloadModel *SyncthingApplet::downloadModel() const
|
|||
inline Data::SyncthingService *SyncthingApplet::service() const
|
||||
{
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
return &Data::syncthingService();
|
||||
return const_cast<Data::SyncthingService *>(&m_service);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "../gui/trayicon.h"
|
||||
#include "../gui/traywidget.h"
|
||||
|
||||
#include "../../widgets/misc/syncthinglauncher.h"
|
||||
#include "../../widgets/settings/settings.h"
|
||||
|
||||
#include "../../connector/syncthingprocess.h"
|
||||
|
@ -161,8 +162,11 @@ int runApplication(int argc, const char *const *argv)
|
|||
Settings::values().qt.apply();
|
||||
qtConfigArgs.applySettings(true);
|
||||
LOAD_QT_TRANSLATIONS;
|
||||
SyncthingLauncher launcher;
|
||||
SyncthingLauncher::setMainInstance(&launcher);
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
SyncthingService &service = syncthingService();
|
||||
SyncthingService service;
|
||||
SyncthingService::setMainInstance(&service);
|
||||
service.setUnitName(Settings::values().systemd.syncthingUnit);
|
||||
QObject::connect(&service, &SyncthingService::errorOccurred, &handleSystemdServiceError);
|
||||
#endif
|
||||
|
|
|
@ -157,10 +157,11 @@ TrayWidget::TrayWidget(const QString &connectionConfig, TrayMenu *parent)
|
|||
connect(m_ui->actionShowNotifications, &QAction::triggered, this, &TrayWidget::showNotifications);
|
||||
connect(m_ui->actionDismissNotifications, &QAction::triggered, this, &TrayWidget::dismissNotifications);
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
const SyncthingService &service = syncthingService();
|
||||
connect(m_ui->startStopPushButton, &QPushButton::clicked, &service, &SyncthingService::toggleRunning);
|
||||
connect(&service, &SyncthingService::systemdAvailableChanged, this, &TrayWidget::handleSystemdStatusChanged);
|
||||
connect(&service, &SyncthingService::stateChanged, this, &TrayWidget::handleSystemdStatusChanged);
|
||||
if (const auto *const service = SyncthingService::mainInstance()) {
|
||||
connect(m_ui->startStopPushButton, &QPushButton::clicked, service, &SyncthingService::toggleRunning);
|
||||
connect(service, &SyncthingService::systemdAvailableChanged, this, &TrayWidget::handleSystemdStatusChanged);
|
||||
connect(service, &SyncthingService::stateChanged, this, &TrayWidget::handleSystemdStatusChanged);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -508,22 +509,19 @@ bool TrayWidget::applySystemdSettings(bool reconnectRequired)
|
|||
bool isServiceRelevant, isServiceRunning;
|
||||
tie(isServiceRelevant, isServiceRunning) = systemdSettings.apply(m_connection, m_selectedConnection, reconnectRequired);
|
||||
|
||||
if (isServiceRelevant) {
|
||||
// update start/stop button
|
||||
if (systemdSettings.showButton) {
|
||||
m_ui->startStopPushButton->setVisible(true);
|
||||
const auto &unitName(syncthingService().unitName());
|
||||
if (isServiceRunning) {
|
||||
m_ui->startStopPushButton->setText(tr("Stop"));
|
||||
m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user stop ") + unitName);
|
||||
m_ui->startStopPushButton->setIcon(
|
||||
QIcon::fromTheme(QStringLiteral("process-stop"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/process-stop.svg"))));
|
||||
} else {
|
||||
m_ui->startStopPushButton->setText(tr("Start"));
|
||||
m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user start ") + unitName);
|
||||
m_ui->startStopPushButton->setIcon(
|
||||
QIcon::fromTheme(QStringLiteral("system-run"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/system-run.svg"))));
|
||||
}
|
||||
// update start/stop button
|
||||
if (isServiceRelevant && systemdSettings.showButton) {
|
||||
m_ui->startStopPushButton->setVisible(true);
|
||||
if (isServiceRunning) {
|
||||
m_ui->startStopPushButton->setText(tr("Stop"));
|
||||
m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user stop ") + systemdSettings.syncthingUnit);
|
||||
m_ui->startStopPushButton->setIcon(
|
||||
QIcon::fromTheme(QStringLiteral("process-stop"), QIcon(QStringLiteral(":/icons/hicolor/scalable/actions/process-stop.svg"))));
|
||||
} else {
|
||||
m_ui->startStopPushButton->setText(tr("Start"));
|
||||
m_ui->startStopPushButton->setToolTip(QStringLiteral("systemctl --user start ") + systemdSettings.syncthingUnit);
|
||||
m_ui->startStopPushButton->setIcon(
|
||||
QIcon::fromTheme(QStringLiteral("system-run"), QIcon(QStringLiteral(":/icons/hicolor/scalable/apps/system-run.svg"))));
|
||||
}
|
||||
}
|
||||
if (!systemdSettings.showButton || !isServiceRelevant) {
|
||||
|
@ -534,7 +532,8 @@ bool TrayWidget::applySystemdSettings(bool reconnectRequired)
|
|||
|
||||
void TrayWidget::connectIfServiceRunning()
|
||||
{
|
||||
if (Settings::values().systemd.considerForReconnect && m_connection.isLocal() && syncthingService().isRunning()) {
|
||||
const auto *const service(SyncthingService::mainInstance());
|
||||
if (Settings::values().systemd.considerForReconnect && m_connection.isLocal() && service && service->isRunning()) {
|
||||
m_connection.connect();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ set(WIDGETS_HEADER_FILES
|
|||
misc/dbusstatusnotifier.h
|
||||
misc/internalerror.h
|
||||
misc/otherdialogs.h
|
||||
misc/syncthinglauncher.h
|
||||
misc/syncthingkiller.h
|
||||
)
|
||||
set(WIDGETS_SRC_FILES
|
||||
|
@ -36,6 +37,7 @@ set(WIDGETS_SRC_FILES
|
|||
misc/dbusstatusnotifier.cpp
|
||||
misc/internalerror.cpp
|
||||
misc/otherdialogs.cpp
|
||||
misc/syncthinglauncher.cpp
|
||||
misc/syncthingkiller.cpp
|
||||
)
|
||||
set(RES_FILES
|
||||
|
@ -88,6 +90,17 @@ use_syncthingconnector()
|
|||
find_package(syncthingmodel ${META_APP_VERSION} REQUIRED)
|
||||
use_syncthingmodel()
|
||||
|
||||
option(USE_LIBSYNCTHING "whether libsyncthing should be included for the launcher" OFF)
|
||||
if(USE_LIBSYNCTHING)
|
||||
find_package(syncthing ${META_APP_VERSION} REQUIRED)
|
||||
use_syncthing()
|
||||
set_source_files_properties(
|
||||
misc/syncthinglauncher.cpp
|
||||
PROPERTIES COMPILE_DEFINITIONS SYNCTHING_WIDGETS_USE_LIBSYNCTHING
|
||||
)
|
||||
list(APPEND ADDITIONAL_QT_MODULES Concurrent)
|
||||
endif()
|
||||
|
||||
# link also explicitely against the following Qt 5 modules
|
||||
list(APPEND ADDITIONAL_QT_MODULES Network)
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#include "./internalerror.h"
|
||||
#include "./syncthinglauncher.h"
|
||||
|
||||
#include "../settings/settings.h"
|
||||
|
||||
#include "../../connector/syncthingconnection.h"
|
||||
#include "../../connector/syncthingprocess.h"
|
||||
#include "../../connector/syncthingservice.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
@ -37,23 +37,24 @@ bool InternalError::isRelevant(const SyncthingConnection &connection, SyncthingE
|
|||
// consider process/launcher or systemd unit status
|
||||
const auto remoteHostClosed(networkError == QNetworkReply::RemoteHostClosedError);
|
||||
// ignore "remote host closed" error if we've just stopped Syncthing ourselves
|
||||
const SyncthingProcess &process(syncthingProcess());
|
||||
if (settings.launcher.considerForReconnect && remoteHostClosed && process.isManuallyStopped()) {
|
||||
const auto *launcher(SyncthingLauncher::mainInstance());
|
||||
if (settings.launcher.considerForReconnect && remoteHostClosed && launcher && launcher->isManuallyStopped()) {
|
||||
return false;
|
||||
}
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
const SyncthingService &service(syncthingService());
|
||||
if (settings.systemd.considerForReconnect && remoteHostClosed && service.isManuallyStopped()) {
|
||||
const auto *const service(SyncthingService::mainInstance());
|
||||
if (settings.systemd.considerForReconnect && remoteHostClosed && service && service->isManuallyStopped()) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ignore inavailability after start or standby-wakeup
|
||||
if (settings.ignoreInavailabilityAfterStart && networkError == QNetworkReply::ConnectionRefusedError) {
|
||||
if (process.isRunning()
|
||||
if ((launcher && launcher->isRunning())
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
&& ((service.isSystemdAvailable() && !service.isActiveWithoutSleepFor(process.activeSince(), settings.ignoreInavailabilityAfterStart))
|
||||
|| !process.isActiveFor(settings.ignoreInavailabilityAfterStart))
|
||||
&& ((service && service->isSystemdAvailable()
|
||||
&& !service->isActiveWithoutSleepFor(launcher->activeSince(), settings.ignoreInavailabilityAfterStart))
|
||||
|| !launcher->isActiveFor(settings.ignoreInavailabilityAfterStart))
|
||||
#else
|
||||
&& !process.isActiveFor(settings.ignoreInavailabilityAfterStart)
|
||||
#endif
|
||||
|
@ -61,7 +62,7 @@ bool InternalError::isRelevant(const SyncthingConnection &connection, SyncthingE
|
|||
return false;
|
||||
}
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
if (service.isRunning() && !service.isActiveWithoutSleepFor(settings.ignoreInavailabilityAfterStart)) {
|
||||
if (service && !service->isActiveWithoutSleepFor(settings.ignoreInavailabilityAfterStart)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
#include "./syncthinglauncher.h"
|
||||
|
||||
#ifdef SYNCTHING_WIDGETS_USE_LIBSYNCTHING
|
||||
#include "../../libsyncthing/interface.h"
|
||||
#include <QtConcurrentRun>
|
||||
#endif
|
||||
|
||||
using namespace ChronoUtilities;
|
||||
|
||||
namespace Data {
|
||||
|
||||
SyncthingLauncher *SyncthingLauncher::s_mainInstance = nullptr;
|
||||
|
||||
SyncthingLauncher::SyncthingLauncher(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
connect(&m_process, &SyncthingProcess::readyRead, this, &SyncthingLauncher::handleProcessReadyRead);
|
||||
connect(&m_process, static_cast<void (SyncthingProcess::*)(int exitCode, QProcess::ExitStatus exitStatus)>(&SyncthingProcess::finished), this,
|
||||
&SyncthingLauncher::handleProcessFinished);
|
||||
connect(&m_process, &SyncthingProcess::confirmKill, this, &SyncthingLauncher::confirmKill);
|
||||
}
|
||||
|
||||
bool SyncthingLauncher::isLibSyncthingAvailable()
|
||||
{
|
||||
#ifdef SYNCTHING_WIDGETS_USE_LIBSYNCTHING
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SyncthingLauncher::launch(const QString &cmd)
|
||||
{
|
||||
if (isRunning()) {
|
||||
return;
|
||||
}
|
||||
m_manuallyStopped = false;
|
||||
m_process.startSyncthing(cmd);
|
||||
}
|
||||
|
||||
void SyncthingLauncher::launch(const LibSyncthing::RuntimeOptions &runtimeOptions)
|
||||
{
|
||||
if (isRunning()) {
|
||||
return;
|
||||
}
|
||||
m_manuallyStopped = false;
|
||||
#ifdef SYNCTHING_WIDGETS_USE_LIBSYNCTHING
|
||||
m_future = QtConcurrent::run(this, &SyncthingLauncher::runLibSyncthing, runtimeOptions);
|
||||
#else
|
||||
VAR_UNUSED(runtimeOptions)
|
||||
emit outputAvailable("libsyncthing support not enabled");
|
||||
emit exited(-1, QProcess::CrashExit);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SyncthingLauncher::terminate()
|
||||
{
|
||||
if (m_process.isRunning()) {
|
||||
m_manuallyStopped = true;
|
||||
m_process.stopSyncthing();
|
||||
} else if (m_future.isRunning()) {
|
||||
m_manuallyStopped = true;
|
||||
m_future.cancel(); // FIXME: this will not work of course
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingLauncher::kill()
|
||||
{
|
||||
if (m_process.isRunning()) {
|
||||
m_manuallyStopped = true;
|
||||
m_process.stopSyncthing();
|
||||
} else if (m_future.isRunning()) {
|
||||
m_manuallyStopped = true;
|
||||
// FIXME
|
||||
}
|
||||
}
|
||||
|
||||
void SyncthingLauncher::handleProcessReadyRead()
|
||||
{
|
||||
emit outputAvailable(m_process.readAll());
|
||||
}
|
||||
|
||||
void SyncthingLauncher::handleProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
emit runningChanged(false);
|
||||
emit exited(exitCode, exitStatus);
|
||||
}
|
||||
|
||||
void SyncthingLauncher::runLibSyncthing(const LibSyncthing::RuntimeOptions &runtimeOptions)
|
||||
{
|
||||
LibSyncthing::runSyncthing(runtimeOptions);
|
||||
}
|
||||
|
||||
SyncthingLauncher &syncthingLauncher()
|
||||
{
|
||||
static SyncthingLauncher launcher;
|
||||
return launcher;
|
||||
}
|
||||
|
||||
} // namespace Data
|
|
@ -0,0 +1,100 @@
|
|||
#ifndef SYNCTHINGWIDGETS_SYNCTHINGLAUNCHER_H
|
||||
#define SYNCTHINGWIDGETS_SYNCTHINGLAUNCHER_H
|
||||
|
||||
#include "../global.h"
|
||||
|
||||
#include "../../connector/syncthingprocess.h"
|
||||
|
||||
#include <QFuture>
|
||||
|
||||
namespace LibSyncthing {
|
||||
struct RuntimeOptions;
|
||||
}
|
||||
|
||||
namespace Data {
|
||||
|
||||
class SYNCTHINGWIDGETS_EXPORT SyncthingLauncher : public QObject {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged)
|
||||
Q_PROPERTY(ChronoUtilities::DateTime activeSince READ activeSince)
|
||||
Q_PROPERTY(bool manuallyStopped READ isManuallyStopped)
|
||||
|
||||
public:
|
||||
explicit SyncthingLauncher(QObject *parent = nullptr);
|
||||
|
||||
bool isRunning() const;
|
||||
ChronoUtilities::DateTime activeSince() const;
|
||||
bool isActiveFor(unsigned int atLeastSeconds) const;
|
||||
bool isManuallyStopped() const;
|
||||
static bool isLibSyncthingAvailable();
|
||||
static SyncthingLauncher *mainInstance();
|
||||
static void setMainInstance(SyncthingLauncher *mainInstance);
|
||||
|
||||
Q_SIGNALS:
|
||||
void confirmKill();
|
||||
void runningChanged(bool isRunning);
|
||||
void outputAvailable(const QByteArray &data);
|
||||
void exited(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
public Q_SLOTS:
|
||||
void launch(const QString &cmd);
|
||||
void launch(const LibSyncthing::RuntimeOptions &runtimeOptions);
|
||||
void terminate();
|
||||
void kill();
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleProcessReadyRead();
|
||||
void handleProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void runLibSyncthing(const LibSyncthing::RuntimeOptions &runtimeOptions);
|
||||
|
||||
private:
|
||||
SyncthingProcess m_process;
|
||||
QFuture<void> m_future;
|
||||
ChronoUtilities::DateTime m_futureStarted;
|
||||
bool m_manuallyStopped;
|
||||
static SyncthingLauncher *s_mainInstance;
|
||||
};
|
||||
|
||||
inline bool SyncthingLauncher::isRunning() const
|
||||
{
|
||||
return m_process.isRunning() || m_future.isRunning();
|
||||
}
|
||||
|
||||
inline ChronoUtilities::DateTime SyncthingLauncher::activeSince() const
|
||||
{
|
||||
if (m_process.isRunning()) {
|
||||
return m_process.activeSince();
|
||||
} else if (m_future.isRunning()) {
|
||||
return m_futureStarted;
|
||||
}
|
||||
return ChronoUtilities::DateTime();
|
||||
}
|
||||
|
||||
inline bool SyncthingLauncher::isActiveFor(unsigned int atLeastSeconds) const
|
||||
{
|
||||
const auto activeSince(this->activeSince());
|
||||
return !activeSince.isNull() && (ChronoUtilities::DateTime::gmtNow() - activeSince).totalSeconds() > atLeastSeconds;
|
||||
}
|
||||
|
||||
inline bool SyncthingLauncher::isManuallyStopped() const
|
||||
{
|
||||
return m_manuallyStopped;
|
||||
}
|
||||
|
||||
inline SyncthingLauncher *SyncthingLauncher::mainInstance()
|
||||
{
|
||||
return s_mainInstance;
|
||||
}
|
||||
|
||||
inline void SyncthingLauncher::setMainInstance(SyncthingLauncher *mainInstance)
|
||||
{
|
||||
if ((s_mainInstance = mainInstance) && !SyncthingProcess::mainInstance()) {
|
||||
SyncthingProcess::setMainInstance(&mainInstance->m_process);
|
||||
}
|
||||
}
|
||||
|
||||
SyncthingLauncher SYNCTHINGWIDGETS_EXPORT &syncthingLauncher();
|
||||
|
||||
} // namespace Data
|
||||
|
||||
#endif // SYNCTHINGWIDGETS_SYNCTHINGLAUNCHER_H
|
|
@ -1,7 +1,10 @@
|
|||
#include "./settings.h"
|
||||
|
||||
#include "../misc/syncthingkiller.h"
|
||||
#include "../misc/syncthinglauncher.h"
|
||||
|
||||
#include "../../connector/syncthingnotifier.h"
|
||||
#include "../../connector/syncthingprocess.h"
|
||||
#include "../misc/syncthingkiller.h"
|
||||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
#include "../../connector/syncthingconnection.h"
|
||||
#include "../../connector/syncthingservice.h"
|
||||
|
@ -72,7 +75,9 @@ std::vector<SyncthingProcess *> Launcher::allProcesses()
|
|||
{
|
||||
vector<SyncthingProcess *> processes;
|
||||
processes.reserve(1 + toolProcesses.size());
|
||||
processes.push_back(&syncthingProcess());
|
||||
if (auto *const syncthingProcess = SyncthingProcess::mainInstance()) {
|
||||
processes.push_back(syncthingProcess);
|
||||
}
|
||||
for (auto &process : toolProcesses) {
|
||||
processes.push_back(&process.second);
|
||||
}
|
||||
|
@ -84,8 +89,10 @@ std::vector<SyncthingProcess *> Launcher::allProcesses()
|
|||
*/
|
||||
void Launcher::autostart() const
|
||||
{
|
||||
if (enabled && !syncthingPath.isEmpty()) {
|
||||
syncthingProcess().startSyncthing(syncthingCmd());
|
||||
auto *const launcher(SyncthingLauncher::mainInstance());
|
||||
// TODO: allow using libsyncthing
|
||||
if (enabled && !syncthingPath.isEmpty() && launcher) {
|
||||
launcher->launch(syncthingCmd());
|
||||
}
|
||||
for (auto i = tools.cbegin(), end = tools.cend(); i != end; ++i) {
|
||||
const ToolParameter &toolParams = i.value();
|
||||
|
@ -338,9 +345,12 @@ void Settings::apply(SyncthingNotifier ¬ifier) const
|
|||
std::tuple<bool, bool> Systemd::apply(
|
||||
Data::SyncthingConnection &connection, const SyncthingConnectionSettings *currentConnectionSettings, bool reconnectRequired) const
|
||||
{
|
||||
const SyncthingService &service(syncthingService());
|
||||
const auto isRelevant = service.isSystemdAvailable() && connection.isLocal();
|
||||
const auto isRunning = service.isRunning();
|
||||
auto *const service(SyncthingService::mainInstance());
|
||||
if (!service) {
|
||||
return make_tuple(false, false);
|
||||
}
|
||||
const auto isRelevant = service->isSystemdAvailable() && connection.isLocal();
|
||||
const auto isRunning = service->isRunning();
|
||||
|
||||
if (currentConnectionSettings && (!considerForReconnect || !isRelevant || isRunning)) {
|
||||
// ensure auto-reconnect is configured according to settings
|
||||
|
@ -354,14 +364,14 @@ std::tuple<bool, bool> Systemd::apply(
|
|||
if (considerForReconnect && isRelevant) {
|
||||
constexpr auto minActiveTimeInSeconds(5);
|
||||
if (reconnectRequired) {
|
||||
if (service.isActiveWithoutSleepFor(minActiveTimeInSeconds)) {
|
||||
if (service->isActiveWithoutSleepFor(minActiveTimeInSeconds)) {
|
||||
connection.reconnect();
|
||||
} else {
|
||||
// give the service (which has just started) a few seconds to initialize
|
||||
connection.reconnectLater(minActiveTimeInSeconds * 1000);
|
||||
}
|
||||
} else if (isRunning && !connection.isConnected()) {
|
||||
if (service.isActiveWithoutSleepFor(minActiveTimeInSeconds)) {
|
||||
if (service->isActiveWithoutSleepFor(minActiveTimeInSeconds)) {
|
||||
connection.connect();
|
||||
} else {
|
||||
// give the service (which has just started) a few seconds to initialize
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "./settingsdialog.h"
|
||||
|
||||
#include "../misc/syncthinglauncher.h"
|
||||
|
||||
#include "../../connector/syncthingconfig.h"
|
||||
#include "../../connector/syncthingconnection.h"
|
||||
#include "../../connector/syncthingprocess.h"
|
||||
|
@ -606,14 +608,16 @@ void AutostartOptionPage::reset()
|
|||
// LauncherOptionPage
|
||||
LauncherOptionPage::LauncherOptionPage(QWidget *parentWidget)
|
||||
: LauncherOptionPageBase(parentWidget)
|
||||
, m_process(syncthingProcess())
|
||||
, m_process(nullptr)
|
||||
, m_launcher(SyncthingLauncher::mainInstance())
|
||||
, m_kill(false)
|
||||
{
|
||||
}
|
||||
|
||||
LauncherOptionPage::LauncherOptionPage(const QString &tool, QWidget *parentWidget)
|
||||
: LauncherOptionPageBase(parentWidget)
|
||||
, m_process(Launcher::toolProcess(tool))
|
||||
, m_process(&Launcher::toolProcess(tool))
|
||||
, m_launcher(nullptr)
|
||||
, m_kill(false)
|
||||
, m_tool(tool)
|
||||
{
|
||||
|
@ -641,14 +645,20 @@ QWidget *LauncherOptionPage::setupWidget()
|
|||
// setup other widgets
|
||||
ui()->syncthingPathSelection->provideCustomFileMode(QFileDialog::ExistingFile);
|
||||
ui()->logTextEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
const bool running = m_process.state() != QProcess::NotRunning;
|
||||
const auto running(isRunning());
|
||||
ui()->launchNowPushButton->setHidden(running);
|
||||
ui()->stopPushButton->setHidden(!running);
|
||||
// connect signals & slots
|
||||
m_connections << QObject::connect(&m_process, &SyncthingProcess::readyRead, bind(&LauncherOptionPage::handleSyncthingReadyRead, this));
|
||||
m_connections << QObject::connect(&m_process,
|
||||
static_cast<void (SyncthingProcess::*)(int exitCode, QProcess::ExitStatus exitStatus)>(&SyncthingProcess::finished),
|
||||
bind(&LauncherOptionPage::handleSyncthingExited, this, _1, _2));
|
||||
if (m_process) {
|
||||
m_connections << QObject::connect(m_process, &SyncthingProcess::readyRead, bind(&LauncherOptionPage::handleSyncthingReadyRead, this));
|
||||
m_connections << QObject::connect(m_process,
|
||||
static_cast<void (SyncthingProcess::*)(int exitCode, QProcess::ExitStatus exitStatus)>(&SyncthingProcess::finished),
|
||||
bind(&LauncherOptionPage::handleSyncthingExited, this, _1, _2));
|
||||
} else if (m_launcher) {
|
||||
m_connections << QObject::connect(
|
||||
m_launcher, &SyncthingLauncher::outputAvailable, bind(&LauncherOptionPage::handleSyncthingOutputAvailable, this, _1));
|
||||
m_connections << QObject::connect(m_launcher, &SyncthingLauncher::exited, bind(&LauncherOptionPage::handleSyncthingExited, this, _1, _2));
|
||||
}
|
||||
QObject::connect(ui()->launchNowPushButton, &QPushButton::clicked, bind(&LauncherOptionPage::launch, this));
|
||||
QObject::connect(ui()->stopPushButton, &QPushButton::clicked, bind(&LauncherOptionPage::stop, this));
|
||||
return widget;
|
||||
|
@ -688,13 +698,18 @@ void LauncherOptionPage::reset()
|
|||
}
|
||||
|
||||
void LauncherOptionPage::handleSyncthingReadyRead()
|
||||
{
|
||||
handleSyncthingOutputAvailable(m_process->readAll());
|
||||
}
|
||||
|
||||
void LauncherOptionPage::handleSyncthingOutputAvailable(const QByteArray &output)
|
||||
{
|
||||
if (!hasBeenShown()) {
|
||||
return;
|
||||
}
|
||||
QTextCursor cursor = ui()->logTextEdit->textCursor();
|
||||
QTextCursor cursor(ui()->logTextEdit->textCursor());
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.insertText(QString::fromLocal8Bit(m_process.readAll()));
|
||||
cursor.insertText(QString::fromLocal8Bit(output));
|
||||
if (ui()->ensureCursorVisibleCheckBox->isChecked()) {
|
||||
ui()->logTextEdit->ensureCursorVisible();
|
||||
}
|
||||
|
@ -721,13 +736,18 @@ void LauncherOptionPage::handleSyncthingExited(int exitCode, QProcess::ExitStatu
|
|||
ui()->launchNowPushButton->show();
|
||||
}
|
||||
|
||||
bool LauncherOptionPage::isRunning() const
|
||||
{
|
||||
return (m_process && m_process->isRunning()) || (m_launcher && m_launcher->isRunning());
|
||||
}
|
||||
|
||||
void LauncherOptionPage::launch()
|
||||
{
|
||||
if (!hasBeenShown()) {
|
||||
return;
|
||||
}
|
||||
apply();
|
||||
if (m_process.state() != QProcess::NotRunning) {
|
||||
if (isRunning()) {
|
||||
return;
|
||||
}
|
||||
ui()->launchNowPushButton->hide();
|
||||
|
@ -735,23 +755,34 @@ void LauncherOptionPage::launch()
|
|||
ui()->stopPushButton->setText(QCoreApplication::translate("QtGui::LauncherOptionPage", "Stop launched instance"));
|
||||
m_kill = false;
|
||||
if (m_tool.isEmpty()) {
|
||||
m_process.startSyncthing(values().launcher.syncthingCmd());
|
||||
// TODO: allow using libsyncthing
|
||||
m_launcher->launch(values().launcher.syncthingCmd());
|
||||
} else {
|
||||
m_process.startSyncthing(values().launcher.toolCmd(m_tool));
|
||||
m_process->startSyncthing(values().launcher.toolCmd(m_tool));
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherOptionPage::stop()
|
||||
{
|
||||
if (!hasBeenShown() || m_process.state() == QProcess::NotRunning) {
|
||||
if (!hasBeenShown()) {
|
||||
return;
|
||||
}
|
||||
if (m_kill) {
|
||||
m_process.kill();
|
||||
if (m_process) {
|
||||
m_process->killSyncthing();
|
||||
}
|
||||
if (m_launcher) {
|
||||
m_launcher->kill();
|
||||
}
|
||||
} else {
|
||||
ui()->stopPushButton->setText(QCoreApplication::translate("QtGui::LauncherOptionPage", "Kill launched instance"));
|
||||
m_kill = true;
|
||||
m_process.terminate();
|
||||
if (m_process) {
|
||||
m_process->stopSyncthing();
|
||||
}
|
||||
if (m_launcher) {
|
||||
m_launcher->terminate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -759,7 +790,7 @@ void LauncherOptionPage::stop()
|
|||
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
|
||||
SystemdOptionPage::SystemdOptionPage(QWidget *parentWidget)
|
||||
: SystemdOptionPageBase(parentWidget)
|
||||
, m_service(syncthingService())
|
||||
, m_service(SyncthingService::mainInstance())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -770,14 +801,17 @@ SystemdOptionPage::~SystemdOptionPage()
|
|||
QWidget *SystemdOptionPage::setupWidget()
|
||||
{
|
||||
auto *const widget = SystemdOptionPageBase::setupWidget();
|
||||
QObject::connect(ui()->syncthingUnitLineEdit, &QLineEdit::textChanged, &m_service, &SyncthingService::setUnitName);
|
||||
QObject::connect(ui()->startPushButton, &QPushButton::clicked, &m_service, &SyncthingService::start);
|
||||
QObject::connect(ui()->stopPushButton, &QPushButton::clicked, &m_service, &SyncthingService::stop);
|
||||
QObject::connect(ui()->enablePushButton, &QPushButton::clicked, &m_service, &SyncthingService::enable);
|
||||
QObject::connect(ui()->disablePushButton, &QPushButton::clicked, &m_service, &SyncthingService::disable);
|
||||
QObject::connect(&m_service, &SyncthingService::descriptionChanged, bind(&SystemdOptionPage::handleDescriptionChanged, this, _1));
|
||||
QObject::connect(&m_service, &SyncthingService::stateChanged, bind(&SystemdOptionPage::handleStatusChanged, this, _1, _2, _3));
|
||||
QObject::connect(&m_service, &SyncthingService::unitFileStateChanged, bind(&SystemdOptionPage::handleEnabledChanged, this, _1));
|
||||
if (!m_service) {
|
||||
return widget;
|
||||
}
|
||||
QObject::connect(ui()->syncthingUnitLineEdit, &QLineEdit::textChanged, m_service, &SyncthingService::setUnitName);
|
||||
QObject::connect(ui()->startPushButton, &QPushButton::clicked, m_service, &SyncthingService::start);
|
||||
QObject::connect(ui()->stopPushButton, &QPushButton::clicked, m_service, &SyncthingService::stop);
|
||||
QObject::connect(ui()->enablePushButton, &QPushButton::clicked, m_service, &SyncthingService::enable);
|
||||
QObject::connect(ui()->disablePushButton, &QPushButton::clicked, m_service, &SyncthingService::disable);
|
||||
QObject::connect(m_service, &SyncthingService::descriptionChanged, bind(&SystemdOptionPage::handleDescriptionChanged, this, _1));
|
||||
QObject::connect(m_service, &SyncthingService::stateChanged, bind(&SystemdOptionPage::handleStatusChanged, this, _1, _2, _3));
|
||||
QObject::connect(m_service, &SyncthingService::unitFileStateChanged, bind(&SystemdOptionPage::handleEnabledChanged, this, _1));
|
||||
return widget;
|
||||
}
|
||||
|
||||
|
@ -796,9 +830,12 @@ void SystemdOptionPage::reset()
|
|||
ui()->syncthingUnitLineEdit->setText(settings.syncthingUnit);
|
||||
ui()->showButtonCheckBox->setChecked(settings.showButton);
|
||||
ui()->considerForReconnectCheckBox->setChecked(settings.considerForReconnect);
|
||||
handleDescriptionChanged(m_service.description());
|
||||
handleStatusChanged(m_service.activeState(), m_service.subState(), m_service.activeSince());
|
||||
handleEnabledChanged(m_service.unitFileState());
|
||||
if (!m_service) {
|
||||
return;
|
||||
}
|
||||
handleDescriptionChanged(m_service->description());
|
||||
handleStatusChanged(m_service->activeState(), m_service->subState(), m_service->activeSince());
|
||||
handleEnabledChanged(m_service->unitFileState());
|
||||
}
|
||||
|
||||
void SystemdOptionPage::handleDescriptionChanged(const QString &description)
|
||||
|
@ -823,7 +860,7 @@ void SystemdOptionPage::handleStatusChanged(const QString &activeState, const QS
|
|||
status << subState;
|
||||
}
|
||||
|
||||
const bool isRunning = m_service.isRunning();
|
||||
const bool isRunning = m_service && m_service->isRunning();
|
||||
QString timeStamp;
|
||||
if (isRunning && !activeSince.isNull()) {
|
||||
timeStamp = QLatin1Char('\n') % QCoreApplication::translate("QtGui::SystemdOptionPage", "since ")
|
||||
|
@ -841,7 +878,7 @@ void SystemdOptionPage::handleStatusChanged(const QString &activeState, const QS
|
|||
|
||||
void SystemdOptionPage::handleEnabledChanged(const QString &unitFileState)
|
||||
{
|
||||
const bool isEnabled = m_service.isEnabled();
|
||||
const bool isEnabled = m_service && m_service->isEnabled();
|
||||
ui()->unitFileStateValueLabel->setText(
|
||||
unitFileState.isEmpty() ? QCoreApplication::translate("QtGui::SystemdOptionPage", "unknown") : unitFileState);
|
||||
setIndicatorColor(
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace Data {
|
|||
class SyncthingConnection;
|
||||
class SyncthingService;
|
||||
class SyncthingProcess;
|
||||
class SyncthingLauncher;
|
||||
} // namespace Data
|
||||
|
||||
namespace QtGui {
|
||||
|
@ -79,10 +80,13 @@ LauncherOptionPage(const QString &tool, QWidget *parentWidget = nullptr);
|
|||
private:
|
||||
DECLARE_SETUP_WIDGETS
|
||||
void handleSyncthingReadyRead();
|
||||
void handleSyncthingOutputAvailable(const QByteArray &output);
|
||||
void handleSyncthingExited(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
bool isRunning() const;
|
||||
void launch();
|
||||
void stop();
|
||||
Data::SyncthingProcess &m_process;
|
||||
Data::SyncthingProcess *const m_process;
|
||||
Data::SyncthingLauncher *const m_launcher;
|
||||
QList<QMetaObject::Connection> m_connections;
|
||||
bool m_kill;
|
||||
QString m_tool;
|
||||
|
@ -95,7 +99,7 @@ DECLARE_SETUP_WIDGETS
|
|||
void handleDescriptionChanged(const QString &description);
|
||||
void handleStatusChanged(const QString &activeState, const QString &subState, ChronoUtilities::DateTime activeSince);
|
||||
void handleEnabledChanged(const QString &unitFileState);
|
||||
Data::SyncthingService &m_service;
|
||||
Data::SyncthingService *const m_service;
|
||||
END_DECLARE_OPTION_PAGE
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue