2018-08-18 17:54:20 +02:00
|
|
|
#pragma once
|
|
|
|
|
2019-01-08 21:05:44 +01:00
|
|
|
#include <giomm.h>
|
2020-05-22 20:57:41 +02:00
|
|
|
#include <spdlog/spdlog.h>
|
2020-05-24 21:33:38 +02:00
|
|
|
#include <sys/wait.h>
|
2018-12-26 11:13:36 +01:00
|
|
|
#include <unistd.h>
|
2020-05-24 21:33:38 +02:00
|
|
|
|
2019-08-09 11:05:34 +02:00
|
|
|
#include <array>
|
2018-08-18 17:54:20 +02:00
|
|
|
|
2020-07-21 04:36:48 +02:00
|
|
|
extern sig_atomic_t is_inserting_pid;
|
|
|
|
extern std::list<pid_t> reap;
|
|
|
|
|
2018-08-18 17:54:20 +02:00
|
|
|
namespace waybar::util::command {
|
|
|
|
|
2018-09-05 19:20:19 +02:00
|
|
|
struct res {
|
2019-04-18 17:52:00 +02:00
|
|
|
int exit_code;
|
2018-08-18 17:54:20 +02:00
|
|
|
std::string out;
|
|
|
|
};
|
|
|
|
|
2019-04-23 15:56:38 +02:00
|
|
|
inline std::string read(FILE* fp) {
|
2018-08-18 17:54:20 +02:00
|
|
|
std::array<char, 128> buffer = {0};
|
2019-04-18 17:52:00 +02:00
|
|
|
std::string output;
|
2018-08-18 17:54:20 +02:00
|
|
|
while (feof(fp) == 0) {
|
|
|
|
if (fgets(buffer.data(), 128, fp) != nullptr) {
|
|
|
|
output += buffer.data();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove last newline
|
2019-04-18 17:52:00 +02:00
|
|
|
if (!output.empty() && output[output.length() - 1] == '\n') {
|
|
|
|
output.erase(output.length() - 1);
|
2018-08-18 17:54:20 +02:00
|
|
|
}
|
2019-04-23 15:56:38 +02:00
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int close(FILE* fp, pid_t pid) {
|
2020-05-22 20:57:41 +02:00
|
|
|
int stat = -1;
|
2020-07-21 04:36:48 +02:00
|
|
|
pid_t ret;
|
2019-04-23 15:56:38 +02:00
|
|
|
|
|
|
|
fclose(fp);
|
2020-05-22 20:57:41 +02:00
|
|
|
do {
|
2020-07-21 04:36:48 +02:00
|
|
|
ret = waitpid(pid, &stat, WCONTINUED | WUNTRACED);
|
2020-05-24 18:27:10 +02:00
|
|
|
|
2020-05-22 20:57:41 +02:00
|
|
|
if (WIFEXITED(stat)) {
|
2020-05-24 22:14:17 +02:00
|
|
|
spdlog::debug("Cmd exited with code {}", WEXITSTATUS(stat));
|
2020-05-22 20:57:41 +02:00
|
|
|
} else if (WIFSIGNALED(stat)) {
|
2020-05-24 22:14:17 +02:00
|
|
|
spdlog::debug("Cmd killed by {}", WTERMSIG(stat));
|
2020-05-22 20:57:41 +02:00
|
|
|
} else if (WIFSTOPPED(stat)) {
|
2020-05-24 22:14:17 +02:00
|
|
|
spdlog::debug("Cmd stopped by {}", WSTOPSIG(stat));
|
2020-05-22 20:57:41 +02:00
|
|
|
} else if (WIFCONTINUED(stat)) {
|
2020-05-24 22:14:17 +02:00
|
|
|
spdlog::debug("Cmd continued");
|
2020-07-21 04:36:48 +02:00
|
|
|
} else if (ret == -1) {
|
|
|
|
spdlog::debug("waitpid failed: {}", strerror(errno));
|
2020-05-22 20:57:41 +02:00
|
|
|
} else {
|
2019-04-23 15:56:38 +02:00
|
|
|
break;
|
|
|
|
}
|
2020-05-22 20:57:41 +02:00
|
|
|
} while (!WIFEXITED(stat) && !WIFSIGNALED(stat));
|
2019-04-23 15:56:38 +02:00
|
|
|
return stat;
|
2018-08-18 17:54:20 +02:00
|
|
|
}
|
|
|
|
|
2020-05-24 21:33:38 +02:00
|
|
|
inline FILE* open(const std::string& cmd, int& pid) {
|
2019-04-23 15:56:38 +02:00
|
|
|
if (cmd == "") return nullptr;
|
|
|
|
int fd[2];
|
|
|
|
pipe(fd);
|
|
|
|
|
|
|
|
pid_t child_pid = fork();
|
|
|
|
|
|
|
|
if (child_pid < 0) {
|
2020-05-24 22:14:17 +02:00
|
|
|
spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno));
|
2019-04-23 15:56:38 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!child_pid) {
|
|
|
|
::close(fd[0]);
|
|
|
|
dup2(fd[1], 1);
|
|
|
|
setpgid(child_pid, child_pid);
|
2020-05-22 20:57:41 +02:00
|
|
|
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
2019-04-23 15:56:38 +02:00
|
|
|
exit(0);
|
|
|
|
} else {
|
|
|
|
::close(fd[1]);
|
|
|
|
}
|
|
|
|
pid = child_pid;
|
|
|
|
return fdopen(fd[0], "r");
|
|
|
|
}
|
|
|
|
|
2020-05-24 21:33:38 +02:00
|
|
|
inline struct res exec(const std::string& cmd) {
|
2019-04-23 15:56:38 +02:00
|
|
|
int pid;
|
|
|
|
auto fp = command::open(cmd, pid);
|
|
|
|
if (!fp) return {-1, ""};
|
|
|
|
auto output = command::read(fp);
|
|
|
|
auto stat = command::close(fp, pid);
|
2020-05-22 20:57:41 +02:00
|
|
|
return {WEXITSTATUS(stat), output};
|
2019-04-23 15:56:38 +02:00
|
|
|
}
|
|
|
|
|
2020-05-24 21:33:38 +02:00
|
|
|
inline struct res execNoRead(const std::string& cmd) {
|
2020-05-24 18:27:10 +02:00
|
|
|
int pid;
|
|
|
|
auto fp = command::open(cmd, pid);
|
|
|
|
if (!fp) return {-1, ""};
|
|
|
|
auto stat = command::close(fp, pid);
|
|
|
|
return {WEXITSTATUS(stat), ""};
|
|
|
|
}
|
|
|
|
|
2020-05-24 21:33:38 +02:00
|
|
|
inline int32_t forkExec(const std::string& cmd) {
|
2019-04-23 15:56:38 +02:00
|
|
|
if (cmd == "") return -1;
|
2018-10-29 18:04:09 +01:00
|
|
|
|
|
|
|
int32_t pid = fork();
|
|
|
|
|
|
|
|
if (pid < 0) {
|
2020-05-24 22:14:17 +02:00
|
|
|
spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno));
|
2019-04-23 15:56:38 +02:00
|
|
|
return pid;
|
2018-10-29 18:04:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Child executes the command
|
2019-04-23 15:56:38 +02:00
|
|
|
if (!pid) {
|
|
|
|
setpgid(pid, pid);
|
2020-03-27 17:35:21 +01:00
|
|
|
signal(SIGCHLD, SIG_DFL);
|
2019-04-23 15:56:38 +02:00
|
|
|
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
|
|
|
exit(0);
|
2019-06-05 14:35:25 +02:00
|
|
|
} else {
|
2020-07-21 04:36:48 +02:00
|
|
|
is_inserting_pid = true;
|
|
|
|
reap.push_back(pid);
|
|
|
|
is_inserting_pid = false;
|
2019-04-23 15:56:38 +02:00
|
|
|
}
|
2018-10-29 18:04:09 +01:00
|
|
|
|
2019-04-23 15:56:38 +02:00
|
|
|
return pid;
|
2018-08-18 17:54:20 +02:00
|
|
|
}
|
2018-10-29 18:04:09 +01:00
|
|
|
|
|
|
|
} // namespace waybar::util::command
|