c0803874d85a57ef3a32a4b8ea5e39d495537a78
[strongswan.git] / src / charon / bus / bus.c
1 /*
2 * Copyright (C) 2006 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * $Id$
16 */
17
18 #include "bus.h"
19
20 #include <pthread.h>
21
22 #include <daemon.h>
23 #include <utils/mutex.h>
24
25 ENUM(signal_names, SIG_ANY, SIG_MAX,
26 /** should not get printed */
27 "SIG_ANY",
28 /** debugging message types */
29 "DMN",
30 "MGR",
31 "IKE",
32 "CHD",
33 "JOB",
34 "CFG",
35 "KNL",
36 "NET",
37 "ENC",
38 "LIB",
39 /** should not get printed */
40 "SIG_DBG_MAX",
41 /** all level0 signals are AUDIT signals */
42 "AUD", "AUD", "AUD",
43 "AUD", "AUD", "AUD",
44 "AUD", "AUD", "AUD",
45 "AUD", "AUD", "AUD",
46 "AUD", "AUD", "AUD",
47 "AUD", "AUD", "AUD",
48 "AUD", "AUD", "AUD",
49 "AUD", "AUD", "AUD",
50 /** should not get printed */
51 "SIG_MAX",
52 );
53
54 typedef struct private_bus_t private_bus_t;
55
56 /**
57 * Private data of a bus_t object.
58 */
59 struct private_bus_t {
60 /**
61 * Public part of a bus_t object.
62 */
63 bus_t public;
64
65 /**
66 * List of registered listeners as entry_t's
67 */
68 linked_list_t *listeners;
69
70 /**
71 * mutex to synchronize active listeners, recursively
72 */
73 mutex_t *mutex;
74
75 /**
76 * Thread local storage for a unique, simple thread ID
77 */
78 pthread_key_t thread_id;
79
80 /**
81 * Thread local storage the threads IKE_SA
82 */
83 pthread_key_t thread_sa;
84 };
85
86 typedef struct entry_t entry_t;
87
88 /**
89 * a listener entry, either active or passive
90 */
91 struct entry_t {
92
93 /**
94 * registered listener interface
95 */
96 bus_listener_t *listener;
97
98 /**
99 * is this a active listen() call with a blocking thread
100 */
101 bool blocker;
102
103 /**
104 * are we currently calling this listener
105 */
106 bool calling;
107
108 /**
109 * condvar where active listeners wait
110 */
111 condvar_t *condvar;
112 };
113
114 /**
115 * create a listener entry
116 */
117 static entry_t *entry_create(bus_listener_t *listener, bool blocker)
118 {
119 entry_t *this = malloc_thing(entry_t);
120
121 this->listener = listener;
122 this->blocker = blocker;
123 this->calling = FALSE;
124 this->condvar = condvar_create(CONDVAR_DEFAULT);
125
126 return this;
127 }
128
129 /**
130 * destroy an entry_t
131 */
132 static void entry_destroy(entry_t *entry)
133 {
134 entry->condvar->destroy(entry->condvar);
135 free(entry);
136 }
137
138 /**
139 * Get a unique thread number for a calling thread. Since
140 * pthread_self returns large and ugly numbers, use this function
141 * for logging; these numbers are incremental starting at 1
142 */
143 static int get_thread_number(private_bus_t *this)
144 {
145 static long current_num = 0;
146 long stored_num;
147
148 stored_num = (long)pthread_getspecific(this->thread_id);
149 if (stored_num == 0)
150 { /* first call of current thread */
151 pthread_setspecific(this->thread_id, (void*)++current_num);
152 return current_num;
153 }
154 else
155 {
156 return stored_num;
157 }
158 }
159
160 /**
161 * Implementation of bus_t.add_listener.
162 */
163 static void add_listener(private_bus_t *this, bus_listener_t *listener)
164 {
165 this->mutex->lock(this->mutex);
166 this->listeners->insert_last(this->listeners, entry_create(listener, FALSE));
167 this->mutex->unlock(this->mutex);
168 }
169
170 /**
171 * Implementation of bus_t.remove_listener.
172 */
173 static void remove_listener(private_bus_t *this, bus_listener_t *listener)
174 {
175 iterator_t *iterator;
176 entry_t *entry;
177
178 this->mutex->lock(this->mutex);
179 iterator = this->listeners->create_iterator(this->listeners, TRUE);
180 while (iterator->iterate(iterator, (void**)&entry))
181 {
182 if (entry->listener == listener)
183 {
184 iterator->remove(iterator);
185 entry_destroy(entry);
186 break;
187 }
188 }
189 iterator->destroy(iterator);
190 this->mutex->unlock(this->mutex);
191 }
192
193 typedef struct cleanup_data_t cleanup_data_t;
194
195 /**
196 * data to remove a listener using pthread_cleanup handler
197 */
198 struct cleanup_data_t {
199 /** bus instance */
200 private_bus_t *this;
201 /** listener entry */
202 entry_t *entry;
203 };
204
205 /**
206 * pthread_cleanup handler to remove a listener
207 */
208 static void listener_cleanup(cleanup_data_t *data)
209 {
210 iterator_t *iterator;
211 entry_t *entry;
212
213 iterator = data->this->listeners->create_iterator(data->this->listeners, TRUE);
214 while (iterator->iterate(iterator, (void**)&entry))
215 {
216 if (entry == data->entry)
217 {
218 iterator->remove(iterator);
219 entry_destroy(entry);
220 break;
221 }
222 }
223 iterator->destroy(iterator);
224 }
225
226 /**
227 * Implementation of bus_t.listen.
228 */
229 static void listen_(private_bus_t *this, bus_listener_t *listener, job_t *job)
230 {
231 int old;
232 cleanup_data_t data;
233
234 data.this = this;
235 data.entry = entry_create(listener, TRUE);
236
237 this->mutex->lock(this->mutex);
238 this->listeners->insert_last(this->listeners, data.entry);
239 charon->processor->queue_job(charon->processor, job);
240 pthread_cleanup_push((void*)this->mutex->unlock, this->mutex);
241 pthread_cleanup_push((void*)listener_cleanup, &data);
242 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
243 while (data.entry->blocker)
244 {
245 data.entry->condvar->wait(data.entry->condvar, this->mutex);
246 }
247 pthread_setcancelstate(old, NULL);
248 pthread_cleanup_pop(FALSE);
249 /* unlock mutex */
250 pthread_cleanup_pop(TRUE);
251 entry_destroy(data.entry);
252 }
253
254 /**
255 * Implementation of bus_t.set_sa.
256 */
257 static void set_sa(private_bus_t *this, ike_sa_t *ike_sa)
258 {
259 pthread_setspecific(this->thread_sa, ike_sa);
260 }
261
262 /**
263 * data associated to a signal, passed to callback
264 */
265 typedef struct {
266 /** associated IKE_SA */
267 ike_sa_t *ike_sa;
268 /** invoking thread */
269 long thread;
270 /** signal type */
271 signal_t signal;
272 /** signal level */
273 level_t level;
274 /** signal specific user data */
275 void *user;
276 /** format string */
277 char *format;
278 /** argument list */
279 va_list args;
280 } signal_data_t;
281
282 /**
283 * listener invocation as a list remove callback
284 */
285 static bool signal_cb(entry_t *entry, signal_data_t *data)
286 {
287 va_list args;
288
289 if (entry->calling)
290 { /* avoid recursive calls */
291 return FALSE;
292 }
293 entry->calling = TRUE;
294 va_copy(args, data->args);
295 if (!entry->listener->signal(entry->listener, data->signal, data->level,
296 data->thread, data->ike_sa, data->user, data->format, args))
297 {
298 if (entry->blocker)
299 {
300 entry->blocker = FALSE;
301 entry->condvar->signal(entry->condvar);
302 }
303 else
304 {
305 entry_destroy(entry);
306 }
307 va_end(args);
308 entry->calling = FALSE;
309 return TRUE;
310 }
311 va_end(args);
312 entry->calling = FALSE;
313 return FALSE;
314 }
315
316 /**
317 * Implementation of bus_t.vsignal.
318 */
319 static void vsignal(private_bus_t *this, signal_t signal, level_t level,
320 void *user, char* format, va_list args)
321 {
322 signal_data_t data;
323
324 data.ike_sa = pthread_getspecific(this->thread_sa);
325 data.thread = get_thread_number(this);
326 data.signal = signal;
327 data.level = level;
328 data.user = user;
329 data.format = format;
330 va_copy(data.args, args);
331
332 this->mutex->lock(this->mutex);
333 /* we use the remove() method to invoke all listeners with small overhead */
334 this->listeners->remove(this->listeners, &data, (void*)signal_cb);
335 this->mutex->unlock(this->mutex);
336
337 va_end(data.args);
338 }
339
340 /**
341 * Implementation of bus_t.signal.
342 */
343 static void signal_(private_bus_t *this, signal_t signal, level_t level,
344 void* data, char* format, ...)
345 {
346 va_list args;
347
348 va_start(args, format);
349 vsignal(this, signal, level, data, format, args);
350 va_end(args);
351 }
352
353 /**
354 * Implementation of bus_t.destroy.
355 */
356 static void destroy(private_bus_t *this)
357 {
358 this->mutex->destroy(this->mutex);
359 this->listeners->destroy_function(this->listeners, (void*)entry_destroy);
360 free(this);
361 }
362
363 /*
364 * Described in header.
365 */
366 bus_t *bus_create()
367 {
368 private_bus_t *this = malloc_thing(private_bus_t);
369
370 this->public.add_listener = (void(*)(bus_t*,bus_listener_t*))add_listener;
371 this->public.remove_listener = (void(*)(bus_t*,bus_listener_t*))remove_listener;
372 this->public.listen = (void(*)(bus_t*, bus_listener_t *listener, job_t *job))listen_;
373 this->public.set_sa = (void(*)(bus_t*,ike_sa_t*))set_sa;
374 this->public.signal = (void(*)(bus_t*,signal_t,level_t,void*,char*,...))signal_;
375 this->public.vsignal = (void(*)(bus_t*,signal_t,level_t,void*,char*,va_list))vsignal;
376 this->public.destroy = (void(*)(bus_t*)) destroy;
377
378 this->listeners = linked_list_create();
379 this->mutex = mutex_create(MUTEX_DEFAULT);
380 pthread_key_create(&this->thread_id, NULL);
381 pthread_key_create(&this->thread_sa, NULL);
382
383 return &this->public;
384 }
385