mirror of
https://github.com/rad4day/Waybar.git
synced 2023-12-21 10:22:59 +01:00
246f7bf555
When forkExec is called it begins to ignore all SIGCHLD signals for the rest of the progam's execution so that they are automatically reaped. However, this means that subsequent waitpid calls in the exec function will always fail. So instead handle SIGCHLD by reaping any processes created by forkExec and ignoring all others so that they can be handled directly by the exec function.
129 lines
2.8 KiB
C++
129 lines
2.8 KiB
C++
#pragma once
|
|
|
|
#include <giomm.h>
|
|
#include <spdlog/spdlog.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
#include <array>
|
|
|
|
extern sig_atomic_t is_inserting_pid;
|
|
extern std::list<pid_t> reap;
|
|
|
|
namespace waybar::util::command {
|
|
|
|
struct res {
|
|
int exit_code;
|
|
std::string out;
|
|
};
|
|
|
|
inline std::string read(FILE* fp) {
|
|
std::array<char, 128> buffer = {0};
|
|
std::string output;
|
|
while (feof(fp) == 0) {
|
|
if (fgets(buffer.data(), 128, fp) != nullptr) {
|
|
output += buffer.data();
|
|
}
|
|
}
|
|
|
|
// Remove last newline
|
|
if (!output.empty() && output[output.length() - 1] == '\n') {
|
|
output.erase(output.length() - 1);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
inline int close(FILE* fp, pid_t pid) {
|
|
int stat = -1;
|
|
pid_t ret;
|
|
|
|
fclose(fp);
|
|
do {
|
|
ret = waitpid(pid, &stat, WCONTINUED | WUNTRACED);
|
|
|
|
if (WIFEXITED(stat)) {
|
|
spdlog::debug("Cmd exited with code {}", WEXITSTATUS(stat));
|
|
} else if (WIFSIGNALED(stat)) {
|
|
spdlog::debug("Cmd killed by {}", WTERMSIG(stat));
|
|
} else if (WIFSTOPPED(stat)) {
|
|
spdlog::debug("Cmd stopped by {}", WSTOPSIG(stat));
|
|
} else if (WIFCONTINUED(stat)) {
|
|
spdlog::debug("Cmd continued");
|
|
} else if (ret == -1) {
|
|
spdlog::debug("waitpid failed: {}", strerror(errno));
|
|
} else {
|
|
break;
|
|
}
|
|
} while (!WIFEXITED(stat) && !WIFSIGNALED(stat));
|
|
return stat;
|
|
}
|
|
|
|
inline FILE* open(const std::string& cmd, int& pid) {
|
|
if (cmd == "") return nullptr;
|
|
int fd[2];
|
|
pipe(fd);
|
|
|
|
pid_t child_pid = fork();
|
|
|
|
if (child_pid < 0) {
|
|
spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno));
|
|
return nullptr;
|
|
}
|
|
|
|
if (!child_pid) {
|
|
::close(fd[0]);
|
|
dup2(fd[1], 1);
|
|
setpgid(child_pid, child_pid);
|
|
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
|
exit(0);
|
|
} else {
|
|
::close(fd[1]);
|
|
}
|
|
pid = child_pid;
|
|
return fdopen(fd[0], "r");
|
|
}
|
|
|
|
inline struct res exec(const std::string& cmd) {
|
|
int pid;
|
|
auto fp = command::open(cmd, pid);
|
|
if (!fp) return {-1, ""};
|
|
auto output = command::read(fp);
|
|
auto stat = command::close(fp, pid);
|
|
return {WEXITSTATUS(stat), output};
|
|
}
|
|
|
|
inline struct res execNoRead(const std::string& cmd) {
|
|
int pid;
|
|
auto fp = command::open(cmd, pid);
|
|
if (!fp) return {-1, ""};
|
|
auto stat = command::close(fp, pid);
|
|
return {WEXITSTATUS(stat), ""};
|
|
}
|
|
|
|
inline int32_t forkExec(const std::string& cmd) {
|
|
if (cmd == "") return -1;
|
|
|
|
int32_t pid = fork();
|
|
|
|
if (pid < 0) {
|
|
spdlog::error("Unable to exec cmd {}, error {}", cmd.c_str(), strerror(errno));
|
|
return pid;
|
|
}
|
|
|
|
// Child executes the command
|
|
if (!pid) {
|
|
setpgid(pid, pid);
|
|
signal(SIGCHLD, SIG_DFL);
|
|
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
|
exit(0);
|
|
} else {
|
|
is_inserting_pid = true;
|
|
reap.push_back(pid);
|
|
is_inserting_pid = false;
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
|
|
} // namespace waybar::util::command
|