Mercurial > hg > freeDiameter
comparison libfreeDiameter/signal.c @ 235:8773740401a5
Centralized signal handlers management in the library
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Fri, 05 Mar 2010 19:01:48 +0900 |
parents | |
children | 60f34df3e025 |
comparison
equal
deleted
inserted
replaced
234:f91fe0b85928 | 235:8773740401a5 |
---|---|
1 /********************************************************************************************************* | |
2 * Software License Agreement (BSD License) * | |
3 * Author: Sebastien Decugis <sdecugis@nict.go.jp> * | |
4 * * | |
5 * Copyright (c) 2009, WIDE Project and NICT * | |
6 * All rights reserved. * | |
7 * * | |
8 * Redistribution and use of this software in source and binary forms, with or without modification, are * | |
9 * permitted provided that the following conditions are met: * | |
10 * * | |
11 * * Redistributions of source code must retain the above * | |
12 * copyright notice, this list of conditions and the * | |
13 * following disclaimer. * | |
14 * * | |
15 * * Redistributions in binary form must reproduce the above * | |
16 * copyright notice, this list of conditions and the * | |
17 * following disclaimer in the documentation and/or other * | |
18 * materials provided with the distribution. * | |
19 * * | |
20 * * Neither the name of the WIDE Project or NICT nor the * | |
21 * names of its contributors may be used to endorse or * | |
22 * promote products derived from this software without * | |
23 * specific prior written permission of WIDE Project and * | |
24 * NICT. * | |
25 * * | |
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * | |
27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * | |
28 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * | |
29 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * | |
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * | |
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * | |
32 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * | |
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * | |
34 *********************************************************************************************************/ | |
35 | |
36 #include "libfD.h" | |
37 | |
38 #include <signal.h> | |
39 | |
40 /* List of signal names */ | |
41 #ifdef HAVE_SIGNALENT_H | |
42 const char *const fd_sig_str[] = { | |
43 # include "signalent.h" | |
44 }; | |
45 const int fd_sig_nstr = sizeof fd_sig_str / sizeof fd_sig_str[0]; | |
46 #endif /* HAVE_SIGNALENT_H */ | |
47 | |
48 /* A structure to hold the registered signal handlers */ | |
49 struct sig_hdl { | |
50 struct fd_list chain; /* Link in the sig_hdl_list ordered by signal */ | |
51 int signal; /* The signal this handler is registered for */ | |
52 void (*sig_hdl)(int);/* The callback to call when the signal is caught */ | |
53 pthread_t sig_thr; /* The thread that receives the signal */ | |
54 char *modname; /* The name of the module who registered the signal (for debug) */ | |
55 }; | |
56 | |
57 /* The list of sig_hdl, and the mutex to protect it */ | |
58 static struct fd_list sig_hdl_list = FD_LIST_INITIALIZER(sig_hdl_list); | |
59 static pthread_mutex_t sig_hdl_lock = PTHREAD_MUTEX_INITIALIZER; | |
60 | |
61 /* Detached thread attribute */ | |
62 static pthread_attr_t detached; | |
63 | |
64 /* Note if the module was initialized */ | |
65 static int sig_initialized = 0; | |
66 | |
67 /* Initialize the support for signals */ | |
68 int fd_sig_init(void) | |
69 { | |
70 sigset_t sig_all; | |
71 | |
72 TRACE_ENTRY(""); | |
73 | |
74 if (sig_initialized) | |
75 return 0; | |
76 | |
77 /* Block all signals from the current thread and all its future children, so that signals are delivered only to our sig_hdl->sig_thr */ | |
78 sigfillset(&sig_all); | |
79 CHECK_POSIX( pthread_sigmask(SIG_BLOCK, &sig_all, NULL) ); | |
80 | |
81 /* Initialize the detached attribute */ | |
82 CHECK_POSIX( pthread_attr_init(&detached) ); | |
83 CHECK_POSIX( pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED) ); | |
84 | |
85 sig_initialized++; | |
86 | |
87 /* That's all we have to do here ... */ | |
88 return 0; | |
89 } | |
90 | |
91 | |
92 /* This thread (created detached) does call the signal handler */ | |
93 static void * signal_caller(void * arg) | |
94 { | |
95 struct sig_hdl * me = arg; | |
96 char buf[40]; | |
97 | |
98 /* Name this thread */ | |
99 snprintf(buf, sizeof(buf), "cb %d:%s:%s", me->signal, SIGNALSTR(me->signal), me->modname); | |
100 fd_log_threadname ( buf ); | |
101 | |
102 TRACE_ENTRY("%p", me); | |
103 | |
104 /* Call the signal handler */ | |
105 (*me->sig_hdl)(me->signal); | |
106 | |
107 TRACE_DEBUG(CALL, "Callback completed"); | |
108 | |
109 /* Done! */ | |
110 return NULL; | |
111 } | |
112 | |
113 /* This thread "really" receives the signal and starts the signal_caller thread */ | |
114 static void * signal_catcher(void * arg) | |
115 { | |
116 struct sig_hdl * me = arg; | |
117 sigset_t ss; | |
118 char buf[40]; | |
119 | |
120 ASSERT(arg); | |
121 | |
122 /* Name this thread */ | |
123 snprintf(buf, sizeof(buf), "catch %d:%s:%s", me->signal, SIGNALSTR(me->signal), me->modname); | |
124 fd_log_threadname ( buf ); | |
125 | |
126 TRACE_ENTRY("%p", me); | |
127 | |
128 sigemptyset(&ss); | |
129 sigaddset(&ss, me->signal); | |
130 | |
131 /* Now loop on the reception of the signal */ | |
132 while (1) { | |
133 int sig; | |
134 pthread_t th; | |
135 | |
136 /* Wait to receive the next signal */ | |
137 CHECK_POSIX_DO( sigwait(&ss, &sig), break ); | |
138 | |
139 TRACE_DEBUG(FULL, "Signal %d caught", sig); | |
140 | |
141 /* Create the thread that will call the handler */ | |
142 CHECK_POSIX_DO( pthread_create( &th, &detached, signal_caller, arg ), break ); | |
143 } | |
144 | |
145 /* An error occurred... What should we do ? */ | |
146 TRACE_DEBUG(INFO, "!!! ERROR !!! The signal catcher for %d:%s:%s is terminating!", me->signal, SIGNALSTR(me->signal), me->modname); | |
147 | |
148 /* Better way to handle this ? */ | |
149 ASSERT(0); | |
150 return NULL; | |
151 } | |
152 | |
153 /* Register a new callback to be called on reception of a given signal (it receives the signal as parameter) */ | |
154 /* EALREADY will be returned if there is already a callback registered on this signal */ | |
155 /* NOTE: the signal handler will be called from a new detached thread */ | |
156 int fd_sig_register(int signal, char * modname, void (*handler)(int signal)) | |
157 { | |
158 struct sig_hdl * new; | |
159 struct fd_list * next = NULL; | |
160 int matched = 0; | |
161 | |
162 TRACE_ENTRY("%d %p %p", signal, modname, handler); | |
163 CHECK_PARAMS( signal && modname && handler && sig_initialized ); | |
164 | |
165 /* Alloc all memory before taking the lock */ | |
166 CHECK_MALLOC( new = malloc(sizeof(struct sig_hdl)) ); | |
167 memset(new, 0, sizeof(struct sig_hdl)); | |
168 fd_list_init(&new->chain, new); | |
169 new->signal = signal; | |
170 new->sig_hdl = handler; | |
171 CHECK_MALLOC_DO( new->modname = strdup(modname), { free(new); return ENOMEM; } ); | |
172 | |
173 /* Search in the list if we already have a handler for this signal */ | |
174 CHECK_POSIX(pthread_mutex_lock(&sig_hdl_lock)); | |
175 for (next = sig_hdl_list.next; next != &sig_hdl_list; next = next->next) { | |
176 struct sig_hdl * nh = (struct sig_hdl *)next; | |
177 | |
178 if (nh->signal < signal) | |
179 continue; | |
180 if (nh->signal == signal) { | |
181 matched = 1; | |
182 TRACE_DEBUG(INFO, "Signal %d (%s) is already registered by module '%s'", signal, SIGNALSTR(signal), nh->modname); | |
183 } | |
184 break; | |
185 } | |
186 if (!matched) | |
187 /* Insert the new element before the next handle */ | |
188 fd_list_insert_before(next, &new->chain); | |
189 | |
190 CHECK_POSIX(pthread_mutex_unlock(&sig_hdl_lock)); | |
191 | |
192 if (matched) | |
193 return EALREADY; /* Already registered... */ | |
194 | |
195 /* OK, now start the thread */ | |
196 CHECK_POSIX( pthread_create( &new->sig_thr, NULL, signal_catcher, new ) ); | |
197 | |
198 /* All good */ | |
199 return 0; | |
200 } | |
201 | |
202 /* Dump list of handlers */ | |
203 void fd_sig_dump(int level, int indent) | |
204 { | |
205 struct fd_list * li = NULL; | |
206 | |
207 if (!TRACE_BOOL(level)) | |
208 return; | |
209 | |
210 | |
211 CHECK_POSIX_DO(pthread_mutex_lock(&sig_hdl_lock), /* continue */); | |
212 if (!FD_IS_LIST_EMPTY(&sig_hdl_list)) | |
213 fd_log_debug("%*sList of registered signal handlers:\n", indent, ""); | |
214 | |
215 for (li = sig_hdl_list.next; li != &sig_hdl_list; li = li->next) { | |
216 struct sig_hdl * h = (struct sig_hdl *)li; | |
217 | |
218 fd_log_debug("%*s - sig %*d (%s) => %p in module %s\n", indent, "", 2, h->signal, SIGNALSTR(h->signal), h->sig_hdl, h->modname); | |
219 } | |
220 CHECK_POSIX_DO(pthread_mutex_unlock(&sig_hdl_lock), /* continue */); | |
221 | |
222 return; | |
223 } | |
224 | |
225 | |
226 /* Remove a callback */ | |
227 int fd_sig_unregister(int signal) | |
228 { | |
229 struct fd_list * li = NULL; | |
230 struct sig_hdl * h = NULL; | |
231 | |
232 TRACE_ENTRY("%d", signal); | |
233 CHECK_PARAMS( signal && sig_initialized ); | |
234 | |
235 /* Search in the list if we already have a handler for this signal */ | |
236 CHECK_POSIX(pthread_mutex_lock(&sig_hdl_lock)); | |
237 for (li = sig_hdl_list.next; li != &sig_hdl_list; li = li->next) { | |
238 struct sig_hdl * nh = (struct sig_hdl *)li; | |
239 | |
240 if (nh->signal < signal) | |
241 continue; | |
242 if (nh->signal == signal) { | |
243 h = nh; | |
244 fd_list_unlink(&h->chain); | |
245 } | |
246 break; | |
247 } | |
248 CHECK_POSIX(pthread_mutex_unlock(&sig_hdl_lock)); | |
249 | |
250 if (!h) | |
251 return ENOENT; | |
252 | |
253 /* Stop the catcher thread */ | |
254 CHECK_FCT( fd_thr_term(&h->sig_thr) ); | |
255 | |
256 /* Free */ | |
257 free(h->modname); | |
258 free(h); | |
259 | |
260 /* All good */ | |
261 return 0; | |
262 } | |
263 | |
264 /* Terminate the signal handler thread -- must be called if fd_sig_init was called */ | |
265 void fd_sig_fini(void) | |
266 { | |
267 if (!sig_initialized) | |
268 return; | |
269 | |
270 /* For each registered handler, stop the thread, free the associated memory */ | |
271 CHECK_POSIX_DO(pthread_mutex_lock(&sig_hdl_lock), /* continue */); | |
272 while (!FD_IS_LIST_EMPTY(&sig_hdl_list)) { | |
273 struct sig_hdl * h = (struct sig_hdl *)sig_hdl_list.next; | |
274 | |
275 /* Unlink */ | |
276 fd_list_unlink(&h->chain); | |
277 | |
278 /* Stop the catcher thread */ | |
279 CHECK_FCT_DO( fd_thr_term(&h->sig_thr), /* continue */ ); | |
280 | |
281 /* Free */ | |
282 free(h->modname); | |
283 free(h); | |
284 } | |
285 CHECK_POSIX_DO(pthread_mutex_unlock(&sig_hdl_lock), /* continue */); | |
286 | |
287 /* Destroy the thread attribute */ | |
288 CHECK_POSIX_DO( pthread_attr_destroy(&detached), /* continue */ ); | |
289 return; | |
290 } |