1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 obt/signal.c for the Openbox window manager
4 Copyright (c) 2010 Dana Jansens
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 See the COPYING file for a copy of the GNU General Public License.
34 typedef struct _ObtSignalCallback ObtSignalCallback
;
36 struct _ObtSignalCallback
38 ObtSignalHandler func
;
42 static gboolean
signal_prepare(GSource
*source
, gint
*timeout
);
43 static gboolean
signal_check(GSource
*source
);
44 static gboolean
signal_occurred(GSource
*source
, GSourceFunc callback
,
46 static void sighandler(gint sig
);
48 /* this should be more than the number of possible signals on any
50 #define NUM_SIGNALS 99
52 /* a set of all possible signals */
53 static sigset_t all_signals_set
;
55 /* keep track of what signals have a signal handler installed, and remember
56 the action we replaced when installing it for when we clean up */
58 guint installed
; /* a ref count */
59 struct sigaction oldact
;
60 } all_signals
[NUM_SIGNALS
];
62 /* signals which cause a core dump, these can't be used for callbacks */
63 static const gint core_signals
[] =
76 #define NUM_CORE_SIGNALS (gint)(sizeof(core_signals) / sizeof(core_signals[0]))
78 static GSourceFuncs source_funcs
= {
84 static GSource
*gsource
= NULL
;
85 static guint listeners
= 0; /* a ref count for the signal listener */
86 static gboolean signal_fired
;
87 guint signals_fired
[NUM_SIGNALS
];
88 GSList
*callbacks
[NUM_SIGNALS
];
90 void obt_signal_listen(void)
94 struct sigaction action
;
97 /* initialize the all_signals_set */
98 sigfillset(&all_signals_set
);
100 sigemptyset(&sigset
);
101 action
.sa_handler
= sighandler
;
102 action
.sa_mask
= sigset
;
103 action
.sa_flags
= SA_NOCLDSTOP
;
105 /* always grab all the signals that cause core dumps */
106 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
107 /* SIGABRT is curiously not grabbed here!! that's because when we
108 get one of the core_signals, we use abort() to dump the core.
109 And having the abort() only go back to our signal handler again
110 is less than optimal */
111 if (core_signals
[i
] != SIGABRT
) {
112 sigaction(core_signals
[i
], &action
,
113 &all_signals
[core_signals
[i
]].oldact
);
114 all_signals
[core_signals
[i
]].installed
++;
118 gsource
= g_source_new(&source_funcs
, sizeof(GSource
));
119 g_source_set_priority(gsource
, G_PRIORITY_HIGH
);
121 g_source_attach(gsource
, NULL
);
127 void obt_signal_stop(void)
135 g_source_unref(gsource
);
138 /* remove user defined signal handlers */
139 for (i
= 0; i
< NUM_SIGNALS
; ++i
)
140 for (it
= callbacks
[i
]; it
; it
= next
) {
141 ObtSignalCallback
*cb
= it
->data
;
142 next
= g_slist_next(it
);
143 obt_signal_remove_callback(i
, cb
->func
);
146 /* release all the signals that cause core dumps */
147 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
) {
148 if (all_signals
[core_signals
[i
]].installed
) {
149 sigaction(core_signals
[i
],
150 &all_signals
[core_signals
[i
]].oldact
, NULL
);
151 all_signals
[core_signals
[i
]].installed
--;
156 for (i
= 0; i
< NUM_SIGNALS
; ++i
)
157 g_assert(all_signals
[i
].installed
== 0);
162 void obt_signal_add_callback(gint sig
, ObtSignalHandler func
, gpointer data
)
164 ObtSignalCallback
*cb
;
167 g_return_if_fail(func
!= NULL
);
168 g_return_if_fail(sig
>= 0 && sig
<= NUM_SIGNALS
);
169 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
170 g_return_if_fail(sig
!= core_signals
[i
]);
172 cb
= g_slice_new(ObtSignalCallback
);
175 callbacks
[sig
] = g_slist_prepend(callbacks
[sig
], cb
);
177 /* install the signal handler */
178 if (!all_signals
[sig
].installed
) {
179 struct sigaction action
;
182 sigemptyset(&sigset
);
183 action
.sa_handler
= sighandler
;
184 action
.sa_mask
= sigset
;
185 action
.sa_flags
= SA_NOCLDSTOP
;
187 sigaction(sig
, &action
, &all_signals
[sig
].oldact
);
190 all_signals
[sig
].installed
++;
193 void obt_signal_remove_callback(gint sig
, ObtSignalHandler func
)
198 g_return_if_fail(func
!= NULL
);
199 g_return_if_fail(sig
>= 0 && sig
<= NUM_SIGNALS
);
200 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
201 g_return_if_fail(sig
!= core_signals
[i
]);
203 for (it
= callbacks
[sig
]; it
; it
= g_slist_next(it
)) {
204 ObtSignalCallback
*cb
= it
->data
;
205 if (cb
->func
== func
) {
206 g_assert(all_signals
[sig
].installed
> 0);
208 callbacks
[sig
] = g_slist_delete_link(callbacks
[sig
], it
);
209 g_slice_free(ObtSignalCallback
, cb
);
211 /* uninstall the signal handler */
212 all_signals
[sig
].installed
--;
213 if (!all_signals
[sig
].installed
)
214 sigaction(sig
, &all_signals
[sig
].oldact
, NULL
);
220 static gboolean
signal_prepare(GSource
*source
, gint
*timeout
)
226 static gboolean
signal_check(GSource
*source
)
231 static gboolean
signal_occurred(GSource
*source
, GSourceFunc callback
,
236 guint fired
[NUM_SIGNALS
];
238 /* block signals so that we can do this without the data changing
240 sigprocmask(SIG_SETMASK
, &all_signals_set
, &oldset
);
242 /* make a copy of the signals that fired */
243 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
244 fired
[i
] = signals_fired
[i
];
245 signals_fired
[i
] = 0;
247 signal_fired
= FALSE
;
249 sigprocmask(SIG_SETMASK
, &oldset
, NULL
);
251 /* call the signal callbacks for the signals */
252 for (i
= 0; i
< NUM_SIGNALS
; ++i
) {
255 for (it
= callbacks
[i
]; it
; it
= g_slist_next(it
)) {
256 const ObtSignalCallback
*cb
= it
->data
;
257 cb
->func(i
, cb
->data
);
263 return TRUE
; /* repeat */
266 static void sighandler(gint sig
)
270 g_return_if_fail(sig
< NUM_SIGNALS
);
272 for (i
= 0; i
< NUM_CORE_SIGNALS
; ++i
)
273 if (sig
== core_signals
[i
]) {
274 /* XXX special case for signals that default to core dump.
275 but throw some helpful output here... */
277 fprintf(stderr
, "How are you gentlemen? All your base are"
278 " belong to us. (Openbox received signal %d)\n", sig
);
280 /* die with a core dump */
285 ++signals_fired
[sig
];
287 /* i don't think we want to modify the GMainContext inside a signal
288 handler, so use a GSource instead of an idle func to call back
289 to the application */