diff options
Diffstat (limited to 'hardinfo2')
-rw-r--r-- | hardinfo2/crash.c | 445 | ||||
-rw-r--r-- | hardinfo2/crash.h | 29 |
2 files changed, 474 insertions, 0 deletions
diff --git a/hardinfo2/crash.c b/hardinfo2/crash.c new file mode 100644 index 00000000..7f749947 --- /dev/null +++ b/hardinfo2/crash.c @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2002-2008 Leandro Pereira <leandro@hardinfo.org> + * Copyright (c) 2002 the Sylpheed Claws Team and Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <glib.h> +#include <gtk/gtk.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <time.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <sys/utsname.h> + +#include <gnu/libc-version.h> + +#include "crash.h" +#include "hardinfo.h" +#include "config.h" +#include "iconcache.h" + +#if !GLIB_CHECK_VERSION(2,8,0) +#undef CRASH_DIALOG +#endif + +/***/ + +static GtkWidget * +crash_dialog_show(const gchar * text, + const gchar * debug_output); +static gboolean crash_create_debugger_file(void); +static void +crash_debug(unsigned long crash_pid, + gchar * exe_image, GString * debug_output); +static const gchar *get_compiled_in_features(void); +static const gchar *get_lib_version(void); +static const gchar *get_operating_system(void); +static gboolean is_crash_dialog_allowed(void); +static void crash_handler(int sig); +static void crash_cleanup_exit(void); + +/***/ + +static const gchar *DEBUG_SCRIPT = "bt full\n" "kill\n" "quit\n"; + +/***/ + +/* + * ! \brief install crash handlers + */ +void +crash_install_handlers(void) +{ +#if CRASH_DIALOG + sigset_t mask; + + if (!is_crash_dialog_allowed()) + return; + + sigemptyset(&mask); + +#ifdef SIGSEGV + signal(SIGSEGV, crash_handler); + sigaddset(&mask, SIGSEGV); +#endif + +#ifdef SIGFPE + signal(SIGFPE, crash_handler); + sigaddset(&mask, SIGFPE); +#endif + +#ifdef SIGILL + signal(SIGILL, crash_handler); + sigaddset(&mask, SIGILL); +#endif + +#ifdef SIGABRT + signal(SIGABRT, crash_handler); + sigaddset(&mask, SIGABRT); +#endif + + sigprocmask(SIG_UNBLOCK, &mask, 0); + +#endif /* CRASH_DIALOG */ +} + +/***/ + +/* + * ! \brief crash dialog entry point + */ +void +crash_main(const char *arg) +{ +#if CRASH_DIALOG + gchar *text; + gchar **tokens; + unsigned long pid; + GString *output; + + crash_create_debugger_file(); + tokens = g_strsplit(arg, ",", 0); + + pid = atol(tokens[0]); + text = g_strdup_printf("HardInfo process (%ld) died due to an error (%s)", + pid, g_strsignal(atol(tokens[1]))); + + output = g_string_new(""); + crash_debug(pid, tokens[2], output); + + crash_dialog_show(text, output->str); + g_string_free(output, TRUE); + g_free(text); + g_strfreev(tokens); +#endif /* CRASH_DIALOG */ +} + +/* + * ! \brief (can't get pixmap working, so discarding it) + */ +static GtkWidget * +crash_dialog_show(const gchar * text, + const gchar * debug_output) +{ + GtkWidget *window1; + GtkWidget *vbox1; + GtkWidget *hbox1; + GtkWidget *label1; + GtkWidget *frame1; + GtkWidget *scrolledwindow1; + GtkWidget *text1; + GtkWidget *hbuttonbox3; + GtkWidget *hbuttonbox4; + GtkWidget *button3; + GtkWidget *pixwid; + GtkTextBuffer *buffer; + GtkTextIter iter; + gchar *crash_report, *header; + + window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width(GTK_CONTAINER(window1), 5); + gtk_window_set_title(GTK_WINDOW(window1), "HardInfo has crashed"); + gtk_window_set_position(GTK_WINDOW(window1), GTK_WIN_POS_CENTER); + gtk_window_set_modal(GTK_WINDOW(window1), TRUE); + gtk_window_set_default_size(GTK_WINDOW(window1), 460, 350); + + vbox1 = gtk_vbox_new(FALSE, 2); + gtk_widget_show(vbox1); + gtk_container_add(GTK_CONTAINER(window1), vbox1); + + hbox1 = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hbox1); + gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, TRUE, 0); + gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4); + + pixwid = gtk_image_new_from_stock(GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG); + gtk_widget_show(pixwid); + gtk_box_pack_start(GTK_BOX(hbox1), pixwid, TRUE, TRUE, 0); + + header = g_strdup_printf("%s.\nPlease file a bug report and include the information below.", text); + label1 = gtk_label_new(header); + g_free(header); + + gtk_widget_show(label1); + gtk_box_pack_start(GTK_BOX(hbox1), label1, TRUE, TRUE, 0); + gtk_misc_set_alignment(GTK_MISC(label1), 0, 0.5); + + frame1 = gtk_frame_new("Debug log"); + gtk_widget_show(frame1); + gtk_box_pack_start(GTK_BOX(vbox1), frame1, TRUE, TRUE, 0); + + scrolledwindow1 = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(scrolledwindow1); + gtk_container_add(GTK_CONTAINER(frame1), scrolledwindow1); + gtk_container_set_border_width(GTK_CONTAINER(scrolledwindow1), 3); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow1), + GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + + text1 = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text1), FALSE); + gtk_widget_show(text1); + gtk_container_add(GTK_CONTAINER(scrolledwindow1), text1); + + crash_report = + g_strdup_printf + ("HardInfo version %s\nGTK+ version %d.%d.%d\nFeatures: %s\nOperating system: %s\nC Library: %s\n--\n%s", + VERSION, gtk_major_version, gtk_minor_version, gtk_micro_version, + get_compiled_in_features(), get_operating_system(), + get_lib_version(), debug_output); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text1)); + gtk_text_buffer_get_start_iter(buffer, &iter); + gtk_text_buffer_insert(buffer, &iter, crash_report, -1); + + g_free(crash_report); + + hbuttonbox3 = gtk_hbutton_box_new(); + gtk_widget_show(hbuttonbox3); + gtk_box_pack_start(GTK_BOX(vbox1), hbuttonbox3, FALSE, FALSE, 0); + + hbuttonbox4 = gtk_hbutton_box_new(); + gtk_widget_show(hbuttonbox4); + gtk_box_pack_start(GTK_BOX(vbox1), hbuttonbox4, FALSE, FALSE, 0); + + button3 = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + gtk_widget_show(button3); + gtk_container_add(GTK_CONTAINER(hbuttonbox4), button3); + + g_signal_connect(G_OBJECT(window1), "delete_event", + GTK_SIGNAL_FUNC(gtk_main_quit), NULL); + g_signal_connect(G_OBJECT(button3), "clicked", + GTK_SIGNAL_FUNC(gtk_main_quit), NULL); + + gtk_widget_show(window1); + + gtk_main(); + return window1; +} + + +/* + * ! \brief create debugger script file in publicit directory. all the + * other options (creating temp files) looked too convoluted. + */ +static gboolean +crash_create_debugger_file(void) +{ + gchar *filename = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S, + ".hardinfo", G_DIR_SEPARATOR_S, + DEBUGGERRC, NULL); + + g_file_set_contents(filename, DEBUG_SCRIPT, -1, NULL); + g_free(filename); + + return (TRUE); +} + +/* + * ! \brief launches debugger and attaches it to crashed publicit + */ +static void +crash_debug(unsigned long crash_pid, + gchar * exe_image, GString * debug_output) +{ + int choutput[2]; + pid_t pid; + + pipe(choutput); + + if (0 == (pid = fork())) { + char *argp[10]; + char **argptr = argp; + + setgid(getgid()); + setuid(getuid()); + + /* + * setup debugger to attach to crashed publicit + */ + *argptr++ = "gdb"; + *argptr++ = "--nw"; + *argptr++ = "--nx"; + *argptr++ = "--quiet"; + *argptr++ = "--batch"; + *argptr++ = "-x"; + *argptr++ = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S, + ".hardinfo", G_DIR_SEPARATOR_S, + DEBUGGERRC, NULL); + *argptr++ = exe_image; + *argptr++ = g_strdup_printf("%ld", crash_pid); + *argptr = NULL; + + /* + * redirect output to write end of pipe + */ + close(1); + dup(choutput[1]); + close(choutput[0]); + if (-1 == execvp("gdb", argp)) + puts("error execvp\n"); + } else { + char buf[100]; + int r; + + waitpid(pid, NULL, 0); + + /* + * make it non blocking + */ + if (-1 == fcntl(choutput[0], F_SETFL, O_NONBLOCK)) + puts("set to non blocking failed\n"); + + /* + * get the output + */ + do { + r = read(choutput[0], buf, sizeof buf - 1); + if (r > 0) { + buf[r] = 0; + g_string_append(debug_output, buf); + } + } while (r > 0); + + close(choutput[0]); + close(choutput[1]); + + /* + * kill the process we attached to + */ + kill(crash_pid, SIGCONT); + } +} + +/***/ + +/* + * ! \brief features + */ +static const gchar * +get_compiled_in_features(void) +{ + return g_strdup("None"); +} + +/***/ + +/* + * ! \brief library version + */ +static const gchar * +get_lib_version(void) +{ +#if defined(__GNU_LIBRARY__) + return g_strdup_printf("GNU libc %s", gnu_get_libc_version()); +#else + return g_strdup("Unknown"); +#endif +} + +/***/ + +/* + * ! \brief operating system + */ +static const gchar * +get_operating_system(void) +{ + struct utsname utsbuf; + uname(&utsbuf); + return g_strdup_printf("%s %s (%s)", + utsbuf.sysname, utsbuf.release, utsbuf.machine); +} + +/***/ + +/* + * ! \brief see if the crash dialog is allowed (because some developers + * may prefer to run hardinfo under gdb...) + */ +static gboolean +is_crash_dialog_allowed(void) +{ + return !getenv("HARDINFO_NO_CRASH"); +} + +/* + * ! \brief this handler will probably evolve into something better. + */ +static void +crash_handler(int sig) +{ + pid_t pid; + static volatile unsigned long crashed_ = 0; + + /* + * besides guarding entrancy it's probably also better + * to mask off signals + */ + if (crashed_) + return; + + crashed_++; + + /* + * gnome ungrabs focus, and flushes gdk. mmmh, good idea. + */ + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + gdk_flush(); + + if (0 == (pid = fork())) { + char buf[50]; + char *args[4]; + + /* + * probably also some other parameters (like GTK+ ones). + * also we pass the full startup dir and the real command + * line typed in (argv0) + */ + args[0] = params.argv0; + args[1] = "--crash"; + sprintf(buf, "%ld,%d,%s", (long) getppid(), sig, params.argv0); + args[2] = buf; + args[3] = NULL; + + setgid(getgid()); + setuid(getuid()); + execvp(params.argv0, args); + } else { + waitpid(pid, NULL, 0); + crash_cleanup_exit(); + _exit(253); + } + + _exit(253); +} + +/* + * ! \brief put all the things here we can do before letting the program + * die + */ +static void +crash_cleanup_exit(void) +{ + ; +} diff --git a/hardinfo2/crash.h b/hardinfo2/crash.h new file mode 100644 index 00000000..7a169290 --- /dev/null +++ b/hardinfo2/crash.h @@ -0,0 +1,29 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2002 by the Sylpheed Claws Team and Hiroyuki Yamamoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef CRASH_H__ +#define CRASH_H__ + +#define DEBUGGERRC "debuggerrc" + +void crash_install_handlers (void); +void crash_main (const char *arg); + +#endif /* CRASH_H__ */ + |