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:
parent
b79b70620f
commit
1666d285d7
4 changed files with 357 additions and 11 deletions
|
@ -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
290
obt/signal.c
Normal 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
46
obt/signal.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue