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>
2022-05-29 22:44:27 +00:00
# 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 ) {
2022-10-01 05:37:20 +00:00
// 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 ) ) ;
2022-10-01 05:37:20 +00:00
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 \n Need 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> " ,
2022-10-01 05:37:20 +00:00
" --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 " ,
2022-10-01 05:37:20 +00:00
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
2022-05-29 22:44:27 +00:00
# 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 ) ;
2022-05-29 22:44:27 +00:00
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
2022-05-29 22:44:27 +00:00
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 ) ;
2022-10-01 05:37:20 +00:00
atexit ( murder_children ) ;
2022-05-14 02:36:12 +00:00
// Close Unneeded File Descriptors
2022-05-29 22:44:27 +00:00
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) " ;
2022-05-29 22:44:27 +00:00
// Setup Logging
2022-05-14 02:36:12 +00:00
# define BUFFER_SIZE 1024
char buf [ BUFFER_SIZE ] ;
// Setup Polling
2022-05-29 22:44:27 +00:00
int number_fds = 3 ;
2022-05-14 02:36:12 +00:00
struct pollfd poll_fds [ number_fds ] ;
2022-05-29 22:44:27 +00:00
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 ) {
2022-05-29 22:44:27 +00:00
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 ) ;
2022-05-29 22:44:27 +00:00
if ( bytes_read = = - 1 ) {
2024-05-14 05:23:16 +00:00
ERR ( " Unable To Read Input: %s " , strerror ( errno ) ) ;
2022-05-29 22:44:27 +00:00
}
// Write To Child
2024-05-14 05:23:16 +00:00
if ( write ( input_pipe [ PIPE_WRITE ] , buf , bytes_read ) = = - 1 ) {
2022-05-29 22:44:27 +00:00
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 */ ) ;
2022-05-29 22:44:27 +00:00
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 ) ;
2022-05-29 22:44:27 +00:00
// Write To log
2024-05-14 05:23:16 +00:00
if ( write ( reborn_get_log_fd ( ) , buf , bytes_read ) = = - 1 ) {
2022-05-29 22:44:27 +00:00
ERR ( " Unable To Write Log Data: %s " , strerror ( errno ) ) ;
}
2022-05-14 02:36:12 +00:00
}
} else {
// File Descriptor No Longer Accessible
2022-05-29 22:44:27 +00:00
poll_fds [ i ] . events = 0 ;
2022-05-14 02:36:12 +00:00
}
}
}
}
2022-05-29 22:44:27 +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
2022-05-29 22:44:27 +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 ) ;
}
}