Add syncthingctl option to wait for dir/dev idle
This commit is contained in:
parent
d52bce55df
commit
1f796e6039
|
@ -42,6 +42,7 @@ Application::Application() :
|
||||||
m_args.pauseAll.setCallback(bind(&Application::requestPauseAll, this, _1));
|
m_args.pauseAll.setCallback(bind(&Application::requestPauseAll, this, _1));
|
||||||
m_args.resume.setCallback(bind(&Application::requestResume, this, _1));
|
m_args.resume.setCallback(bind(&Application::requestResume, this, _1));
|
||||||
m_args.resumeAll.setCallback(bind(&Application::requestResumeAll, 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 signals and slots
|
||||||
connect(&m_connection, &SyncthingConnection::statusChanged, this, &Application::handleStatusChanged);
|
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
|
// 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
|
// those arguments rquire establishing a connection first, the actual handler is called by handleStatusChanged() when
|
||||||
// the connection has been established
|
// the connection has been established
|
||||||
m_connection.reconnect(m_settings);
|
m_connection.reconnect(m_settings);
|
||||||
|
@ -139,7 +140,9 @@ void Application::handleStatusChanged(SyncthingStatus newStatus)
|
||||||
eraseLine(cout);
|
eraseLine(cout);
|
||||||
cout << '\r';
|
cout << '\r';
|
||||||
m_args.parser.invokeCallbacks();
|
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();
|
m_connection.resumeAllDevs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::printStatus(const ArgumentOccurrence &)
|
void Application::findRelevantDirsAndDevs()
|
||||||
{
|
{
|
||||||
// find relevant dirs and devs
|
|
||||||
std::vector<const SyncthingDir *> relevantDirs;
|
|
||||||
std::vector<const SyncthingDev *> relevantDevs;
|
|
||||||
int dummy;
|
int dummy;
|
||||||
if(m_args.dir.isPresent()) {
|
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) {
|
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)) {
|
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 {
|
} 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()) {
|
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) {
|
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);
|
const SyncthingDev *dev = m_connection.findDevInfo(QString::fromLocal8Bit(m_args.dev.values(i).front()), dummy);
|
||||||
if(!dev) {
|
if(!dev) {
|
||||||
dev = m_connection.findDevInfoByName(QString::fromLocal8Bit(m_args.dev.values(i).front()), dummy);
|
dev = m_connection.findDevInfoByName(QString::fromLocal8Bit(m_args.dev.values(i).front()), dummy);
|
||||||
}
|
}
|
||||||
if(dev) {
|
if(dev) {
|
||||||
relevantDevs.emplace_back(dev);
|
m_relevantDevs.emplace_back(dev);
|
||||||
} else {
|
} 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()) {
|
if(m_relevantDirs.empty() && m_relevantDevs.empty()) {
|
||||||
relevantDirs.reserve(m_connection.dirInfo().size());
|
m_relevantDirs.reserve(m_connection.dirInfo().size());
|
||||||
for(const SyncthingDir &dir : m_connection.dirInfo()) {
|
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()) {
|
for(const SyncthingDev &dev : m_connection.devInfo()) {
|
||||||
relevantDevs.emplace_back(&dev);
|
m_relevantDevs.emplace_back(&dev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::printStatus(const ArgumentOccurrence &)
|
||||||
|
{
|
||||||
|
findRelevantDirsAndDevs();
|
||||||
|
|
||||||
// display dirs
|
// display dirs
|
||||||
if(!relevantDirs.empty()) {
|
if(!m_relevantDirs.empty()) {
|
||||||
setStyle(cout, TextAttribute::Bold);
|
setStyle(cout, TextAttribute::Bold);
|
||||||
cout << "Directories\n";
|
cout << "Directories\n";
|
||||||
setStyle(cout);
|
setStyle(cout);
|
||||||
for(const SyncthingDir *dir : relevantDirs) {
|
for(const SyncthingDir *dir : m_relevantDirs) {
|
||||||
cout << " - ";
|
cout << " - ";
|
||||||
setStyle(cout, TextAttribute::Bold);
|
setStyle(cout, TextAttribute::Bold);
|
||||||
cout << dir->id.toLocal8Bit().data() << '\n';
|
cout << dir->id.toLocal8Bit().data() << '\n';
|
||||||
|
@ -333,11 +338,11 @@ void Application::printStatus(const ArgumentOccurrence &)
|
||||||
}
|
}
|
||||||
|
|
||||||
// display devs
|
// display devs
|
||||||
if(!relevantDevs.empty()) {
|
if(!m_relevantDevs.empty()) {
|
||||||
setStyle(cout, TextAttribute::Bold);
|
setStyle(cout, TextAttribute::Bold);
|
||||||
cout << "Devices\n";
|
cout << "Devices\n";
|
||||||
setStyle(cout);
|
setStyle(cout);
|
||||||
for(const SyncthingDev *dev : relevantDevs) {
|
for(const SyncthingDev *dev : m_relevantDevs) {
|
||||||
cout << " - ";
|
cout << " - ";
|
||||||
setStyle(cout, TextAttribute::Bold);
|
setStyle(cout, TextAttribute::Bold);
|
||||||
cout << dev->name.toLocal8Bit().data() << '\n';
|
cout << dev->name.toLocal8Bit().data() << '\n';
|
||||||
|
@ -398,4 +403,45 @@ void Application::printLog(const std::vector<SyncthingLogEntry> &logEntries)
|
||||||
QCoreApplication::exit();
|
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
|
} // namespace Cli
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
namespace Cli {
|
namespace Cli {
|
||||||
|
|
||||||
class Application : public QObject
|
class Application : public QObject
|
||||||
|
@ -24,6 +26,7 @@ private slots:
|
||||||
void handleStatusChanged(Data::SyncthingStatus newStatus);
|
void handleStatusChanged(Data::SyncthingStatus newStatus);
|
||||||
void handleResponse();
|
void handleResponse();
|
||||||
void handleError(const QString &message);
|
void handleError(const QString &message);
|
||||||
|
void findRelevantDirsAndDevs();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void requestLog(const ArgumentOccurrence &);
|
void requestLog(const ArgumentOccurrence &);
|
||||||
|
@ -37,11 +40,16 @@ private:
|
||||||
void requestResumeAll(const ArgumentOccurrence &);
|
void requestResumeAll(const ArgumentOccurrence &);
|
||||||
void printStatus(const ArgumentOccurrence &);
|
void printStatus(const ArgumentOccurrence &);
|
||||||
void printLog(const std::vector<Data::SyncthingLogEntry> &logEntries);
|
void printLog(const std::vector<Data::SyncthingLogEntry> &logEntries);
|
||||||
|
void initWaitForIdle(const ArgumentOccurrence &);
|
||||||
|
void waitForIdle();
|
||||||
|
|
||||||
Args m_args;
|
Args m_args;
|
||||||
Data::SyncthingConnectionSettings m_settings;
|
Data::SyncthingConnectionSettings m_settings;
|
||||||
Data::SyncthingConnection m_connection;
|
Data::SyncthingConnection m_connection;
|
||||||
size_t m_expectedResponse;
|
size_t m_expectedResponse;
|
||||||
|
std::vector<const Data::SyncthingDir *> m_relevantDirs;
|
||||||
|
std::vector<const Data::SyncthingDev *> m_relevantDevs;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Cli
|
} // namespace Cli
|
||||||
|
|
|
@ -14,6 +14,7 @@ Args::Args() :
|
||||||
pauseAll("pause-all", '\0', "pauses all devices"),
|
pauseAll("pause-all", '\0', "pauses all devices"),
|
||||||
resume("resume", '\0', "resumes the specified devices"),
|
resume("resume", '\0', "resumes the specified devices"),
|
||||||
resumeAll("resume-all", '\0', "resumes all 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"}),
|
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"}),
|
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"}),
|
configFile("config-file", 'f', "specifies the Syncthing config file", {"path"}),
|
||||||
|
@ -24,6 +25,7 @@ Args::Args() :
|
||||||
{
|
{
|
||||||
dir.setConstraints(0, -1), dev.setConstraints(0, -1);
|
dir.setConstraints(0, -1), dev.setConstraints(0, -1);
|
||||||
status.setSubArguments({&dir, &dev});
|
status.setSubArguments({&dir, &dev});
|
||||||
|
waitForIdle.setSubArguments({&dir, &dev});
|
||||||
|
|
||||||
rescan.setValueNames({"dir ID"});
|
rescan.setValueNames({"dir ID"});
|
||||||
rescan.setRequiredValueCount(-1);
|
rescan.setRequiredValueCount(-1);
|
||||||
|
@ -33,7 +35,7 @@ Args::Args() :
|
||||||
resume.setRequiredValueCount(-1);
|
resume.setRequiredValueCount(-1);
|
||||||
|
|
||||||
parser.setMainArguments({&status, &log, &stop, &restart, &rescan, &rescanAll, &pause, &pauseAll, &resume, &resumeAll,
|
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
|
// allow setting default values via environment
|
||||||
configFile.setEnvironmentVariable("SYNCTHING_CTL_CONFIG_FILE");
|
configFile.setEnvironmentVariable("SYNCTHING_CTL_CONFIG_FILE");
|
||||||
|
|
|
@ -12,7 +12,7 @@ struct Args
|
||||||
Args();
|
Args();
|
||||||
ArgumentParser parser;
|
ArgumentParser parser;
|
||||||
HelpArgument help;
|
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 dir, dev;
|
||||||
ConfigValueArgument configFile, apiKey, url, credentials, certificate;
|
ConfigValueArgument configFile, apiKey, url, credentials, certificate;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue