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:
Martchus 2018-04-11 23:15:15 +02:00
parent 278ba521d9
commit 0ceb8d5e79
17 changed files with 388 additions and 101 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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();
}
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 &notifier) 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

View File

@ -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(

View File

@ -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