diff --git a/src/systray/systraybar.c b/src/systray/systraybar.c index f33a653..eaa0af5 100644 --- a/src/systray/systraybar.c +++ b/src/systray/systraybar.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "systraybar.h" #include "server.h" diff --git a/src/tint.c b/src/tint.c index 7b5d047..a5d7d33 100644 --- a/src/tint.c +++ b/src/tint.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #ifdef HAVE_SN #include @@ -75,6 +77,7 @@ XSettingsClient *xsettings_client = NULL; timeout *detect_compositor_timer = NULL; int detect_compositor_timer_counter = 0; +double start_time = 0; void detect_compositor(void *arg) { @@ -185,14 +188,9 @@ const char *get_home_dir() return pw->pw_dir; } -void crash_handler(int sig) +void dump_backtrace(int log_fd) { - char path[4096]; - sprintf(path, "%s/.tint2-crash.log", get_home_dir()); - int log_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0600); - log_string(log_fd, "Crashing with signal "); - log_string(log_fd, signal_name(sig)); - log_string(log_fd, "\nBacktrace:\n"); + log_string(log_fd, YELLOW "\nBacktrace:\n"); #ifdef ENABLE_LIBUNWIND unw_cursor_t cursor; @@ -225,7 +223,61 @@ void crash_handler(int sig) log_string(log_fd, "Backtrace not supported on this system. Install libunwind or libexecinfo.\n"); #endif #endif - _exit(sig); + log_string(log_fd, RESET); +} + +// sleep() returns early when signals arrive. This function does not. +void safe_sleep(int seconds) +{ + double t0 = get_time(); + while (1) { + double t = get_time(); + if (t > t0 + seconds) + return; + sleep(1); + } +} + +void reexecute_tint2() +{ + write_string(2, GREEN "Attempting to restart tint2...\n" RESET); + // If tint2 cannot stay stable for 30 seconds, don't restart + if (get_time() - start_time < 30) { + write_string(2, GREEN "Not restarting tint2 since the uptime is too small.\n" RESET); + exit(-1); + } + safe_sleep(1); + close_all_fds(); + char *path = get_own_path(); + execlp(path, path, "-c", config_path, NULL); + exit(-1); +} + +void crash_handler(int sig) +{ + // We are going to crash, so restart the panel + char path[4096]; + sprintf(path, "%s/.tint2-crash.log", get_home_dir()); + int log_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0600); + log_string(log_fd, RED "tint2 crashed with signal " RESET); + log_string(log_fd, signal_name(sig)); + dump_backtrace(log_fd); + log_string(log_fd, RED "Please create a bug report with this log output.\n" RESET); + close(log_fd); + reexecute_tint2(); +} + +void x11_io_error(Display *display) +{ + // We are going to crash, so restart the panel + char path[4096]; + sprintf(path, "%s/.tint2-crash.log", get_home_dir()); + int log_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0600); + log_string(log_fd, RED "tint2 crashed due to an X11 I/O error" RESET); + dump_backtrace(log_fd); + log_string(log_fd, RED "Please create a bug report with this log output.\n" RESET); + close(log_fd); + reexecute_tint2(); } void init(int argc, char *argv[]) @@ -373,6 +425,7 @@ void init_X11_pre_config() exit(1); } XSetErrorHandler((XErrorHandler)server_catch_error); + XSetIOErrorHandler((XIOErrorHandler)x11_io_error); server_init_atoms(); server.screen = DefaultScreen(server.display); server.root_win = RootWindow(server.display, server.screen); @@ -1387,6 +1440,8 @@ void dnd_drop(XClientMessageEvent *e) int main(int argc, char *argv[]) { start: + start_time = get_time(); + init(argc, argv); init_X11_pre_config(); diff --git a/src/util/common.c b/src/util/common.c index ac253a8..40954f3 100644 --- a/src/util/common.c +++ b/src/util/common.c @@ -32,6 +32,13 @@ #include "common.h" #include "../server.h" #include +#include +#include +#include +#include +#include +#include +#include #ifdef HAVE_RSVG #include @@ -620,3 +627,112 @@ GList *g_list_copy_deep(GList *list, GCopyFunc func, gpointer user_data) return list; } #endif + +// Based loosely on close_allv from +// https://git.gnome.org/browse/glib/tree/gio/libasyncns/asyncns.c?h=2.21.0#n205 +// license: LGPL version 2.1 +// but with all the junk removed +// and +// https://opensource.apple.com/source/sudo/sudo-46/src/closefrom.c +// license: BSD +void close_all_fds() +{ + const int from_fd = 3; + +#ifdef __linux__ + DIR *d = opendir("/proc/self/fd"); + if (d) { + for (struct dirent *de = readdir(d); de; de = readdir(d)) { + if (de->d_name[0] == '.') + continue; + int fd = atoi(de->d_name); + if (fd < from_fd) + continue; + if (fd == dirfd(d)) + continue; + close(fd); + } + closedir(d); + return; + } +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) + closefrom(from_fd); + return; +#endif + +#if defined(__NetBSD__) + fcntl(from_fd, F_CLOSEM, 0); +#endif + + // Worst case scenario: iterate over all possible fds + int max_fd = sysconf(_SC_OPEN_MAX); + for (int fd = from_fd; fd < max_fd; fd++) { + close(fd); + } + + return; +} + +char* get_own_path() +{ + const int buf_size = 4096; + char *buf = calloc(buf_size, 1); + +#ifdef __linux__ + if (readlink("/proc/self/exe", buf, buf_size) > 0) + return buf; +#endif + +#if defined(__FreeBSD__) + int mib[4] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PATHNAME, + getpid() + }; + + size_t max_len = buf_size; + if (sysctl(mib, 4, buf, &max_len, NULL, 0) == 0) + return buf; +#endif + +#if defined(__DragonFly__) + if (readlink("/proc/curproc/file", buf, buf_size) > 0) + return buf; +#endif + +#if defined(__NetBSD__) + if (readlink("/proc/curproc/exe", buf, buf_size) > 0) + return buf; +#endif + +#if defined(__OpenBSD__) + int mib[4] = { + CTL_KERN, + KERN_PROC_ARGS, + getpid(), + KERN_PROC_ARGV + }; + + char *path = NULL; + size_t len; + if (sysctl(mib, 4, NULL, &len, NULL, 0) == 0 && len > 0) { + char **argv = malloc(len); + if (argv) { + if (sysctl(mib, 4, argv, &len, NULL, 0) == 0) { + path = realpath(argv[0], NULL); + } + } + free(argv); + } + if (path) { + free(buf); + return path; + } +#endif + + sprintf(buf, "tint2"); + return buf; +} diff --git a/src/util/common.h b/src/util/common.h index 8097caa..fc76f31 100644 --- a/src/util/common.h +++ b/src/util/common.h @@ -14,7 +14,7 @@ #define GREEN "\033[1;32m" #define YELLOW "\033[1;33m" -#define RED "\033[31m" +#define RED "\033[1;31m" #define BLUE "\033[1;34m" #define RESET "\033[0m" @@ -108,4 +108,8 @@ GList *g_list_copy_deep(GList *list, GCopyFunc func, gpointer user_data); #define g_assert_null(expr) g_assert((expr) == NULL) #endif +void close_all_fds(); + +char* get_own_path(); + #endif diff --git a/src/util/timer.c b/src/util/timer.c index f8e39ce..1a89e6f 100644 --- a/src/util/timer.c +++ b/src/util/timer.c @@ -435,11 +435,16 @@ void stop_multi_timeout(timeout *t) double profiling_get_time_old_time = 0; -double profiling_get_time() +double get_time() { struct timespec cur_time; clock_gettime(CLOCK_MONOTONIC, &cur_time); - double t = cur_time.tv_sec + cur_time.tv_nsec * 1.0e-9; + return cur_time.tv_sec + cur_time.tv_nsec * 1.0e-9; +} + +double profiling_get_time() +{ + double t = get_time(); if (profiling_get_time_old_time == 0) profiling_get_time_old_time = t; double delta = t - profiling_get_time_old_time; diff --git a/src/util/timer.h b/src/util/timer.h index c347ed8..61901d2 100644 --- a/src/util/timer.h +++ b/src/util/timer.h @@ -69,4 +69,6 @@ struct timespec add_msec_to_timespec(struct timespec ts, int msec); // At the first call returns zero. double profiling_get_time(); +double get_time(); + #endif // TIMER_H