--- /dev/null
+/* -*- 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 <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#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 */
+}
--- /dev/null
+/* -*- 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 <glib.h>
+
+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
#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"
{
gchar *program_name;
+ obt_signal_listen();
+
ob_set_state(OB_STATE_STARTING);
ob_debug_startup();
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);
if (restart) {
ob_debug_shutdown();
+ obt_signal_stop();
if (restart_path != NULL) {
gint argcp;
gchar **argvp;
g_free(ob_sm_id);
g_free(program_name);
- if (!restart)
+ if (!restart) {
ob_debug_shutdown();
+ obt_signal_stop();
+ }
return exitcode;
}