diff options
author | virchau13 <[email protected]> | 2024-04-28 00:38:48 +0800 |
---|---|---|
committer | GitHub <[email protected]> | 2024-04-27 17:38:48 +0100 |
commit | 90a53aed59be6bac45c13eac954471d56ac23375 (patch) | |
tree | c1a246f6cdf0b9f6d0f1f6ea6f282b4fa5666976 /src/signal-safe.hpp | |
parent | 55490637aaf13c8400a0679fcf5b7fca417bc923 (diff) | |
download | Hyprland-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.hpp | 172 |
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); |