source: branches/samba-3.5.x/lib/tevent/tevent_signal.c

Last change on this file was 664, checked in by Silvan Scherrer, 14 years ago

Samba Server 3.5: fixed a missing return, changed os2_socketpair to os2_pipe

File size: 11.7 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 common events code for signal events
5
6 Copyright (C) Andrew Tridgell 2007
7
8 ** NOTE! The following LGPL license applies to the tevent
9 ** library. This does NOT imply that all of Samba is released
10 ** under the LGPL
11
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Lesser General Public
14 License as published by the Free Software Foundation; either
15 version 3 of the License, or (at your option) any later version.
16
17 This library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 Lesser General Public License for more details.
21
22 You should have received a copy of the GNU Lesser General Public
23 License along with this library; if not, see <http://www.gnu.org/licenses/>.
24*/
25
26#include "replace.h"
27#include "system/filesys.h"
28#include "system/wait.h"
29#include "tevent.h"
30#include "tevent_internal.h"
31#include "tevent_util.h"
32#ifdef __OS2__
33#define pipe(A) os2_pipe(A)
34#endif
35
36#define TEVENT_NUM_SIGNALS 64
37
38/* maximum number of SA_SIGINFO signals to hold in the queue.
39 NB. This *MUST* be a power of 2, in order for the ring buffer
40 wrap to work correctly. Thanks to Petr Vandrovec <petr@vandrovec.name>
41 for this. */
42
43#define TEVENT_SA_INFO_QUEUE_COUNT 64
44
45struct tevent_sigcounter {
46 uint32_t count;
47 uint32_t seen;
48};
49
50#define TEVENT_SIG_INCREMENT(s) (s).count++
51#define TEVENT_SIG_SEEN(s, n) (s).seen += (n)
52#define TEVENT_SIG_PENDING(s) ((s).seen != (s).count)
53
54struct tevent_common_signal_list {
55 struct tevent_common_signal_list *prev, *next;
56 struct tevent_signal *se;
57};
58
59/*
60 the poor design of signals means that this table must be static global
61*/
62static struct tevent_sig_state {
63 struct tevent_common_signal_list *sig_handlers[TEVENT_NUM_SIGNALS+1];
64 struct sigaction *oldact[TEVENT_NUM_SIGNALS+1];
65 struct tevent_sigcounter signal_count[TEVENT_NUM_SIGNALS+1];
66 struct tevent_sigcounter got_signal;
67#ifdef SA_SIGINFO
68 /* with SA_SIGINFO we get quite a lot of info per signal */
69 siginfo_t *sig_info[TEVENT_NUM_SIGNALS+1];
70 struct tevent_sigcounter sig_blocked[TEVENT_NUM_SIGNALS+1];
71#endif
72} *sig_state;
73
74/*
75 return number of sigcounter events not processed yet
76*/
77static uint32_t tevent_sig_count(struct tevent_sigcounter s)
78{
79 return s.count - s.seen;
80}
81
82/*
83 signal handler - redirects to registered signals
84*/
85static void tevent_common_signal_handler(int signum)
86{
87 char c = 0;
88 ssize_t res;
89 struct tevent_common_signal_list *sl;
90 struct tevent_context *ev = NULL;
91 int saved_errno = errno;
92
93 TEVENT_SIG_INCREMENT(sig_state->signal_count[signum]);
94 TEVENT_SIG_INCREMENT(sig_state->got_signal);
95
96 /* Write to each unique event context. */
97 for (sl = sig_state->sig_handlers[signum]; sl; sl = sl->next) {
98 if (sl->se->event_ctx && sl->se->event_ctx != ev) {
99 ev = sl->se->event_ctx;
100 /* doesn't matter if this pipe overflows */
101 res = write(ev->pipe_fds[1], &c, 1);
102 }
103 }
104
105 errno = saved_errno;
106}
107
108#ifdef SA_SIGINFO
109/*
110 signal handler with SA_SIGINFO - redirects to registered signals
111*/
112static void tevent_common_signal_handler_info(int signum, siginfo_t *info,
113 void *uctx)
114{
115 uint32_t count = tevent_sig_count(sig_state->signal_count[signum]);
116 /* sig_state->signal_count[signum].seen % TEVENT_SA_INFO_QUEUE_COUNT
117 * is the base of the unprocessed signals in the ringbuffer. */
118 uint32_t ofs = (sig_state->signal_count[signum].seen + count) %
119 TEVENT_SA_INFO_QUEUE_COUNT;
120 sig_state->sig_info[signum][ofs] = *info;
121
122 tevent_common_signal_handler(signum);
123
124 /* handle SA_SIGINFO */
125 if (count+1 == TEVENT_SA_INFO_QUEUE_COUNT) {
126 /* we've filled the info array - block this signal until
127 these ones are delivered */
128 sigset_t set;
129 sigemptyset(&set);
130 sigaddset(&set, signum);
131 sigprocmask(SIG_BLOCK, &set, NULL);
132 TEVENT_SIG_INCREMENT(sig_state->sig_blocked[signum]);
133 }
134}
135#endif
136
137static int tevent_common_signal_list_destructor(struct tevent_common_signal_list *sl)
138{
139 if (sig_state->sig_handlers[sl->se->signum]) {
140 DLIST_REMOVE(sig_state->sig_handlers[sl->se->signum], sl);
141 }
142 return 0;
143}
144
145/*
146 destroy a signal event
147*/
148static int tevent_signal_destructor(struct tevent_signal *se)
149{
150 struct tevent_common_signal_list *sl;
151 sl = talloc_get_type(se->additional_data,
152 struct tevent_common_signal_list);
153
154 if (se->event_ctx) {
155 DLIST_REMOVE(se->event_ctx->signal_events, se);
156 }
157
158 talloc_free(sl);
159
160 if (sig_state->sig_handlers[se->signum] == NULL) {
161 /* restore old handler, if any */
162 if (sig_state->oldact[se->signum]) {
163 sigaction(se->signum, sig_state->oldact[se->signum], NULL);
164 sig_state->oldact[se->signum] = NULL;
165 }
166#ifdef SA_SIGINFO
167 if (se->sa_flags & SA_SIGINFO) {
168 if (sig_state->sig_info[se->signum]) {
169 talloc_free(sig_state->sig_info[se->signum]);
170 sig_state->sig_info[se->signum] = NULL;
171 }
172 }
173#endif
174 }
175
176 return 0;
177}
178
179/*
180 this is part of the pipe hack needed to avoid the signal race condition
181*/
182static void signal_pipe_handler(struct tevent_context *ev, struct tevent_fd *fde,
183 uint16_t flags, void *_private)
184{
185 char c[16];
186 ssize_t res;
187 /* its non-blocking, doesn't matter if we read too much */
188 res = read(fde->fd, c, sizeof(c));
189}
190
191/*
192 add a signal event
193 return NULL on failure (memory allocation error)
194*/
195struct tevent_signal *tevent_common_add_signal(struct tevent_context *ev,
196 TALLOC_CTX *mem_ctx,
197 int signum,
198 int sa_flags,
199 tevent_signal_handler_t handler,
200 void *private_data,
201 const char *handler_name,
202 const char *location)
203{
204 struct tevent_signal *se;
205 struct tevent_common_signal_list *sl;
206 sigset_t set, oldset;
207
208 if (signum >= TEVENT_NUM_SIGNALS) {
209 errno = EINVAL;
210 return NULL;
211 }
212
213 /* the sig_state needs to be on a global context as it can last across
214 multiple event contexts */
215 if (sig_state == NULL) {
216 sig_state = talloc_zero(talloc_autofree_context(), struct tevent_sig_state);
217 if (sig_state == NULL) {
218 return NULL;
219 }
220 }
221
222 se = talloc(mem_ctx?mem_ctx:ev, struct tevent_signal);
223 if (se == NULL) return NULL;
224
225 se->event_ctx = ev;
226 se->signum = signum;
227 se->sa_flags = sa_flags;
228 se->handler = handler;
229 se->private_data = private_data;
230 se->handler_name = handler_name;
231 se->location = location;
232 se->additional_data = NULL;
233
234 sl = talloc(se, struct tevent_common_signal_list);
235 if (!sl) {
236 talloc_free(se);
237 return NULL;
238 }
239 sl->se = se;
240 se->additional_data = sl;
241
242 /* Ensure, no matter the destruction order, that we always have a handle on the global sig_state */
243 if (!talloc_reference(se, sig_state)) {
244 talloc_free(se);
245 return NULL;
246 }
247
248 /* we need to setup the pipe hack handler if not already
249 setup */
250 if (ev->pipe_fde == NULL) {
251 if (pipe(ev->pipe_fds) == -1) {
252 talloc_free(se);
253 return NULL;
254 }
255 ev_set_blocking(ev->pipe_fds[0], false);
256 ev_set_blocking(ev->pipe_fds[1], false);
257 ev->pipe_fde = tevent_add_fd(ev, ev, ev->pipe_fds[0],
258 TEVENT_FD_READ,
259 signal_pipe_handler, NULL);
260 if (!ev->pipe_fde) {
261 close(ev->pipe_fds[0]);
262 close(ev->pipe_fds[1]);
263 talloc_free(se);
264 return NULL;
265 }
266 }
267
268 /* only install a signal handler if not already installed */
269 if (sig_state->sig_handlers[signum] == NULL) {
270 struct sigaction act;
271 ZERO_STRUCT(act);
272 act.sa_handler = tevent_common_signal_handler;
273 act.sa_flags = sa_flags;
274#ifdef SA_SIGINFO
275 if (sa_flags & SA_SIGINFO) {
276 act.sa_handler = NULL;
277 act.sa_sigaction = tevent_common_signal_handler_info;
278 if (sig_state->sig_info[signum] == NULL) {
279 sig_state->sig_info[signum] =
280 talloc_zero_array(sig_state, siginfo_t,
281 TEVENT_SA_INFO_QUEUE_COUNT);
282 if (sig_state->sig_info[signum] == NULL) {
283 talloc_free(se);
284 return NULL;
285 }
286 }
287 }
288#endif
289 sig_state->oldact[signum] = talloc(sig_state, struct sigaction);
290 if (sig_state->oldact[signum] == NULL) {
291 talloc_free(se);
292 return NULL;
293 }
294 if (sigaction(signum, &act, sig_state->oldact[signum]) == -1) {
295 talloc_free(se);
296 return NULL;
297 }
298 }
299
300 DLIST_ADD(se->event_ctx->signal_events, se);
301
302 /* Make sure the signal doesn't come in while we're mangling list. */
303 sigemptyset(&set);
304 sigaddset(&set, signum);
305 sigprocmask(SIG_BLOCK, &set, &oldset);
306 DLIST_ADD(sig_state->sig_handlers[signum], sl);
307 sigprocmask(SIG_SETMASK, &oldset, NULL);
308
309 talloc_set_destructor(se, tevent_signal_destructor);
310 talloc_set_destructor(sl, tevent_common_signal_list_destructor);
311
312 return se;
313}
314
315
316/*
317 check if a signal is pending
318 return != 0 if a signal was pending
319*/
320int tevent_common_check_signal(struct tevent_context *ev)
321{
322 int i;
323
324 if (!sig_state || !TEVENT_SIG_PENDING(sig_state->got_signal)) {
325 return 0;
326 }
327
328 for (i=0;i<TEVENT_NUM_SIGNALS+1;i++) {
329 struct tevent_common_signal_list *sl, *next;
330 struct tevent_sigcounter counter = sig_state->signal_count[i];
331 uint32_t count = tevent_sig_count(counter);
332#ifdef SA_SIGINFO
333 /* Ensure we null out any stored siginfo_t entries
334 * after processing for debugging purposes. */
335 bool clear_processed_siginfo = false;
336#endif
337
338 if (count == 0) {
339 continue;
340 }
341 for (sl=sig_state->sig_handlers[i];sl;sl=next) {
342 struct tevent_signal *se = sl->se;
343 next = sl->next;
344#ifdef SA_SIGINFO
345 if (se->sa_flags & SA_SIGINFO) {
346 uint32_t j;
347
348 clear_processed_siginfo = true;
349
350 for (j=0;j<count;j++) {
351 /* sig_state->signal_count[i].seen
352 * % TEVENT_SA_INFO_QUEUE_COUNT is
353 * the base position of the unprocessed
354 * signals in the ringbuffer. */
355 uint32_t ofs = (counter.seen + j)
356 % TEVENT_SA_INFO_QUEUE_COUNT;
357 se->handler(ev, se, i, 1,
358 (void*)&sig_state->sig_info[i][ofs],
359 se->private_data);
360 }
361#ifdef SA_RESETHAND
362 if (se->sa_flags & SA_RESETHAND) {
363 talloc_free(se);
364 }
365#endif
366 continue;
367 }
368#endif
369 se->handler(ev, se, i, count, NULL, se->private_data);
370#ifdef SA_RESETHAND
371 if (se->sa_flags & SA_RESETHAND) {
372 talloc_free(se);
373 }
374#endif
375 }
376
377#ifdef SA_SIGINFO
378 if (clear_processed_siginfo) {
379 uint32_t j;
380 for (j=0;j<count;j++) {
381 uint32_t ofs = (counter.seen + j)
382 % TEVENT_SA_INFO_QUEUE_COUNT;
383 memset((void*)&sig_state->sig_info[i][ofs],
384 '\0',
385 sizeof(siginfo_t));
386 }
387 }
388#endif
389
390 TEVENT_SIG_SEEN(sig_state->signal_count[i], count);
391 TEVENT_SIG_SEEN(sig_state->got_signal, count);
392
393#ifdef SA_SIGINFO
394 if (TEVENT_SIG_PENDING(sig_state->sig_blocked[i])) {
395 /* We'd filled the queue, unblock the
396 signal now the queue is empty again.
397 Note we MUST do this after the
398 TEVENT_SIG_SEEN(sig_state->signal_count[i], count)
399 call to prevent a new signal running
400 out of room in the sig_state->sig_info[i][]
401 ring buffer. */
402 sigset_t set;
403 sigemptyset(&set);
404 sigaddset(&set, i);
405 TEVENT_SIG_SEEN(sig_state->sig_blocked[i],
406 tevent_sig_count(sig_state->sig_blocked[i]));
407 sigprocmask(SIG_UNBLOCK, &set, NULL);
408 }
409#endif
410 }
411
412 return 1;
413}
414
415void tevent_cleanup_pending_signal_handlers(struct tevent_signal *se)
416{
417 struct tevent_common_signal_list *sl;
418 sl = talloc_get_type(se->additional_data,
419 struct tevent_common_signal_list);
420
421 tevent_common_signal_list_destructor(sl);
422
423 if (sig_state->sig_handlers[se->signum] == NULL) {
424 if (sig_state->oldact[se->signum]) {
425 sigaction(se->signum, sig_state->oldact[se->signum], NULL);
426 sig_state->oldact[se->signum] = NULL;
427 }
428 }
429 return;
430}
Note: See TracBrowser for help on using the repository browser.