minecraft-pi-reborn/launcher/src/crash-report.c

252 lines
8.3 KiB
C
Raw Normal View History

2022-05-14 02:36:12 +00:00
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <poll.h>
#include <sys/ioctl.h>
2022-07-14 03:05:59 +00:00
#include <sys/stat.h>
2024-06-08 20:30:39 +00:00
#include <sys/prctl.h>
2022-05-14 02:36:12 +00:00
#include <libreborn/libreborn.h>
#include "crash-report.h"
// Show Crash Report Dialog
#define DIALOG_TITLE "Crash Report"
#define CRASH_REPORT_DIALOG_WIDTH "640"
#define CRASH_REPORT_DIALOG_HEIGHT "480"
static void show_report(const char *log_filename) {
// Fork
pid_t pid = fork();
if (pid == 0) {
// Child
setsid();
2022-10-02 04:47:11 +00:00
ALLOC_CHECK(freopen("/dev/null", "w", stdout));
ALLOC_CHECK(freopen("/dev/null", "w", stderr));
ALLOC_CHECK(freopen("/dev/null", "r", stdin));
const char *command[] = {
"zenity",
"--title", DIALOG_TITLE,
"--name", MCPI_APP_ID,
"--width", CRASH_REPORT_DIALOG_WIDTH,
"--height", CRASH_REPORT_DIALOG_HEIGHT,
"--text-info",
2024-06-15 12:52:15 +00:00
"--text", MCPI_APP_TITLE " has crashed!\n\nNeed help? Consider asking on the <a href=\"" MCPI_DISCORD_INVITE "\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
"--filename", log_filename,
"--no-wrap",
"--font", "Monospace",
2022-10-07 03:19:43 +00:00
"--save-filename", MCPI_VARIANT_NAME "-crash-report.log",
"--ok-label", "Exit",
NULL
};
safe_execvpe(command, (const char *const *) environ);
}
2022-05-14 02:36:12 +00:00
}
// Exit Handler
static void exit_handler(__attribute__((unused)) int signal) {
// Murder
murder_children();
}
// Setup
#define PIPE_READ 0
#define PIPE_WRITE 1
2022-07-14 03:05:59 +00:00
#define MCPI_LOGS_DIR "/tmp/.minecraft-pi-logs"
2022-10-02 04:47:11 +00:00
static char log_filename[] = MCPI_LOGS_DIR "/XXXXXX";
void setup_log_file() {
2022-10-07 03:19:43 +00:00
// Ensure Temporary Directory
{
// Check If It Exists
struct stat tmp_stat;
int exists = stat(MCPI_LOGS_DIR, &tmp_stat) != 0 ? 0 : S_ISDIR(tmp_stat.st_mode);
if (!exists) {
// Doesn't Exist
if (mkdir(MCPI_LOGS_DIR, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
ERR("Unable To Create Temporary Folder: %s", strerror(errno));
}
}
}
2022-10-02 04:47:11 +00:00
// Create Temporary File
2024-05-14 05:23:16 +00:00
int log_file_fd = mkstemp(log_filename);
2022-10-02 04:47:11 +00:00
if (log_file_fd == -1) {
ERR("Unable To Create Log File: %s", strerror(errno));
}
2024-05-14 05:23:16 +00:00
close(log_file_fd);
reborn_set_log(log_filename);
2022-10-02 04:47:11 +00:00
}
2022-05-14 02:36:12 +00:00
void setup_crash_report() {
// Store Output
int output_pipe[2];
safe_pipe2(output_pipe, 0);
int error_pipe[2];
safe_pipe2(error_pipe, 0);
int input_pipe[2];
safe_pipe2(input_pipe, 0);
2022-05-14 02:36:12 +00:00
// Fork
pid_t ret = fork();
if (ret == -1) {
ERR("Unable To Fork: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
// Pipe stdio
dup2(output_pipe[PIPE_WRITE], STDOUT_FILENO);
close(output_pipe[PIPE_READ]);
close(output_pipe[PIPE_WRITE]);
dup2(error_pipe[PIPE_WRITE], STDERR_FILENO);
close(error_pipe[PIPE_READ]);
close(error_pipe[PIPE_WRITE]);
dup2(input_pipe[PIPE_READ], STDIN_FILENO);
close(input_pipe[PIPE_READ]);
close(input_pipe[PIPE_WRITE]);
2022-05-14 02:36:12 +00:00
// Create New Process Group
setpgid(0, 0);
2024-06-08 20:30:39 +00:00
// Kill Child If Parent Exits First
prctl(PR_SET_PDEATHSIG, SIGKILL);
2022-05-14 02:36:12 +00:00
// Continue Execution
} else {
// Parent Process
track_child(ret);
// Install Signal Handlers
2024-05-12 07:19:01 +00:00
struct sigaction act_sigint = {0};
2022-05-14 02:36:12 +00:00
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, NULL);
2024-05-12 07:19:01 +00:00
struct sigaction act_sigterm = {0};
2022-05-14 02:36:12 +00:00
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, NULL);
atexit(murder_children);
2022-05-14 02:36:12 +00:00
// Close Unneeded File Descriptors
close(output_pipe[PIPE_WRITE]);
close(error_pipe[PIPE_WRITE]);
close(input_pipe[PIPE_READ]);
2022-05-14 02:36:12 +00:00
2022-09-22 21:43:21 +00:00
// Set Debug Tag
reborn_debug_tag = "(Crash Reporter) ";
// Setup Logging
2022-05-14 02:36:12 +00:00
#define BUFFER_SIZE 1024
char buf[BUFFER_SIZE];
// Setup Polling
int number_fds = 3;
2022-05-14 02:36:12 +00:00
struct pollfd poll_fds[number_fds];
poll_fds[0].fd = output_pipe[PIPE_READ];
poll_fds[1].fd = error_pipe[PIPE_READ];
poll_fds[2].fd = STDIN_FILENO;
2022-05-14 02:36:12 +00:00
for (int i = 0; i < number_fds; i++) {
poll_fds[i].events = POLLIN;
}
// Poll Data
2022-10-02 04:47:11 +00:00
int status;
while (waitpid(ret, &status, WNOHANG) != ret) {
2022-05-14 02:36:12 +00:00
int poll_ret = poll(poll_fds, number_fds, -1);
if (poll_ret == -1) {
if (errno == EINTR) {
continue;
} else {
ERR("Unable To Poll Data: %s", strerror(errno));
}
}
// Handle Data
for (int i = 0; i < number_fds; i++) {
if (poll_fds[i].revents != 0) {
if (poll_fds[i].revents & POLLIN) {
if (poll_fds[i].fd == STDIN_FILENO) {
// Data Available From stdin
int bytes_available;
if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) {
bytes_available = 0;
}
// Read
2024-05-14 05:23:16 +00:00
ssize_t bytes_read = read(poll_fds[i].fd, buf, BUFFER_SIZE);
if (bytes_read == -1) {
2024-05-14 05:23:16 +00:00
ERR("Unable To Read Input: %s", strerror(errno));
}
// Write To Child
2024-05-14 05:23:16 +00:00
if (write(input_pipe[PIPE_WRITE], buf, bytes_read) == -1) {
ERR("Unable To Write Input To Child: %s", strerror(errno));
}
} else {
// Data Available From Child's stdout/stderr
2024-05-14 05:23:16 +00:00
ssize_t bytes_read = read(poll_fds[i].fd, buf, BUFFER_SIZE - 1 /* Account For NULL-Terminator */);
if (bytes_read == -1) {
ERR("Unable To Read Log Data: %s", strerror(errno));
}
// Print To Terminal
buf[bytes_read] = '\0';
2022-10-02 04:47:11 +00:00
fprintf(poll_fds[i].fd == output_pipe[PIPE_READ] ? stdout : stderr, "%s", buf);
// Write To log
2024-05-14 05:23:16 +00:00
if (write(reborn_get_log_fd(), buf, bytes_read) == -1) {
ERR("Unable To Write Log Data: %s", strerror(errno));
}
2022-05-14 02:36:12 +00:00
}
} else {
// File Descriptor No Longer Accessible
poll_fds[i].events = 0;
2022-05-14 02:36:12 +00:00
}
}
}
}
2022-10-02 04:47:11 +00:00
// Untrack Process
2022-05-14 02:36:12 +00:00
untrack_child(ret);
2022-10-02 04:47:11 +00:00
// Close Pipes
close(output_pipe[PIPE_READ]);
close(error_pipe[PIPE_READ]);
close(input_pipe[PIPE_WRITE]);
2022-05-14 02:36:12 +00:00
// Check If Is Crash
2022-05-15 17:51:28 +00:00
int is_crash = !is_exit_status_success(status);
2022-05-14 02:36:12 +00:00
// Log Exit Code To log If Crash
if (is_crash) {
// Create Exit Code Log Line
2022-05-15 17:51:28 +00:00
char *exit_status = NULL;
get_exit_status_string(status, &exit_status);
2022-05-14 02:36:12 +00:00
char *exit_code_line = NULL;
2022-05-15 17:51:28 +00:00
safe_asprintf(&exit_code_line, "[CRASH]: Terminated%s\n", exit_status);
free(exit_status);
2022-05-14 02:36:12 +00:00
// Print Exit Code Log Line
fprintf(stderr, "%s", exit_code_line);
// Write Exit Code Log Line
2024-05-14 05:23:16 +00:00
if (write(reborn_get_log_fd(), exit_code_line, strlen(exit_code_line)) == -1) {
2022-05-14 02:36:12 +00:00
ERR("Unable To Write Exit Code To Log: %s", strerror(errno));
}
// Free Exit Code Log Line
free(exit_code_line);
}
2024-05-14 05:23:16 +00:00
// Close Log File
reborn_close_log();
2024-05-18 22:58:39 +00:00
unsetenv(MCPI_LOG_ENV);
2022-05-14 02:36:12 +00:00
// Show Crash Log
2024-06-15 12:52:15 +00:00
if (is_crash && !reborn_is_headless()) {
2022-05-14 02:36:12 +00:00
show_report(log_filename);
}
// Exit
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
}
}