fix(script): Rerun tail script when done

- Improve handling of command life time proc
- Restart tail command on successful completion
- Should fix jaagr/lemonbuddy#105
This commit is contained in:
Michael Carlberg 2016-10-19 01:23:06 +02:00
parent 20ca754629
commit 7e960a3966
3 changed files with 88 additions and 88 deletions

View File

@ -76,8 +76,8 @@ class controller {
m_log.trace("controller: Stop modules");
for (auto&& block : m_modules) {
for (auto&& module : block.second) {
module->on_update.disconnect(this, &controller::on_module_update);
module->on_stop.disconnect(this, &controller::on_module_stop);
module->on_update.clear();
module->on_stop.clear();
module->stop();
}
}
@ -88,7 +88,7 @@ class controller {
}
m_log.trace("controller: Deconstruct bar instance");
g_signals::bar::action_click.disconnect(this, &controller::on_module_click);
g_signals::bar::action_click.clear();
m_bar.reset();
m_log.trace("controller: Interrupt X event loop");

View File

@ -5,7 +5,7 @@
LEMONBUDDY_NS
#define SHELL_CMD "/usr/bin/env\nsh\n-c\n"
#define SHELL_CMD "/usr/bin/env\nsh\n-c\n%cmd%"
#define OUTPUT_ACTION(BUTTON) \
if (!m_actions[BUTTON].empty()) \
m_builder->cmd(BUTTON, string_util::replace_all(m_actions[BUTTON], "%counter%", counter_str))
@ -18,7 +18,8 @@ namespace modules {
void setup() {
m_formatter->add(DEFAULT_FORMAT, TAG_OUTPUT, {TAG_OUTPUT});
// Load configuration values {{{
// Load configuration values
REQ_CONFIG_VALUE(name(), m_exec, "exec");
GET_CONFIG_VALUE(name(), m_tail, "tail");
GET_CONFIG_VALUE(name(), m_maxlen, "maxlen");
@ -30,113 +31,99 @@ namespace modules {
m_actions[mousebtn::SCROLL_UP] = m_conf.get<string>(name(), "scroll-up", "");
m_actions[mousebtn::SCROLL_DOWN] = m_conf.get<string>(name(), "scroll-down", "");
if (!m_tail) {
m_interval = interval_t{m_conf.get<float>(name(), "interval", m_interval.count())};
}
// }}}
// Execute the tail command {{{
if (m_tail) {
try {
auto exec = string_util::replace_all(m_exec, "%counter%", to_string(++m_counter));
m_log.trace("%s: Executing '%s'", name(), exec);
m_command = command_util::make_command(SHELL_CMD + exec);
m_command->exec(false);
} catch (const std::exception& err) {
m_log.err("%s: Failed to execute tail command, stopping module..", name());
m_log.err("%s: %s", name(), err.what());
stop();
}
}
// }}}
m_interval = interval_t{m_conf.get<float>(name(), "interval", 0.0f)};
}
void stop() {
// Put the module in stopped state {{{
event_module::stop();
// }}}
// Terminate running command {{{
try {
if (m_tail && m_command)
m_command.reset();
} catch (const std::exception& err) {
m_log.err("%s: %s", name(), err.what());
}
// }}}
wakeup();
enable(false);
m_command.reset();
std::lock_guard<threading_util::spin_lock> lck(this->update_lock);
wakeup();
}
void idle() {
if (!enabled())
sleep(100ms);
if (!m_tail)
sleep(m_interval);
else if (!m_command || !m_command->is_running())
sleep(m_interval);
}
bool has_event() {
// Handle non-tailing command {{{
if (!m_tail) {
sleep(m_interval);
return enabled();
}
// }}}
// Handle tailing command {{{
if (!m_command || !m_command->is_running()) {
m_log.warn("%s: Tail command finished, stopping module...", name());
stop();
return false;
} else if ((m_output = m_command->readline()) != m_prev) {
m_prev = m_output;
// Non tail commands should always run
if (!m_tail)
return true;
} else {
if (!enabled())
return false;
try {
if (!m_command || !m_command->is_running()) {
auto exec = string_util::replace_all(m_exec, "%counter%", to_string(++m_counter));
m_log.trace("%s: Executing '%s'", name(), exec);
m_command = command_util::make_command(string_util::replace(SHELL_CMD, "%cmd%", exec));
m_command->exec(false);
}
} catch (const std::exception& err) {
m_log.err("%s: %s", name(), err.what());
throw module_error(name() + ": Failed to execute tail command, stopping module...");
}
// }}}
if (!m_command)
return false;
if ((m_output = m_command->readline()) == m_prev)
return false;
m_prev = m_output;
return true;
}
bool update() {
// Handle tailing command {{{
// Tailing commands always update
if (m_tail)
return true;
// }}}
// Handle non-tailing command {{{
try {
auto exec = string_util::replace_all(m_exec, "%counter%", to_string(++m_counter));
auto cmd = command_util::make_command(SHELL_CMD + exec);
auto cmd = command_util::make_command(string_util::replace(SHELL_CMD, "%cmd%", exec));
m_log.trace("%s: Executing '%s'", name(), exec);
cmd->exec();
cmd->tail([this](string contents) { m_output = contents; });
} catch (const std::exception& err) {
m_log.err("%s: Failed to execute command, stopping module..", name());
m_log.err("%s: %s", name(), err.what());
stop();
throw module_error(name() + ": Failed to execute command, stopping module...");
}
if (m_output == m_prev)
return false;
}
if (m_output != m_prev) {
m_prev = m_output;
return true;
}
return false;
// }}}
m_prev = m_output;
return true;
}
string get_output() {
if (m_output.empty())
return " ";
// Truncate output to the defined max length {{{
// Truncate output to the defined max length
if (m_maxlen > 0 && m_output.length() > m_maxlen) {
m_output.erase(m_maxlen);
m_output += m_ellipsis ? "..." : "";
}
// }}}
// Add mousebtn command handlers {{{
auto counter_str = to_string(m_counter);
auto counter_str = to_string(m_counter);
OUTPUT_ACTION(mousebtn::LEFT);
OUTPUT_ACTION(mousebtn::MIDDLE);
OUTPUT_ACTION(mousebtn::RIGHT);
OUTPUT_ACTION(mousebtn::SCROLL_UP);
OUTPUT_ACTION(mousebtn::SCROLL_DOWN);
// }}}
m_builder->node(module::get_output());
return m_builder->flush();
@ -156,7 +143,7 @@ namespace modules {
string m_exec;
bool m_tail = false;
interval_t m_interval = 1s;
interval_t m_interval = 0s;
size_t m_maxlen = 0;
bool m_ellipsis = true;
map<mousebtn, string> m_actions;

View File

@ -60,15 +60,8 @@ namespace command_util {
}
~command() {
if (is_running()) {
try {
m_log.trace("command: Sending SIGTERM to running child process (%d)", m_forkpid);
killpg(m_forkpid, SIGTERM);
wait();
} catch (const std::exception& err) {
m_log.err("command: %s", err.what());
}
}
if (is_running())
terminate();
if (m_stdin[PIPE_READ] > 0)
close(m_stdin[PIPE_READ]);
@ -119,13 +112,30 @@ namespace command_util {
if ((m_stdout[PIPE_WRITE] = close(m_stdout[PIPE_WRITE])) == -1)
throw command_strerror("Failed to close fd");
if (wait_for_completion)
return wait();
if (wait_for_completion) {
auto status = wait();
m_forkpid = -1;
return status;
}
}
return EXIT_SUCCESS;
}
void terminate() {
try {
if (is_running()) {
m_log.trace("command: Sending SIGTERM to running child process (%d)", m_forkpid);
killpg(m_forkpid, SIGTERM);
wait();
}
} catch (const command_error& err) {
m_log.warn("%s", err.what());
}
m_forkpid = -1;
}
/**
* Wait for the child processs to finish
*/
@ -133,11 +143,12 @@ namespace command_util {
auto waitflags = WCONTINUED | WUNTRACED;
do {
if (process_util::wait_for_completion(m_forkpid, &m_forkstatus, waitflags) == -1)
throw command_error("Process did not finish successfully");
process_util::wait_for_completion(m_forkpid, &m_forkstatus, waitflags);
if (WIFEXITED(m_forkstatus))
m_log.trace("command: Exited with status %d", WEXITSTATUS(m_forkstatus));
if (WIFEXITED(m_forkstatus) && m_forkstatus > 0)
m_log.warn("command: Exited with failed status %d", WEXITSTATUS(m_forkstatus));
else if (WIFEXITED(m_forkstatus))
m_log.warn("command: Exited with status %d", WEXITSTATUS(m_forkstatus));
else if (WIFSIGNALED(m_forkstatus))
m_log.trace("command: killed by signal %d", WTERMSIG(m_forkstatus));
else if (WIFSTOPPED(m_forkstatus))
@ -197,7 +208,9 @@ namespace command_util {
* Check if command is running
*/
bool is_running() {
return process_util::wait_for_completion_nohang(m_forkpid, &m_forkstatus) > -1;
if (m_forkpid > 0)
return process_util::wait_for_completion_nohang(m_forkpid, &m_forkstatus) > -1;
return false;
}
/**