aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/signal-safe.hpp
diff options
context:
space:
mode:
authorvirchau13 <[email protected]>2024-04-28 00:38:48 +0800
committerGitHub <[email protected]>2024-04-27 17:38:48 +0100
commit90a53aed59be6bac45c13eac954471d56ac23375 (patch)
treec1a246f6cdf0b9f6d0f1f6ea6f282b4fa5666976 /src/signal-safe.hpp
parent55490637aaf13c8400a0679fcf5b7fca417bc923 (diff)
downloadHyprland-90a53aed59be6bac45c13eac954471d56ac23375.tar.gz
Hyprland-90a53aed59be6bac45c13eac954471d56ac23375.zip
CrashReporter: fix deadlocks by making it mostly async-signal-safe (#5771)
`CrashReporter::createAndSaveCrash()` is not async-signal-safe, resulting in random deadlocks/double-crashes during Hyprland crashes. This changes the function to be (mostly) async-signal-safe.
Diffstat (limited to 'src/signal-safe.hpp')
-rw-r--r--src/signal-safe.hpp172
1 files changed, 172 insertions, 0 deletions
diff --git a/src/signal-safe.hpp b/src/signal-safe.hpp
new file mode 100644
index 00000000..22c825db
--- /dev/null
+++ b/src/signal-safe.hpp
@@ -0,0 +1,172 @@
+#pragma once
+
+#include "defines.hpp"
+
+template <uint16_t N>
+class MaxLengthCString {
+ public:
+ MaxLengthCString() : m_strPos(0), m_boundsExceeded(false) {
+ m_str[0] = '\0';
+ }
+ inline void operator+=(char const* rhs) {
+ write(rhs, strlen(rhs));
+ }
+ void write(char const* data, size_t len) {
+ if (m_boundsExceeded || m_strPos + len >= N) {
+ m_boundsExceeded = true;
+ return;
+ }
+ memcpy(m_str + m_strPos, data, len);
+ m_strPos += len;
+ m_str[m_strPos] = '\0';
+ }
+ void write(char c) {
+ if (m_boundsExceeded || m_strPos + 1 >= N) {
+ m_boundsExceeded = true;
+ return;
+ }
+ m_str[m_strPos] = c;
+ m_strPos++;
+ }
+ void write_num(size_t num) {
+ size_t d = 1;
+ while (num / 10 >= d)
+ d *= 10;
+ while (num > 0) {
+ char c = '0' + (num / d);
+ write(c);
+ num %= d;
+ d /= 10;
+ }
+ }
+ char const* get_str() {
+ return m_str;
+ };
+ bool boundsExceeded() {
+ return m_boundsExceeded;
+ };
+
+ private:
+ char m_str[N];
+ size_t m_strPos;
+ bool m_boundsExceeded;
+};
+
+template <uint16_t BUFSIZE>
+class BufFileWriter {
+ public:
+ inline BufFileWriter(int fd_) : m_writeBufPos(0), m_fd(fd_) {}
+ ~BufFileWriter() {
+ flush();
+ }
+ void write(char const* data, size_t len) {
+ while (len > 0) {
+ size_t to_add = std::min(len, (size_t)BUFSIZE - m_writeBufPos);
+ memcpy(m_writeBuf + m_writeBufPos, data, to_add);
+ data += to_add;
+ len -= to_add;
+ m_writeBufPos += to_add;
+ if (m_writeBufPos == BUFSIZE)
+ flush();
+ }
+ }
+ inline void write(char c) {
+ if (m_writeBufPos == BUFSIZE)
+ flush();
+ m_writeBuf[m_writeBufPos] = c;
+ m_writeBufPos++;
+ }
+ inline void operator+=(char const* str) {
+ write(str, strlen(str));
+ }
+ inline void operator+=(std::string_view str) {
+ write(str.data(), str.size());
+ }
+ inline void operator+=(char c) {
+ write(c);
+ }
+ void writeNum(size_t num) {
+ size_t d = 1;
+ while (num / 10 >= d)
+ d *= 10;
+ while (num > 0) {
+ char c = '0' + (num / d);
+ write(c);
+ num %= d;
+ d /= 10;
+ }
+ }
+ void writeCmdOutput(const char* cmd) {
+ int pipefd[2];
+ if (pipe(pipefd) < 0) {
+ *this += "<pipe(pipefd) failed with";
+ writeNum(errno);
+ *this += ">\n";
+ return;
+ }
+ // terminate child instead of waiting
+ {
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_NOCLDWAIT;
+ act.sa_restorer = NULL;
+ sigaction(SIGCHLD, &act, NULL);
+ }
+ pid_t pid = fork();
+ if (pid < 0) {
+ *this += "<fork() failed with ";
+ writeNum(errno);
+ *this += ">\n";
+ return;
+ }
+ if (pid == 0) {
+ close(pipefd[0]);
+ dup2(pipefd[1], STDOUT_FILENO);
+ char const* const argv[] = {"/bin/sh", "-c", cmd};
+ execv("/bin/sh", (char* const*)argv);
+
+ BufFileWriter<64> failmsg(pipefd[1]);
+ failmsg += "<execv(";
+ failmsg += cmd;
+ failmsg += ") resulted in errno ";
+ failmsg.write(errno);
+ failmsg += ">\n";
+ close(pipefd[1]);
+ abort();
+ } else {
+ close(pipefd[1]);
+ int len;
+ char readbuf[256];
+ while ((len = read(pipefd[0], readbuf, 256)) > 0) {
+ write(readbuf, len);
+ }
+ if (len < 0) {
+ *this += "<interrupted, read() resulted in errno ";
+ writeNum(errno);
+ *this += ">\n";
+ }
+ close(pipefd[0]);
+ }
+ }
+ void flush() {
+ size_t i = 0;
+ while (i < m_writeBufPos) {
+ int written = ::write(m_fd, m_writeBuf + i, m_writeBufPos - i);
+ if (written <= 0) {
+ return;
+ }
+ i += written;
+ }
+ m_writeBufPos = 0;
+ }
+
+ private:
+ char m_writeBuf[BUFSIZE];
+ size_t m_writeBufPos;
+ int m_fd;
+};
+
+char const* sig_getenv(char const* name);
+
+char const* sig_strsignal(int sig);