From 1666d285d744173e03c585dd6525219732ba313a Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Sun, 27 Jun 2010 12:02:44 -0400 Subject: [PATCH] Add signal handling with the GMainLoop Provided through a very simplistic interface in obt, found in the obt/signal.[ch] files --- Makefile.am | 3 + obt/signal.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++ obt/signal.h | 46 ++++++++ openbox/openbox.c | 29 +++-- 4 files changed, 357 insertions(+), 11 deletions(-) create mode 100644 obt/signal.c create mode 100644 obt/signal.h diff --git a/Makefile.am b/Makefile.am index 765ffaa9..62570bd0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -144,6 +144,8 @@ obt_libobt_la_SOURCES = \ obt/paths.c \ obt/prop.h \ obt/prop.c \ + obt/signal.h \ + obt/signal.c \ obt/util.h \ obt/watch.h \ obt/watch.c \ @@ -439,6 +441,7 @@ obtpubinclude_HEADERS = \ obt/xml.h \ obt/paths.h \ obt/prop.h \ + obt/signal.h \ obt/util.h \ obt/version.h \ obt/watch.h \ diff --git a/obt/signal.c b/obt/signal.c new file mode 100644 index 00000000..f6de6c4e --- /dev/null +++ b/obt/signal.c @@ -0,0 +1,290 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/signal.c for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + 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. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "signal.h" + +#ifdef HAVE_STDIO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_SIGNAL_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +typedef struct _ObtSignalCallback ObtSignalCallback; + +struct _ObtSignalCallback +{ + ObtSignalHandler func; + gpointer data; +}; + +static gboolean signal_prepare(GSource *source, gint *timeout); +static gboolean signal_check(GSource *source); +static gboolean signal_occured(GSource *source, GSourceFunc callback, + gpointer data); +static void sighandler(gint sig); + +/* this should be more than the number of possible signals on any + architecture... */ +#define NUM_SIGNALS 99 + +/* a set of all possible signals */ +static sigset_t all_signals_set; + +/* keep track of what signals have a signal handler installed, and remember + the action we replaced when installing it for when we clean up */ +static struct { + guint installed; /* a ref count */ + struct sigaction oldact; +} all_signals[NUM_SIGNALS]; + +/* signals which cause a core dump, these can't be used for callbacks */ +static const gint core_signals[] = +{ + SIGABRT, + SIGSEGV, + SIGFPE, + SIGILL, + SIGQUIT, + SIGTRAP, + SIGSYS, + SIGBUS, + SIGXCPU, + SIGXFSZ +}; +#define NUM_CORE_SIGNALS (gint)(sizeof(core_signals) / sizeof(core_signals[0])) + +static GSourceFuncs source_funcs = { + signal_prepare, + signal_check, + signal_occured, + NULL +}; +static GSource *gsource = NULL; +static guint listeners = 0; /* a ref count for the signal listener */ +static gboolean signal_fired; +guint signals_fired[NUM_SIGNALS]; +GSList *callbacks[NUM_SIGNALS]; + +void obt_signal_listen(void) +{ + if (!listeners) { + guint i; + struct sigaction action; + sigset_t sigset; + + /* initialize the all_signals_set */ + sigfillset(&all_signals_set); + + sigemptyset(&sigset); + action.sa_handler = sighandler; + action.sa_mask = sigset; + action.sa_flags = SA_NOCLDSTOP; + + /* always grab all the signals that cause core dumps */ + for (i = 0; i < NUM_CORE_SIGNALS; ++i) { + /* SIGABRT is curiously not grabbed here!! that's because when we + get one of the core_signals, we use abort() to dump the core. + And having the abort() only go back to our signal handler again + is less than optimal */ + if (core_signals[i] != SIGABRT) { + sigaction(core_signals[i], &action, + &all_signals[core_signals[i]].oldact); + all_signals[core_signals[i]].installed++; + } + } + + gsource = g_source_new(&source_funcs, sizeof(GSource)); + g_source_set_priority(gsource, G_PRIORITY_HIGH); + + g_source_attach(gsource, NULL); + } + + ++listeners; +} + +void obt_signal_stop(void) +{ + --listeners; + + if (!listeners) { + gint i; + GSList *it, *next; + + g_source_unref(gsource); + gsource = NULL; + + /* remove user defined signal handlers */ + for (i = 0; i < NUM_SIGNALS; ++i) + for (it = callbacks[i]; it; it = next) { + ObtSignalCallback *cb = it->data; + next = g_slist_next(it); + obt_signal_remove_callback(i, cb->func); + } + + /* release all the signals that cause core dumps */ + for (i = 0; i < NUM_CORE_SIGNALS; ++i) { + if (all_signals[core_signals[i]].installed) { + sigaction(core_signals[i], + &all_signals[core_signals[i]].oldact, NULL); + all_signals[core_signals[i]].installed--; + } + } + +#ifdef DEBUG + for (i = 0; i < NUM_SIGNALS; ++i) + g_assert(all_signals[i].installed == 0); +#endif + } +} + +void obt_signal_add_callback(gint sig, ObtSignalHandler func, gpointer data) +{ + ObtSignalCallback *cb; + gint i; + + g_return_if_fail(func != NULL); + g_return_if_fail(sig >= 0 && sig <= NUM_SIGNALS); + for (i = 0; i < NUM_CORE_SIGNALS; ++i) + g_return_if_fail(sig != core_signals[i]); + + cb = g_slice_new(ObtSignalCallback); + cb->func = func; + cb->data = data; + callbacks[sig] = g_slist_prepend(callbacks[sig], cb); + + /* install the signal handler */ + if (!all_signals[sig].installed) { + struct sigaction action; + sigset_t sigset; + + sigemptyset(&sigset); + action.sa_handler = sighandler; + action.sa_mask = sigset; + action.sa_flags = SA_NOCLDSTOP; + + sigaction(sig, &action, &all_signals[sig].oldact); + } + + all_signals[sig].installed++; +} + +void obt_signal_remove_callback(gint sig, ObtSignalHandler func) +{ + GSList *it; + gint i; + + g_return_if_fail(func != NULL); + g_return_if_fail(sig >= 0 && sig <= NUM_SIGNALS); + for (i = 0; i < NUM_CORE_SIGNALS; ++i) + g_return_if_fail(sig != core_signals[i]); + + for (it = callbacks[sig]; it; it = g_slist_next(it)) { + ObtSignalCallback *cb = it->data; + if (cb->func == func) { + g_assert(all_signals[sig].installed > 0); + + callbacks[sig] = g_slist_delete_link(callbacks[sig], it); + g_slice_free(ObtSignalCallback, cb); + + /* uninstall the signal handler */ + all_signals[sig].installed--; + if (!all_signals[sig].installed) + sigaction(sig, &all_signals[sig].oldact, NULL); + break; + } + } +} + +static gboolean signal_prepare(GSource *source, gint *timeout) +{ + *timeout = -1; + return signal_fired; +} + +static gboolean signal_check(GSource *source) +{ + return signal_fired; +} + +static gboolean signal_occured(GSource *source, GSourceFunc callback, + gpointer data) +{ + guint i; + sigset_t oldset; + guint fired[NUM_SIGNALS]; + + /* block signals so that we can do this without the data changing + on us */ + sigprocmask(SIG_SETMASK, &all_signals_set, &oldset); + + /* make a copy of the signals that fired */ + for (i = 0; i < NUM_SIGNALS; ++i) { + fired[i] = signals_fired[i]; + signals_fired[i] = 0; + } + signal_fired = FALSE; + + sigprocmask(SIG_SETMASK, &oldset, NULL); + + /* call the signal callbacks for the signals */ + for (i = 0; i < NUM_SIGNALS; ++i) { + while (fired[i]) { + GSList *it; + for (it = callbacks[i]; it; it = g_slist_next(it)) { + const ObtSignalCallback *cb = it->data; + cb->func(i, cb->data); + } + --fired[i]; + } + } + + return TRUE; /* repeat */ +} + +static void sighandler(gint sig) +{ + guint i; + + g_return_if_fail(sig < NUM_SIGNALS); + + for (i = 0; i < NUM_CORE_SIGNALS; ++i) + if (sig == core_signals[i]) { + /* XXX special case for signals that default to core dump. + but throw some helpful output here... */ + + fprintf(stderr, "How are you gentlemen? All your base are" + " belong to us. (Openbox received signal %d)\n", sig); + + /* die with a core dump */ + abort(); + } + + signal_fired = TRUE; + ++signals_fired[sig]; + + /* i don't think we want to modify the GMainContext inside a signal + handler, so use a GSource instead of an idle func to call back + to the application */ +} diff --git a/obt/signal.h b/obt/signal.h new file mode 100644 index 00000000..ec712b24 --- /dev/null +++ b/obt/signal.h @@ -0,0 +1,46 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/signal.h for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + 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. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_signal_h +#define __obt_signal_h + +#include + +G_BEGIN_DECLS + +typedef void (*ObtSignalHandler)(gint signal, gpointer data); + +/*! Listen for signals and report them through the default GMainContext within + main program thread (except signals that require immediate exit). + The app should not set its own signal handler function or it will interfere + with this one. */ +void obt_signal_listen(void); +/*! Stop listening to signals and clean up */ +void obt_signal_stop(void); + +/*! Adds a signal handler for a signal. The callback function @func will be + called when the signal @sig is fired. @sig must not be a signal that + would cause the core to dump as these are handled internally. + */ +void obt_signal_add_callback(gint sig, ObtSignalHandler func, gpointer data); +/*! Removes the most recently added callback with the given function. */ +void obt_signal_remove_callback(gint sig, ObtSignalHandler func); + +G_END_DECLS + +#endif diff --git a/openbox/openbox.c b/openbox/openbox.c index b2cdc8ef..ae0ba8ae 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -47,6 +47,7 @@ #include "obrender/theme.h" #include "obt/display.h" #include "obt/xqueue.h" +#include "obt/signal.h" #include "obt/prop.h" #include "obt/keyboard.h" #include "obt/xml.h" @@ -117,6 +118,8 @@ gint main(gint argc, gchar **argv) { gchar *program_name; + obt_signal_listen(); + ob_set_state(OB_STATE_STARTING); ob_debug_startup(); @@ -160,16 +163,17 @@ gint main(gint argc, gchar **argv) ob_main_loop = g_main_loop_new(NULL, FALSE); - /* set up signal handler */ -// obt_main_loop_signal_add(ob_main_loop, SIGUSR1, signal_handler, NULL,NULL); -// obt_main_loop_signal_add(ob_main_loop, SIGUSR2, signal_handler, NULL,NULL); -// obt_main_loop_signal_add(ob_main_loop, SIGTERM, signal_handler, NULL,NULL); -// obt_main_loop_signal_add(ob_main_loop, SIGINT, signal_handler, NULL,NULL); -// obt_main_loop_signal_add(ob_main_loop, SIGHUP, signal_handler, NULL,NULL); -// obt_main_loop_signal_add(ob_main_loop, SIGPIPE, signal_handler, NULL,NULL); -// obt_main_loop_signal_add(ob_main_loop, SIGCHLD, signal_handler, NULL,NULL); -// obt_main_loop_signal_add(ob_main_loop, SIGTTIN, signal_handler, NULL,NULL); -// obt_main_loop_signal_add(ob_main_loop, SIGTTOU, signal_handler, NULL,NULL); + /* set up signal handlers, they are called from the mainloop + in the main program's thread */ + obt_signal_add_callback(SIGUSR1, signal_handler, NULL); + obt_signal_add_callback(SIGUSR2, signal_handler, NULL); + obt_signal_add_callback(SIGTERM, signal_handler, NULL); + obt_signal_add_callback(SIGINT, signal_handler, NULL); + obt_signal_add_callback(SIGHUP, signal_handler, NULL); + obt_signal_add_callback(SIGPIPE, signal_handler, NULL); + obt_signal_add_callback(SIGCHLD, signal_handler, NULL); + obt_signal_add_callback(SIGTTIN, signal_handler, NULL); + obt_signal_add_callback(SIGTTOU, signal_handler, NULL); ob_screen = DefaultScreen(obt_display); @@ -420,6 +424,7 @@ gint main(gint argc, gchar **argv) if (restart) { ob_debug_shutdown(); + obt_signal_stop(); if (restart_path != NULL) { gint argcp; gchar **argvp; @@ -473,8 +478,10 @@ gint main(gint argc, gchar **argv) g_free(ob_sm_id); g_free(program_name); - if (!restart) + if (!restart) { ob_debug_shutdown(); + obt_signal_stop(); + } return exitcode; } -- 2.45.2