Add signal handling with the GMainLoop

Provided through a very simplistic interface in obt, found in the
 obt/signal.[ch] files
This commit is contained in:
Dana Jansens 2010-06-27 12:02:44 -04:00 committed by Mikael Magnusson
parent b79b70620f
commit 1666d285d7
4 changed files with 357 additions and 11 deletions

View file

@ -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 \

290
obt/signal.c Normal file
View file

@ -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 <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 */
}

46
obt/signal.h Normal file
View file

@ -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 <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

View file

@ -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;
}