Last active
June 15, 2016 11:44
-
-
Save sekia/d249b44104cf89653b404f1c9cf597f4 to your computer and use it in GitHub Desktop.
Tiny command runner using POCO library.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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