1 /*
2 * Copyright (c) 2007 Brian Tarricone <bjt23@cornell.edu>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
18 */
19
20 /**
21 * SECTION: xfce-posix-signal-handler
22 * @title: POSIX Signal Handling
23 * @short_description: a callback system for handling POSIX signals safely
24 *
25 * Due to reentrancy issues, there is a restricted set of functions/syscalls
26 * that are allowed to be performed inside a POSIX signal handler. In
27 * general, it's safer to defer any signal-related processing until after the
28 * signal handler has run. The functionality in this module automatically
29 * handles this, and allows you to set a handler function (with optional user
30 * data) for any signal.
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36
37 #include <stdio.h>
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42
43 #ifdef HAVE_SIGNAL_H
44 #include <signal.h>
45 #endif
46
47 #ifdef HAVE_STRING_H
48 #include <string.h>
49 #endif
50
51 #ifdef HAVE_ERRNO_H
52 #include <errno.h>
53 #endif
54
55 #include <glib.h>
56
57 #include <libxfce4util/libxfce4util.h>
58 #include <libxfce4util/libxfce4util-alias.h>
59
60 #define SIGNAL_PIPE_READ __signal_pipe[0]
61 #define SIGNAL_PIPE_WRITE __signal_pipe[1]
62
63 typedef struct
64 {
65 gint signal_id;
66 XfcePosixSignalHandler handler;
67 gpointer user_data;
68 struct sigaction old_sa;
69 } XfcePosixSignalHandlerData;
70
71 static gboolean __inited = FALSE;
72 static int __signal_pipe[2] = { -1, -1 };
73 static GHashTable *__handlers = NULL;
74 static GIOChannel *__signal_io = NULL;
75 static guint __io_watch_id = 0;
76
77
78 static void
xfce_posix_signal_handler_data_free(XfcePosixSignalHandlerData * hdata)79 xfce_posix_signal_handler_data_free(XfcePosixSignalHandlerData *hdata)
80 {
81 if(!hdata)
82 return;
83
84 sigaction(hdata->signal_id, &hdata->old_sa, NULL);
85 g_free(hdata);
86 }
87
88 static gboolean
xfce_posix_signal_handler_pipe_io(GIOChannel * source,GIOCondition condition,gpointer data)89 xfce_posix_signal_handler_pipe_io(GIOChannel *source,
90 GIOCondition condition,
91 gpointer data)
92 {
93 gint signal_id = 0;
94 GError *error = NULL;
95 gsize bin = 0;
96 XfcePosixSignalHandlerData *hdata;
97
98 if(G_IO_STATUS_NORMAL == g_io_channel_read_chars(source, (gchar *)&signal_id,
99 sizeof(signal_id), &bin,
100 &error)
101 && bin == sizeof(signal_id))
102 {
103 hdata = g_hash_table_lookup(__handlers, GINT_TO_POINTER(signal_id));
104 if(hdata)
105 hdata->handler(signal_id, hdata->user_data);
106 } else {
107 if(error) {
108 g_critical("Signal pipe read failed: %s\n", error->message);
109 g_error_free(error);
110 } else {
111 g_critical("Short read from signal pipe (expected %d, got %d)\n",
112 (int)sizeof(signal_id), (int)bin);
113 }
114 }
115
116 return TRUE;
117 }
118
119 static void
xfce_posix_signal_handler(gint signal_id)120 xfce_posix_signal_handler(gint signal_id)
121 {
122 write(SIGNAL_PIPE_WRITE, &signal_id, sizeof(signal_id));
123 }
124
125
126 /**
127 * xfce_posix_signal_handler_init:
128 * @error: (out) (allow-none) (transfer full): Location of a #GError to store any possible errors.
129 *
130 * Initializes the POSIX signal handler system. Must be called
131 * before setting any POSIX signal handlers.
132 *
133 * Returns: %TRUE on success, %FALSE on failure, in which case
134 * @error will be set.
135 **/
136 gboolean
xfce_posix_signal_handler_init(GError ** error)137 xfce_posix_signal_handler_init(GError **error)
138 {
139 if(G_UNLIKELY(__inited))
140 return TRUE;
141
142 if(pipe(__signal_pipe)) {
143 if(error) {
144 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno),
145 _("pipe() failed: %s"), strerror(errno));
146 }
147 return FALSE;
148 }
149
150 __handlers = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
151 (GDestroyNotify)xfce_posix_signal_handler_data_free);
152
153 __signal_io = g_io_channel_unix_new(SIGNAL_PIPE_READ);
154 g_io_channel_set_close_on_unref(__signal_io, FALSE);
155 g_io_channel_set_encoding(__signal_io, NULL, NULL);
156 g_io_channel_set_buffered(__signal_io, FALSE);
157 __io_watch_id = g_io_add_watch(__signal_io, G_IO_IN,
158 xfce_posix_signal_handler_pipe_io, NULL);
159
160 __inited = TRUE;
161 return TRUE;
162 }
163
164 /**
165 * xfce_posix_signal_handler_shutdown:
166 *
167 * Frees all memory associated with the POSIX signal handling system
168 * and restores all default signal handlers.
169 **/
170 void
xfce_posix_signal_handler_shutdown(void)171 xfce_posix_signal_handler_shutdown(void)
172 {
173 if(G_UNLIKELY(!__inited))
174 return;
175
176 g_source_remove(__io_watch_id);
177 __io_watch_id = 0;
178 g_io_channel_unref(__signal_io);
179 __signal_io = NULL;
180
181 g_hash_table_destroy(__handlers);
182 __handlers = NULL;
183
184 close(SIGNAL_PIPE_READ);
185 SIGNAL_PIPE_READ = -1;
186 close(SIGNAL_PIPE_WRITE);
187 SIGNAL_PIPE_WRITE = -1;
188
189 __inited = FALSE;
190 }
191
192 /**
193 * xfce_posix_signal_handler_set_handler:
194 * @signal: A POSIX signal id number.
195 * @handler: (scope call): A callback function.
196 * @user_data: Arbitrary data that will be passed to @handler.
197 * @error: (out) (allow-none) (transfer full): Location of a #GError to store any possible errors.
198 *
199 * Sets @handler to be called whenever @signal is caught by the
200 * application. The @user_data parameter will be passed as an argument
201 * to @handler.
202 *
203 * Returns: %TRUE on success, %FALSE otherwise, in which case
204 * @error will be set.
205 **/
206 gboolean
xfce_posix_signal_handler_set_handler(gint signal,XfcePosixSignalHandler handler,gpointer user_data,GError ** error)207 xfce_posix_signal_handler_set_handler(gint signal,
208 XfcePosixSignalHandler handler,
209 gpointer user_data,
210 GError **error)
211 {
212 XfcePosixSignalHandlerData *hdata;
213 struct sigaction sa;
214
215 if(G_UNLIKELY(!__inited)) {
216 if(error) {
217 g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
218 _("xfce_posix_signal_handler_init() must be called first"));
219 }
220 return FALSE;
221 }
222
223 if(!handler) {
224 g_warning("NULL signal handler supplied; removing existing handler");
225 xfce_posix_signal_handler_restore_handler(signal);
226 return TRUE;
227 }
228
229 if(g_hash_table_lookup(__handlers, GINT_TO_POINTER(signal)))
230 xfce_posix_signal_handler_restore_handler(signal);
231
232 hdata = g_new0(XfcePosixSignalHandlerData, 1);
233 hdata->signal_id = signal;
234 hdata->handler = handler;
235 hdata->user_data = user_data;
236
237 memset(&sa, 0, sizeof(sa));
238 sa.sa_handler = xfce_posix_signal_handler;
239 sa.sa_flags = SA_RESTART;
240
241 if(sigaction(signal, &sa, &hdata->old_sa)) {
242 if(error) {
243 g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno),
244 _("sigaction() failed: %s\n"), strerror(errno));
245 }
246 g_free(hdata);
247 return FALSE;
248 }
249
250 g_hash_table_insert(__handlers, GINT_TO_POINTER(signal), hdata);
251
252 return TRUE;
253 }
254
255 /**
256 * xfce_posix_signal_handler_restore_handler:
257 * @signal: A POSIX signal id number.
258 *
259 * Restores the default handler for @signal.
260 **/
261 void
xfce_posix_signal_handler_restore_handler(gint signal)262 xfce_posix_signal_handler_restore_handler(gint signal)
263 {
264 if(G_UNLIKELY(!__inited))
265 return;
266
267 g_hash_table_remove(__handlers, GINT_TO_POINTER(signal));
268 }
269
270
271
272 #define __XFCE_POSIX_SIGNAL_HANDLER_C__
273 #include <libxfce4util/libxfce4util-aliasdef.c>
274