diff --git a/src/os/macosx/crashlog_osx.cpp b/src/os/macosx/crashlog_osx.cpp index 9f3038e51b..0fe1f4ad61 100644 --- a/src/os/macosx/crashlog_osx.cpp +++ b/src/os/macosx/crashlog_osx.cpp @@ -48,23 +48,35 @@ #define MAX_STACK_FRAMES 64 #if !defined(WITHOUT_DBG_LLDB) -static bool ExecReadStdout(const char *file, char *const *args, char *&buffer, const char *last) +static bool ExecReadStdoutThroughFile(const char *file, char *const *args, char *&buffer, const char *last) { int null_fd = open("/dev/null", O_RDWR); if (null_fd == -1) return false; - int pipefd[2]; - if (pipe(pipefd) == -1) return false; + char name[MAX_PATH]; + extern std::string _personal_dir; + seprintf(name, lastof(name), "%sopenttd-tmp-XXXXXX", _personal_dir.c_str()); + int fd = mkstemp(name); + if (fd == -1) { + close(null_fd); + return false; + } + + /* Unlink file but leave fd open until finished with */ + unlink(name); int pid = fork(); - if (pid < 0) return false; + if (pid < 0) { + close(null_fd); + close(fd); + return false; + } if (pid == 0) { /* child */ - close(pipefd[0]); /* Close unused read end */ - dup2(pipefd[1], STDOUT_FILENO); - close(pipefd[1]); + dup2(fd, STDOUT_FILENO); + close(fd); dup2(null_fd, STDERR_FILENO); dup2(null_fd, STDIN_FILENO); close(null_fd); @@ -76,30 +88,29 @@ static bool ExecReadStdout(const char *file, char *const *args, char *&buffer, c /* parent */ close(null_fd); - close(pipefd[1]); /* Close unused write end */ - - while (buffer < last) { - ssize_t res = read(pipefd[0], buffer, last - buffer); - if (res < 0) { - if (errno == EINTR) continue; - break; - } else if (res == 0) { - break; - } else { - buffer += res; - } - } - buffer += seprintf(buffer, last, "\n"); - - close(pipefd[0]); /* close read end */ int status; int wait_ret = waitpid(pid, &status, 0); if (wait_ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { /* command did not appear to run successfully */ + close(fd); return false; } else { /* command executed successfully */ + lseek(fd, 0, SEEK_SET); + while (buffer < last) { + ssize_t res = read(fd, buffer, last - buffer); + if (res < 0) { + if (errno == EINTR) continue; + break; + } else if (res == 0) { + break; + } else { + buffer += res; + } + } + buffer += seprintf(buffer, last, "\n"); + close(fd); return true; } } @@ -284,7 +295,7 @@ class CrashLogOSX : public CrashLog { } args.push_back(nullptr); - if (!ExecReadStdout("lldb", const_cast(&(args[0])), buffer, last)) { + if (!ExecReadStdoutThroughFile("lldb", const_cast(&(args[0])), buffer, last)) { buffer = buffer_orig; } #endif /* !WITHOUT_DBG_LLDB */ diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index 5037bfa9c6..ac5277c02d 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -128,6 +128,73 @@ static bool ExecReadStdout(const char *file, char *const *args, char *&buffer, c } } +static bool ExecReadStdoutThroughFile(const char *file, char *const *args, char *&buffer, const char *last) +{ + int null_fd = open("/dev/null", O_RDWR); + if (null_fd == -1) return false; + + char name[MAX_PATH]; + extern std::string _personal_dir; + seprintf(name, lastof(name), "%sopenttd-tmp-XXXXXX", _personal_dir.c_str()); + int fd = mkstemp(name); + if (fd == -1) { + close(null_fd); + return false; + } + + /* Unlink file but leave fd open until finished with */ + unlink(name); + + int pid = fork(); + if (pid < 0) { + close(null_fd); + close(fd); + return false; + } + + if (pid == 0) { + /* child */ + + dup2(fd, STDOUT_FILENO); + close(fd); + dup2(null_fd, STDERR_FILENO); + dup2(null_fd, STDIN_FILENO); + close(null_fd); + + execvp(file, args); + exit(42); + } + + /* parent */ + + close(null_fd); + + int status; + int wait_ret = waitpid(pid, &status, 0); + if (wait_ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { + /* command did not appear to run successfully */ + close(fd); + return false; + } else { + /* command executed successfully */ + lseek(fd, 0, SEEK_SET); + while (buffer < last) { + ssize_t res = read(fd, buffer, last - buffer); + if (res < 0) { + if (errno == EINTR) continue; + break; + } else if (res == 0) { + break; + } else { + buffer += res; + } + } + buffer += seprintf(buffer, last, "\n"); + close(fd); + return true; + } +} + /** * Unix implementation for the crash logger. */ @@ -357,7 +424,7 @@ class CrashLogUnix : public CrashLog { #endif args.push_back(nullptr); - if (!ExecReadStdout("gdb", const_cast(&(args[0])), buffer, last)) { + if (!ExecReadStdoutThroughFile("gdb", const_cast(&(args[0])), buffer, last)) { buffer = buffer_orig; } #endif /* WITH_DBG_GDB */