fix(command): broken pipe when ignoring output.

This commit is contained in:
Jérôme BOULMIER
2019-03-07 00:22:00 -05:00
committed by Patrick Ziegler
parent 92181fd237
commit 5e5d8faf04
9 changed files with 169 additions and 97 deletions

View File

@ -3,6 +3,7 @@
#include <csignal>
#include <cstdlib>
#include <utility>
#include <utils/string.hpp>
#include "errors.hpp"
#include "utils/command.hpp"
@ -18,7 +19,95 @@
POLYBAR_NS
command::command(const logger& logger, string cmd) : m_log(logger), m_cmd(move(cmd)) {
command<output_policy::IGNORED>::command(const logger& logger, string cmd) : m_log(logger), m_cmd(move(cmd)) {}
command<output_policy::IGNORED>::~command() {
if (is_running()) {
terminate();
}
}
/**
* Execute the command
*/
int command<output_policy::IGNORED>::exec(bool wait_for_completion) {
if ((m_forkpid = fork()) == -1) {
throw system_error("Failed to fork process");
}
if (process_util::in_forked_process(m_forkpid)) {
setpgid(m_forkpid, 0);
process_util::exec_sh(m_cmd.c_str());
} else {
if (wait_for_completion) {
auto status = wait();
m_forkpid = -1;
return status;
}
}
return EXIT_SUCCESS;
}
void command<output_policy::IGNORED>::terminate() {
if (is_running()) {
m_log.trace("command: Sending SIGTERM to running child process (%d)", m_forkpid);
killpg(m_forkpid, SIGTERM);
wait();
}
m_forkpid = -1;
}
/**
* Check if command is running
*/
bool command<output_policy::IGNORED>::is_running() {
return m_forkpid > 0 && process_util::wait_for_completion_nohang(m_forkpid, &m_forkstatus) > -1;
}
/**
* Wait for the child processs to finish
*/
int command<output_policy::IGNORED>::wait() {
do {
m_log.trace("command: Waiting for pid %d to finish...", m_forkpid);
process_util::wait_for_completion(m_forkpid, &m_forkstatus, WCONTINUED | WUNTRACED);
if (WIFEXITED(m_forkstatus) && m_forkstatus > 0) {
m_log.trace("command: Exited with failed status %d", WEXITSTATUS(m_forkstatus));
} else if (WIFEXITED(m_forkstatus)) {
m_log.trace("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)) {
m_log.trace("command: Stopped by signal %d", WSTOPSIG(m_forkstatus));
} else if (WIFCONTINUED(m_forkstatus)) {
m_log.trace("command: Continued");
} else {
break;
}
} while (!WIFEXITED(m_forkstatus) && !WIFSIGNALED(m_forkstatus));
return m_forkstatus;
}
/**
* Get command pid
*/
pid_t command<output_policy::IGNORED>::get_pid() {
return m_forkpid;
}
/**
* Get command exit status
*/
int command<output_policy::IGNORED>::get_exit_status() {
return m_forkstatus;
}
command<output_policy::REDIRECTED>::command(const polybar::logger& logger, std::string cmd)
: command<output_policy::IGNORED>(logger, move(cmd)) {
if (pipe(m_stdin) != 0) {
throw command_error("Failed to allocate input stream");
}
@ -27,10 +116,7 @@ command::command(const logger& logger, string cmd) : m_log(logger), m_cmd(move(c
}
}
command::~command() {
if (is_running()) {
terminate();
}
command<output_policy::REDIRECTED>::~command() {
if (m_stdin[PIPE_READ] > 0) {
close(m_stdin[PIPE_READ]);
}
@ -48,7 +134,7 @@ command::~command() {
/**
* Execute the command
*/
int command::exec(bool wait_for_completion) {
int command<output_policy::REDIRECTED>::exec(bool wait_for_completion) {
if ((m_forkpid = fork()) == -1) {
throw system_error("Failed to fork process");
}
@ -99,71 +185,28 @@ int command::exec(bool wait_for_completion) {
return EXIT_SUCCESS;
}
void command::terminate() {
if (is_running()) {
m_log.trace("command: Sending SIGTERM to running child process (%d)", m_forkpid);
killpg(m_forkpid, SIGTERM);
wait();
}
m_forkpid = -1;
}
/**
* Check if command is running
*/
bool command::is_running() {
return m_forkpid > 0 && process_util::wait_for_completion_nohang(m_forkpid, &m_forkstatus) > -1;
}
/**
* Wait for the child processs to finish
*/
int command::wait() {
do {
m_log.trace("command: Waiting for pid %d to finish...", m_forkpid);
process_util::wait_for_completion(m_forkpid, &m_forkstatus, WCONTINUED | WUNTRACED);
if (WIFEXITED(m_forkstatus) && m_forkstatus > 0) {
m_log.trace("command: Exited with failed status %d", WEXITSTATUS(m_forkstatus));
} else if (WIFEXITED(m_forkstatus)) {
m_log.trace("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)) {
m_log.trace("command: Stopped by signal %d", WSTOPSIG(m_forkstatus));
} else if (WIFCONTINUED(m_forkstatus)) {
m_log.trace("command: Continued");
} else {
break;
}
} while (!WIFEXITED(m_forkstatus) && !WIFSIGNALED(m_forkstatus));
return m_forkstatus;
}
/**
* Tail command output
*
* \note: This is a blocking call and will not
* end until the stream is closed
*/
void command::tail(callback<string> cb) {
io_util::tail(m_stdout[PIPE_READ], move(cb));
void command<output_policy::REDIRECTED>::tail(callback<string> cb) {
io_util::tail(m_stdout[PIPE_READ], cb);
}
/**
* Write line to command input channel
*/
int command::writeline(string data) {
int command<output_policy::REDIRECTED>::writeline(string data) {
std::lock_guard<std::mutex> lck(m_pipelock);
return io_util::writeline(m_stdin[PIPE_WRITE], move(data));
return static_cast<int>(io_util::writeline(m_stdin[PIPE_WRITE], data));
}
/**
* Read a line from the commands output stream
*/
string command::readline() {
string command<output_policy::REDIRECTED>::readline() {
std::lock_guard<std::mutex> lck(m_pipelock);
return io_util::readline(m_stdout[PIPE_READ]);
}
@ -171,29 +214,15 @@ string command::readline() {
/**
* Get command output channel
*/
int command::get_stdout(int c) {
int command<output_policy::REDIRECTED>::get_stdout(int c) {
return m_stdout[c];
}
/**
* Get command input channel
*/
int command::get_stdin(int c) {
int command<output_policy::REDIRECTED>::get_stdin(int c) {
return m_stdin[c];
}
/**
* Get command pid
*/
pid_t command::get_pid() {
return m_forkpid;
}
/**
* Get command exit status
*/
int command::get_exit_status() {
return m_forkstatus;
}
POLYBAR_NS_END