2016-11-12 09:36:22 -05:00
|
|
|
#include <sys/wait.h>
|
2016-11-20 17:04:31 -05:00
|
|
|
#include <unistd.h>
|
2016-11-02 15:22:45 -04:00
|
|
|
#include <csignal>
|
2017-01-10 23:10:51 -05:00
|
|
|
#include <cstdlib>
|
2016-11-25 07:55:15 -05:00
|
|
|
#include <utility>
|
2019-03-07 00:22:00 -05:00
|
|
|
#include <utils/string.hpp>
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-25 07:55:15 -05:00
|
|
|
#include "errors.hpp"
|
2016-11-02 15:22:45 -04:00
|
|
|
#include "utils/command.hpp"
|
|
|
|
#include "utils/io.hpp"
|
|
|
|
#include "utils/process.hpp"
|
|
|
|
|
2017-01-10 23:10:51 -05:00
|
|
|
#ifndef STDOUT_FILENO
|
|
|
|
#define STDOUT_FILENO 1
|
|
|
|
#endif
|
|
|
|
#ifndef STDERR_FILENO
|
|
|
|
#define STDERR_FILENO 2
|
|
|
|
#endif
|
|
|
|
|
2016-11-19 00:22:44 -05:00
|
|
|
POLYBAR_NS
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2019-03-07 00:22:00 -05:00
|
|
|
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)) {
|
2016-12-19 23:05:43 -05:00
|
|
|
if (pipe(m_stdin) != 0) {
|
|
|
|
throw command_error("Failed to allocate input stream");
|
|
|
|
}
|
|
|
|
if (pipe(m_stdout) != 0) {
|
|
|
|
throw command_error("Failed to allocate output stream");
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
2016-12-19 23:05:43 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2019-03-07 00:22:00 -05:00
|
|
|
command<output_policy::REDIRECTED>::~command() {
|
2016-12-19 23:05:43 -05:00
|
|
|
if (m_stdin[PIPE_READ] > 0) {
|
|
|
|
close(m_stdin[PIPE_READ]);
|
|
|
|
}
|
|
|
|
if (m_stdin[PIPE_WRITE] > 0) {
|
|
|
|
close(m_stdin[PIPE_WRITE]);
|
|
|
|
}
|
|
|
|
if (m_stdout[PIPE_READ] > 0) {
|
|
|
|
close(m_stdout[PIPE_READ]);
|
|
|
|
}
|
|
|
|
if (m_stdout[PIPE_WRITE] > 0) {
|
|
|
|
close(m_stdout[PIPE_WRITE]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute the command
|
|
|
|
*/
|
2019-03-07 00:22:00 -05:00
|
|
|
int command<output_policy::REDIRECTED>::exec(bool wait_for_completion) {
|
2016-12-19 23:05:43 -05:00
|
|
|
if ((m_forkpid = fork()) == -1) {
|
|
|
|
throw system_error("Failed to fork process");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (process_util::in_forked_process(m_forkpid)) {
|
|
|
|
if (dup2(m_stdin[PIPE_READ], STDIN_FILENO) == -1) {
|
|
|
|
throw command_error("Failed to redirect stdin in child process");
|
|
|
|
}
|
|
|
|
if (dup2(m_stdout[PIPE_WRITE], STDOUT_FILENO) == -1) {
|
|
|
|
throw command_error("Failed to redirect stdout in child process");
|
|
|
|
}
|
|
|
|
if (dup2(m_stdout[PIPE_WRITE], STDERR_FILENO) == -1) {
|
|
|
|
throw command_error("Failed to redirect stderr in child process");
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-19 23:05:43 -05:00
|
|
|
// Close file descriptors that won't be used by the child
|
|
|
|
if ((m_stdin[PIPE_READ] = close(m_stdin[PIPE_READ])) == -1) {
|
|
|
|
throw command_error("Failed to close fd");
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-12-19 23:05:43 -05:00
|
|
|
if ((m_stdin[PIPE_WRITE] = close(m_stdin[PIPE_WRITE])) == -1) {
|
|
|
|
throw command_error("Failed to close fd");
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-12-19 23:05:43 -05:00
|
|
|
if ((m_stdout[PIPE_READ] = close(m_stdout[PIPE_READ])) == -1) {
|
|
|
|
throw command_error("Failed to close fd");
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-12-19 23:05:43 -05:00
|
|
|
if ((m_stdout[PIPE_WRITE] = close(m_stdout[PIPE_WRITE])) == -1) {
|
|
|
|
throw command_error("Failed to close fd");
|
2016-11-25 07:55:15 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-19 23:05:43 -05:00
|
|
|
setpgid(m_forkpid, 0);
|
|
|
|
process_util::exec_sh(m_cmd.c_str());
|
|
|
|
} else {
|
|
|
|
// Close file descriptors that won't be used by the parent
|
|
|
|
if ((m_stdin[PIPE_READ] = close(m_stdin[PIPE_READ])) == -1) {
|
|
|
|
throw command_error("Failed to close fd");
|
|
|
|
}
|
|
|
|
if ((m_stdout[PIPE_WRITE] = close(m_stdout[PIPE_WRITE])) == -1) {
|
|
|
|
throw command_error("Failed to close fd");
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
2016-12-19 23:05:43 -05:00
|
|
|
if (wait_for_completion) {
|
|
|
|
auto status = wait();
|
|
|
|
m_forkpid = -1;
|
|
|
|
return status;
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
}
|
|
|
|
|
2016-12-19 23:05:43 -05:00
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tail command output
|
|
|
|
*
|
2018-11-04 10:08:54 -05:00
|
|
|
* \note: This is a blocking call and will not
|
2016-12-19 23:05:43 -05:00
|
|
|
* end until the stream is closed
|
|
|
|
*/
|
2019-03-07 00:22:00 -05:00
|
|
|
void command<output_policy::REDIRECTED>::tail(callback<string> cb) {
|
|
|
|
io_util::tail(m_stdout[PIPE_READ], cb);
|
2016-12-19 23:05:43 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-19 23:05:43 -05:00
|
|
|
/**
|
|
|
|
* Write line to command input channel
|
|
|
|
*/
|
2019-03-07 00:22:00 -05:00
|
|
|
int command<output_policy::REDIRECTED>::writeline(string data) {
|
2016-12-19 23:05:43 -05:00
|
|
|
std::lock_guard<std::mutex> lck(m_pipelock);
|
2019-03-07 00:22:00 -05:00
|
|
|
return static_cast<int>(io_util::writeline(m_stdin[PIPE_WRITE], data));
|
2016-12-19 23:05:43 -05:00
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-19 23:05:43 -05:00
|
|
|
/**
|
|
|
|
* Read a line from the commands output stream
|
|
|
|
*/
|
2019-03-07 00:22:00 -05:00
|
|
|
string command<output_policy::REDIRECTED>::readline() {
|
2016-12-19 23:05:43 -05:00
|
|
|
std::lock_guard<std::mutex> lck(m_pipelock);
|
|
|
|
return io_util::readline(m_stdout[PIPE_READ]);
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-19 23:05:43 -05:00
|
|
|
/**
|
|
|
|
* Get command output channel
|
|
|
|
*/
|
2019-03-07 00:22:00 -05:00
|
|
|
int command<output_policy::REDIRECTED>::get_stdout(int c) {
|
2016-12-19 23:05:43 -05:00
|
|
|
return m_stdout[c];
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-12-19 23:05:43 -05:00
|
|
|
/**
|
|
|
|
* Get command input channel
|
|
|
|
*/
|
2019-03-07 00:22:00 -05:00
|
|
|
int command<output_policy::REDIRECTED>::get_stdin(int c) {
|
2016-12-19 23:05:43 -05:00
|
|
|
return m_stdin[c];
|
|
|
|
}
|
2016-11-02 15:22:45 -04:00
|
|
|
|
2016-11-19 00:22:44 -05:00
|
|
|
POLYBAR_NS_END
|