Skip to content

Instantly share code, notes, and snippets.

@sekia
Last active June 15, 2016 11:44
Show Gist options
  • Select an option

  • Save sekia/d249b44104cf89653b404f1c9cf597f4 to your computer and use it in GitHub Desktop.

Select an option

Save sekia/d249b44104cf89653b404f1c9cf597f4 to your computer and use it in GitHub Desktop.
Tiny command runner using POCO library.
#include <iostream>
#include <memory>
#include <string>
#include "Poco/Delegate.h"
#include "Poco/DirectoryWatcher.h"
#include "Poco/Event.h"
#include "Poco/Exception.h"
#include "Poco/File.h"
#include "Poco/Mutex.h"
#include "Poco/Path.h"
#include "Poco/Process.h"
#include "Poco/ScopedLock.h"
#include "Poco/Thread.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionCallback.h"
#include "Poco/Util/OptionException.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Validator.h"
namespace simple_watcher {
using Poco::Delegate;
using Poco::DirectoryWatcher;
using Poco::Event;
using Poco::Exception;
using Poco::FastMutex;
using Poco::File;
using Poco::Path;
using Poco::Process;
using Poco::Thread;
using Poco::Util::HelpFormatter;
using Poco::Util::Option;
using Poco::Util::OptionCallback;
using Poco::Util::OptionException;
using Poco::Util::OptionSet;
using Poco::Util::ServerApplication;
using Poco::Util::Validator;
using DirectoryEvent = DirectoryWatcher::DirectoryEvent;
using ScopedLock = FastMutex::ScopedLock;
namespace {
File GetDirectoryFor(const std::string& path_str) {
Path path = Path::forDirectory(path_str);
if (path.isRelative()) {
path = Path::forDirectory(Path::current()).resolve(path);
}
return File(path);
}
} // Anonymouse namespace
class DirectoryOptionValidator : public Validator {
public:
virtual void validate(const Option&, const std::string& path) override {
try {
File dir = GetDirectoryFor(path);
if (!(dir.exists() && dir.isDirectory())) {
throw OptionException("Specified directory does not exist.");
}
} catch (const OptionException& e) {
e.rethrow();
} catch (const Exception& e) {
throw OptionException(
"Error occured during directory path parsing.", e);
}
}
};
class SimpleWatcher : public ServerApplication {
protected:
virtual void defineOptions(OptionSet& options) override {
// Avoid calling ServerApplication::defineOptions() that adds
// daemon-related optoins.
Application::defineOptions(options);
options.addOption(
Option("target", "t", "Target directory.")
.argument("<path>", true)
.callback(
OptionCallback<SimpleWatcher>(
this, &SimpleWatcher::HandleTargetOption))
.required(true)
.validator(new DirectoryOptionValidator()));
options.addOption(
Option("help", "h", "Show help message.")
.callback(
OptionCallback<SimpleWatcher>(
this, &SimpleWatcher::HandleHelpOption)));
}
virtual int main(const std::vector<std::string>& args) override {
if (help_ || args.empty() || !isInteractive()) {
ShowHelpMessage();
return EXIT_OK;
}
const std::string& command = args[0];
const std::vector<std::string> command_args(args.begin() + 1, args.end());
FastMutex lock;
bool stop_launcher = false;
Thread launcher;
launcher.startFunc(
[&](){
while (true) {
awake_launcher_.wait();
ScopedLock guard(lock);
if (stop_launcher) { break; }
ExecuteCommand(command, command_args);
}
});
awake_launcher_.set(); // First execution.
waitForTerminationRequest();
lock.lock();
stop_launcher = true;
lock.unlock();
awake_launcher_.set();
launcher.join();
return EXIT_OK;
}
private:
void ExecuteCommand(
const std::string& command,
const std::vector<std::string>& args) const {
Process::wait(Process::launch(command, args));
}
void HandleDirectoryEvent(const DirectoryEvent&) {
awake_launcher_.set();
}
void HandleHelpOption(const std::string&, const std::string&) noexcept {
help_ = true;
stopOptionsProcessing();
}
void HandleTargetOption(const std::string&, const std::string& dir_path) {
auto watcher = new DirectoryWatcher(GetDirectoryFor(dir_path));
watcher_ = std::unique_ptr<DirectoryWatcher>(watcher);
auto event_handler =
Delegate<SimpleWatcher, const DirectoryEvent, false>(
this, &SimpleWatcher::HandleDirectoryEvent);
watcher_->itemAdded += event_handler;
watcher_->itemModified += event_handler;
watcher_->itemMovedFrom += event_handler;
watcher_->itemMovedTo += event_handler;
watcher_->itemRemoved += event_handler;
}
void ShowHelpMessage() const noexcept {
HelpFormatter formatter(options());
formatter.setCommand(commandName());
formatter.setUsage("[options] [--] command ...");
formatter.format(std::cout);
}
Event awake_launcher_;
bool help_ = false;
std::unique_ptr<DirectoryWatcher> watcher_;
};
} // namespace simple_watcher
int main(int argc, const char **argv) {
using namespace simple_watcher;
SimpleWatcher app;
return app.run(argc, const_cast<char **>(argv));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment