diff --git a/cli/application.cpp b/cli/application.cpp index b852c35..7c1a3c9 100644 --- a/cli/application.cpp +++ b/cli/application.cpp @@ -42,6 +42,7 @@ Application::Application() : m_args.pauseAll.setCallback(bind(&Application::requestPauseAll, this, _1)); m_args.resume.setCallback(bind(&Application::requestResume, this, _1)); m_args.resumeAll.setCallback(bind(&Application::requestResumeAll, this, _1)); + m_args.waitForIdle.setCallback(bind(&Application::initWaitForIdle, this, _1)); // connect signals and slots connect(&m_connection, &SyncthingConnection::statusChanged, this, &Application::handleStatusChanged); @@ -111,7 +112,7 @@ int Application::exec(int argc, const char * const *argv) } // finally to request / establish connection - if(m_args.status.isPresent() || m_args.rescanAll.isPresent() || m_args.pauseAll.isPresent() || m_args.resumeAll.isPresent()) { + if(m_args.status.isPresent() || m_args.rescanAll.isPresent() || m_args.pauseAll.isPresent() || m_args.resumeAll.isPresent() || m_args.waitForIdle.isPresent()) { // those arguments rquire establishing a connection first, the actual handler is called by handleStatusChanged() when // the connection has been established m_connection.reconnect(m_settings); @@ -139,7 +140,9 @@ void Application::handleStatusChanged(SyncthingStatus newStatus) eraseLine(cout); cout << '\r'; m_args.parser.invokeCallbacks(); - m_connection.disconnect(); + if(!m_args.waitForIdle.isPresent()) { + m_connection.disconnect(); + } } } @@ -242,53 +245,55 @@ void Application::requestResumeAll(const ArgumentOccurrence &) m_connection.resumeAllDevs(); } -void Application::printStatus(const ArgumentOccurrence &) +void Application::findRelevantDirsAndDevs() { - // find relevant dirs and devs - std::vector relevantDirs; - std::vector relevantDevs; int dummy; if(m_args.dir.isPresent()) { - relevantDirs.reserve(m_args.dir.occurrences()); + m_relevantDirs.reserve(m_args.dir.occurrences()); for(size_t i = 0; i != m_args.dir.occurrences(); ++i) { if(const SyncthingDir *dir = m_connection.findDirInfo(QString::fromLocal8Bit(m_args.dir.values(i).front()), dummy)) { - relevantDirs.emplace_back(dir); + m_relevantDirs.emplace_back(dir); } else { - cerr << "Warning: Specified directory \"" << m_args.dir.values(i).front() << "\" does not exist" << endl; + cerr << "Warning: Specified directory \"" << m_args.dir.values(i).front() << "\" does not exist and will be ignored" << endl; } } } if(m_args.dev.isPresent()) { - relevantDevs.reserve(m_args.dev.occurrences()); + m_relevantDevs.reserve(m_args.dev.occurrences()); for(size_t i = 0; i != m_args.dev.occurrences(); ++i) { const SyncthingDev *dev = m_connection.findDevInfo(QString::fromLocal8Bit(m_args.dev.values(i).front()), dummy); if(!dev) { dev = m_connection.findDevInfoByName(QString::fromLocal8Bit(m_args.dev.values(i).front()), dummy); } if(dev) { - relevantDevs.emplace_back(dev); + m_relevantDevs.emplace_back(dev); } else { - cerr << "Warning: Specified device \"" << m_args.dev.values(i).front() << "\" does not exist" << endl; + cerr << "Warning: Specified device \"" << m_args.dev.values(i).front() << "\" does not exist and will be ignored" << endl; } } } - if(relevantDirs.empty() && relevantDevs.empty()) { - relevantDirs.reserve(m_connection.dirInfo().size()); + if(m_relevantDirs.empty() && m_relevantDevs.empty()) { + m_relevantDirs.reserve(m_connection.dirInfo().size()); for(const SyncthingDir &dir : m_connection.dirInfo()) { - relevantDirs.emplace_back(&dir); + m_relevantDirs.emplace_back(&dir); } - relevantDevs.reserve(m_connection.devInfo().size()); + m_relevantDevs.reserve(m_connection.devInfo().size()); for(const SyncthingDev &dev : m_connection.devInfo()) { - relevantDevs.emplace_back(&dev); + m_relevantDevs.emplace_back(&dev); } } +} + +void Application::printStatus(const ArgumentOccurrence &) +{ + findRelevantDirsAndDevs(); // display dirs - if(!relevantDirs.empty()) { + if(!m_relevantDirs.empty()) { setStyle(cout, TextAttribute::Bold); cout << "Directories\n"; setStyle(cout); - for(const SyncthingDir *dir : relevantDirs) { + for(const SyncthingDir *dir : m_relevantDirs) { cout << " - "; setStyle(cout, TextAttribute::Bold); cout << dir->id.toLocal8Bit().data() << '\n'; @@ -333,11 +338,11 @@ void Application::printStatus(const ArgumentOccurrence &) } // display devs - if(!relevantDevs.empty()) { + if(!m_relevantDevs.empty()) { setStyle(cout, TextAttribute::Bold); cout << "Devices\n"; setStyle(cout); - for(const SyncthingDev *dev : relevantDevs) { + for(const SyncthingDev *dev : m_relevantDevs) { cout << " - "; setStyle(cout, TextAttribute::Bold); cout << dev->name.toLocal8Bit().data() << '\n'; @@ -398,4 +403,45 @@ void Application::printLog(const std::vector &logEntries) QCoreApplication::exit(); } +void Application::initWaitForIdle(const ArgumentOccurrence &) +{ + findRelevantDirsAndDevs(); + + // might idle already + waitForIdle(); + + // currently not idling + // -> relevant dirs/devs might be invalidated so findRelevantDirsAndDevs() must invoked again + connect(&m_connection, &SyncthingConnection::newDirs, this, &Application::findRelevantDirsAndDevs); + connect(&m_connection, &SyncthingConnection::newDevices, this, &Application::findRelevantDirsAndDevs); + // -> check for idle again when dir/dev status changed + connect(&m_connection, &SyncthingConnection::dirStatusChanged, this, &Application::waitForIdle); + connect(&m_connection, &SyncthingConnection::devStatusChanged, this, &Application::waitForIdle); +} + +void Application::waitForIdle() +{ + for(const SyncthingDir *dir : m_relevantDirs) { + switch(dir->status) { + case SyncthingDirStatus::Unknown: + case SyncthingDirStatus::Idle: + break; + default: + return; + } + } + for(const SyncthingDev *dev : m_relevantDevs) { + switch(dev->status) { + case SyncthingDevStatus::Unknown: + case SyncthingDevStatus::Disconnected: + case SyncthingDevStatus::OwnDevice: + case SyncthingDevStatus::Idle: + break; + default: + return; + } + } + QCoreApplication::exit(); +} + } // namespace Cli diff --git a/cli/application.h b/cli/application.h index 402071b..d256d1a 100644 --- a/cli/application.h +++ b/cli/application.h @@ -8,6 +8,8 @@ #include +#include + namespace Cli { class Application : public QObject @@ -24,6 +26,7 @@ private slots: void handleStatusChanged(Data::SyncthingStatus newStatus); void handleResponse(); void handleError(const QString &message); + void findRelevantDirsAndDevs(); private: void requestLog(const ArgumentOccurrence &); @@ -37,11 +40,16 @@ private: void requestResumeAll(const ArgumentOccurrence &); void printStatus(const ArgumentOccurrence &); void printLog(const std::vector &logEntries); + void initWaitForIdle(const ArgumentOccurrence &); + void waitForIdle(); Args m_args; Data::SyncthingConnectionSettings m_settings; Data::SyncthingConnection m_connection; size_t m_expectedResponse; + std::vector m_relevantDirs; + std::vector m_relevantDevs; + }; } // namespace Cli diff --git a/cli/args.cpp b/cli/args.cpp index 5b906f3..8deb0fb 100644 --- a/cli/args.cpp +++ b/cli/args.cpp @@ -14,6 +14,7 @@ Args::Args() : pauseAll("pause-all", '\0', "pauses all devices"), resume("resume", '\0', "resumes the specified devices"), resumeAll("resume-all", '\0', "resumes all devices"), + waitForIdle("wait-for-idle", 'w', "waits until the specified dirs/devs are idling"), dir("dir", 'd', "specifies the directory to display status info for (default is all dirs)", {"ID"}), dev("dev", '\0', "specifies the device to display status info for (default is all devs)", {"ID"}), configFile("config-file", 'f', "specifies the Syncthing config file", {"path"}), @@ -24,6 +25,7 @@ Args::Args() : { dir.setConstraints(0, -1), dev.setConstraints(0, -1); status.setSubArguments({&dir, &dev}); + waitForIdle.setSubArguments({&dir, &dev}); rescan.setValueNames({"dir ID"}); rescan.setRequiredValueCount(-1); @@ -33,7 +35,7 @@ Args::Args() : resume.setRequiredValueCount(-1); parser.setMainArguments({&status, &log, &stop, &restart, &rescan, &rescanAll, &pause, &pauseAll, &resume, &resumeAll, - &configFile, &apiKey, &url, &credentials, &certificate, &help}); + &waitForIdle, &configFile, &apiKey, &url, &credentials, &certificate, &help}); // allow setting default values via environment configFile.setEnvironmentVariable("SYNCTHING_CTL_CONFIG_FILE"); diff --git a/cli/args.h b/cli/args.h index 094bc5b..fa889cb 100644 --- a/cli/args.h +++ b/cli/args.h @@ -12,7 +12,7 @@ struct Args Args(); ArgumentParser parser; HelpArgument help; - OperationArgument status, log, stop, restart, rescan, rescanAll, pause, pauseAll, resume, resumeAll; + OperationArgument status, log, stop, restart, rescan, rescanAll, pause, pauseAll, resume, resumeAll, waitForIdle; ConfigValueArgument dir, dev; ConfigValueArgument configFile, apiKey, url, credentials, certificate; };