From 6cae06c4dc9a8eef30e0110a181cc98b35478aec Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Tue, 14 Jun 2016 12:14:29 +0200 Subject: [PATCH] refactor(script): Better handling of tail scripts --- include/modules/script.hpp | 2 + include/services/command.hpp | 5 ++- include/utils/io.hpp | 1 + src/modules/script.cpp | 77 +++++++++++++++++++++++++----------- src/services/command.cpp | 16 +++++--- src/utils/io.cpp | 34 ++++++++++------ 6 files changed, 92 insertions(+), 43 deletions(-) diff --git a/include/modules/script.hpp b/include/modules/script.hpp index 0d2041f6..9b8ad8f5 100644 --- a/include/modules/script.hpp +++ b/include/modules/script.hpp @@ -10,6 +10,7 @@ namespace modules static constexpr auto TAG_OUTPUT = ""; std::unique_ptr builder; + std::unique_ptr command; std::string exec; bool tail = false; @@ -28,6 +29,7 @@ namespace modules public: explicit ScriptModule(const std::string& name); + void start(); bool update(); bool build(Builder *builder, const std::string& tag); std::string get_output(); diff --git a/include/services/command.hpp b/include/services/command.hpp index bc48f4cb..b0620482 100644 --- a/include/services/command.hpp +++ b/include/services/command.hpp @@ -34,6 +34,7 @@ class Command int get_stdout(int); // int get_stdin(int); - // pid_t get_pid(); - // int get_exit_status(); + pid_t get_pid(); + bool is_running(); + int get_exit_status(); }; diff --git a/include/utils/io.hpp b/include/utils/io.hpp index 841ede44..81d664d9 100644 --- a/include/utils/io.hpp +++ b/include/utils/io.hpp @@ -58,6 +58,7 @@ namespace io std::string read(int read_fd, int bytes_to_read = -1); std::string read(int read_fd, int bytes_to_read, int &bytes_read_loc, int &status_loc); + std::string readline(int read_fd, int &bytes_read); std::string readline(int read_fd); int write(int write_fd, const std::string& data); diff --git a/src/modules/script.cpp b/src/modules/script.cpp index 7ea60308..2cb54fd0 100644 --- a/src/modules/script.cpp +++ b/src/modules/script.cpp @@ -13,7 +13,9 @@ ScriptModule::ScriptModule(const std::string& name_) this->exec = config::get(name(), "exec"); this->tail = config::get(name(), "tail", this->tail); - if (!this->tail) + if (this->tail) + this->interval = 0s; + else this->interval = std::chrono::duration(config::get(name(), "interval", 1)); this->click_left = config::get(name(), "click-left", ""); @@ -27,38 +29,67 @@ ScriptModule::ScriptModule(const std::string& name_) // Add formats and elements {{{ this->formatter->add(DEFAULT_FORMAT, TAG_OUTPUT, { TAG_OUTPUT }); // }}} + + this->counter = 0; +} + +void ScriptModule::start() +{ + this->TimerModule::start(); + + if (!this->tail) + return; + + // Start the tail script in a separate thread {{{ + this->threads.push_back(std::thread([&]{ + while (this->enabled() && (!this->command || !this->command->is_running())) { + log_debug("Executing command: "+ this->exec); + this->counter++; + this->command = std::make_unique("/usr/bin/env\nsh\n-c\n" + + string::replace_all(this->exec, "%counter%", std::to_string(this->counter))); + this->command->exec(true); + } + })); + // }}} } bool ScriptModule::update() { - this->counter++; - this->output.clear(); + int bytes_read; + + if (this->tail) { + if (!this->command) + return false; + if (!io::poll_read(this->command->get_stdout(PIPE_READ), 100)) + return false; + this->output = io::readline(this->command->get_stdout(PIPE_READ), bytes_read); + return bytes_read > 0; + } try { - std::string buf; + this->counter++; + this->output.clear(); - auto execline = string::replace_all(this->exec, "%counter%", std::to_string(this->counter)); - auto command = std::make_unique("/usr/bin/env\nsh\n-c\n"+ execline); + this->command = std::make_unique("/usr/bin/env\nsh\n-c\n" + + string::replace_all(this->exec, "%counter%", std::to_string(this->counter))); + this->command->exec(false); - command->exec(false); - - while (!(buf = io::readline(command->get_stdout(PIPE_READ))).empty() || (this->tail && this->enabled())) { - this->output.append(buf + "\n"); - - if (this->tail) { - this->broadcast(); - this->output.clear(); - } + while (true) { + auto buf = io::readline(this->command->get_stdout(PIPE_READ), bytes_read); + if (bytes_read <= 0) + break; + this->output.append(buf +"\n"); } - command->wait(); + if (this->command) + this->command->wait(); } catch (CommandException &e) { log_error(e.what()); } catch (proc::ExecFailure &e) { log_error(e.what()); } - return !this->output.empty(); + return true; } std::string ScriptModule::get_output() @@ -66,17 +97,19 @@ std::string ScriptModule::get_output() if (this->output.empty()) return ""; + auto counter_str = std::to_string(this->counter); + if (!this->click_left.empty()) - this->builder->cmd(Cmd::LEFT_CLICK, string::replace_all(this->click_left, "%counter%", std::to_string(this->counter))); + this->builder->cmd(Cmd::LEFT_CLICK, string::replace_all(this->click_left, "%counter%", counter_str)); if (!this->click_middle.empty()) - this->builder->cmd(Cmd::MIDDLE_CLICK, string::replace_all(this->click_middle, "%counter%", std::to_string(this->counter))); + this->builder->cmd(Cmd::MIDDLE_CLICK, string::replace_all(this->click_middle, "%counter%", counter_str)); if (!this->click_right.empty()) - this->builder->cmd(Cmd::RIGHT_CLICK, string::replace_all(this->click_right, "%counter%", std::to_string(this->counter))); + this->builder->cmd(Cmd::RIGHT_CLICK, string::replace_all(this->click_right, "%counter%", counter_str)); if (!this->scroll_up.empty()) - this->builder->cmd(Cmd::SCROLL_UP, string::replace_all(this->scroll_up, "%counter%", std::to_string(this->counter))); + this->builder->cmd(Cmd::SCROLL_UP, string::replace_all(this->scroll_up, "%counter%", counter_str)); if (!scroll_down.empty()) - this->builder->cmd(Cmd::SCROLL_DOWN, string::replace_all(this->scroll_down, "%counter%", std::to_string(this->counter))); + this->builder->cmd(Cmd::SCROLL_DOWN, string::replace_all(this->scroll_down, "%counter%", counter_str)); this->builder->node(this->Module::get_output()); diff --git a/src/services/command.cpp b/src/services/command.cpp index 1bbc434e..7c0e4d65 100644 --- a/src/services/command.cpp +++ b/src/services/command.cpp @@ -146,10 +146,14 @@ int Command::get_stdout(int c) { // return this->stdin[c]; // } -// pid_t Command::get_pid() { -// return this->fork_pid; -// } +pid_t Command::get_pid() { + return this->fork_pid; +} -// int Command::get_exit_status() { -// return this->fork_status; -// } +bool Command::is_running() { + return proc::wait_for_completion_nohang(this->fork_pid, &this->fork_status) > -1; +} + +int Command::get_exit_status() { + return this->fork_status; +} diff --git a/src/utils/io.cpp b/src/utils/io.cpp index 2757adcd..3e6505f7 100644 --- a/src/utils/io.cpp +++ b/src/utils/io.cpp @@ -130,10 +130,9 @@ namespace io return read(read_fd, bytes_to_read, bytes_read, status); } - std::string readline(int read_fd) + std::string readline(int read_fd, int &bytes_read) { std::stringstream buffer; - int bytes_read; char c; while ((bytes_read = ::read(read_fd, &c, 1)) > 0) { @@ -145,9 +144,17 @@ namespace io get_logger()->debug("Reached EOF"); } else if (bytes_read == -1) { get_logger()->debug("Read failed"); + } else { + return string::strip_trailing_newline(buffer.str()); } - return string::strip_trailing_newline(buffer.str()); + return ""; + } + + std::string readline(int read_fd) + { + int bytes_read; + return readline(read_fd, bytes_read); } int write(int write_fd, const std::string& data) { @@ -163,21 +170,22 @@ namespace io return io::write(write_fd, data); } - void tail(int read_fd, int writeback_fd) + void tail(int read_fd, std::function callback) { - std::string line; - - while (false == (line = io::readline(read_fd)).empty()) { - io::writeline(writeback_fd, line); - std::cout << '\n'; + int bytes_read; + while (true) { + auto line = io::readline(read_fd, bytes_read); + if (bytes_read <= 0) + break; + callback(line); } } - void tail(int read_fd, std::function callback) + void tail(int read_fd, int writeback_fd) { - std::string line; - while (false == (line = io::readline(read_fd)).empty()) - callback(line); + tail(read_fd, [&writeback_fd](std::string data){ + io::writeline(writeback_fd, data); + }); } bool poll_read(int fd, int timeout_ms) {