1c57fcc40b26767744246302af2ee7bd1a6b3d19
[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 * condvar where active listeners wait
105 */
106 condvar_t *condvar;
107 };
108
109 /**
110 * create a listener entry
111 */
112 static entry_t *entry_create(bus_listener_t *listener, bool blocker)
113 {
114 entry_t *this = malloc_thing(entry_t);
115
116 this->listener = listener;
117 this->blocker = blocker;
118 this->condvar = condvar_create(CONDVAR_DEFAULT);
119
120 return this;
121 }
122
123 /**
124 * destroy an entry_t
125 */
126 static void entry_destroy(entry_t *entry)
127 {
128 entry->condvar->destroy(entry->condvar);
129 free(entry);
130 }
131
132 /**
133 * Get a unique thread number for a calling thread. Since
134 * pthread_self returns large and ugly numbers, use this function
135 * for logging; these numbers are incremental starting at 1
136 */
137 static int get_thread_number(private_bus_t *this)
138 {
139 static long current_num = 0;
140 long stored_num;
141
142 stored_num = (long)pthread_getspecific(this->thread_id);
143 if (stored_num == 0)
144 { /* first call of current thread */
145 pthread_setspecific(this->thread_id, (void*)++current_num);
146 return current_num;
147 }
148 else
149 {
150 return stored_num;
151 }
152 }
153
154 /**
155 * Implementation of bus_t.add_listener.
156 */
157 static void add_listener(private_bus_t *this, bus_listener_t *listener)
158 {
159 this->mutex->lock(this->mutex);
160 this->listeners->insert_last(this->listeners, entry_create(listener, FALSE));
161 this->mutex->unlock(this->mutex);
162 }
163
164 /**
165 * Implementation of bus_t.remove_listener.
166 */
167 static void remove_listener(private_bus_t *this, bus_listener_t *listener)
168 {
169 iterator_t *iterator;
170 entry_t *entry;
171
172 this->mutex->lock(this->mutex);
173 iterator = this->listeners->create_iterator(this->listeners, TRUE);
174 while (iterator->iterate(iterator, (void**)&entry))
175 {
176 if (entry->listener == listener)
177 {
178 iterator->remove(iterator);
179 entry_destroy(entry);
180 break;
181 }
182 }
183 iterator->destroy(iterator);
184 this->mutex->unlock(this->mutex);
185 }
186
187 typedef struct cleanup_data_t cleanup_data_t;
188
189 /**
190 * data to remove a listener using pthread_cleanup handler
191 */
192 struct cleanup_data_t {
193 /** bus instance */
194 private_bus_t *this;
195 /** listener entry */
196 entry_t *entry;
197 };
198
199 /**
200 * pthread_cleanup handler to remove a listener
201 */
202 static void listener_cleanup(cleanup_data_t *data)
203 {
204 iterator_t *iterator;
205 entry_t *entry;
206
207 iterator = data->this->listeners->create_iterator(data->this->listeners, TRUE);
208 while (iterator->iterate(iterator, (void**)&entry))
209 {
210 if (entry == data->entry)
211 {
212 iterator->remove(iterator);
213 entry_destroy(entry);
214 break;
215 }
216 }
217 iterator->destroy(iterator);
218 }
219
220 /**
221 * Implementation of bus_t.listen.
222 */
223 static void listen_(private_bus_t *this, bus_listener_t *listener, job_t *job)
224 {
225 int old;
226 cleanup_data_t data;
227
228 data.this = this;
229 data.entry = entry_create(listener, TRUE);
230
231 this->mutex->lock(this->mutex);
232 this->listeners->insert_last(this->listeners, data.entry);
233 charon->processor->queue_job(charon->processor, job);
234 pthread_cleanup_push((void*)this->mutex->unlock, this->mutex);
235 pthread_cleanup_push((void*)listener_cleanup, &data);
236 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
237 while (data.entry->blocker)
238 {
239 data.entry->condvar->wait(data.entry->condvar, this->mutex);
240 }
241 pthread_setcancelstate(old, NULL);
242 pthread_cleanup_pop(FALSE);
243 /* unlock mutex */
244 pthread_cleanup_pop(TRUE);
245 entry_destroy(data.entry);
246 }
247
248 /**
249 * Implementation of bus_t.set_sa.
250 */
251 static void set_sa(private_bus_t *this, ike_sa_t *ike_sa)
252 {
253 pthread_setspecific(this->thread_sa, ike_sa);
254 }
255
256 /**
257 * data associated to a signal, passed to callback
258 */
259 typedef struct {
260 /** associated IKE_SA */
261 ike_sa_t *ike_sa;
262 /** invoking thread */
263 long thread;
264 /** signal type */
265 signal_t signal;
266 /** signal level */
267 level_t level;
268 /** format string */
269 char *format;
270 /** argument list */
271 va_list args;
272 } signal_data_t;
273
274 /**
275 * listener invocation as a list remove callback
276 */
277 static bool signal_cb(entry_t *entry, signal_data_t *data)
278 {
279 if (!entry->listener->signal(entry->listener, data->signal, data->level,
280 data->thread, data->ike_sa, data->format, data->args))
281 {
282 if (entry->blocker)
283 {
284 entry->blocker = FALSE;
285 entry->condvar->signal(entry->condvar);
286 }
287 else
288 {
289 entry_destroy(entry);
290 }
291 return TRUE;
292 }
293 return FALSE;
294 }
295
296 /**
297 * Implementation of bus_t.vsignal.
298 */
299 static void vsignal(private_bus_t *this, signal_t signal, level_t level,
300 char* format, va_list args)
301 {
302 signal_data_t data;
303
304 data.ike_sa = pthread_getspecific(this->thread_sa);
305 data.thread = get_thread_number(this);
306 data.signal = signal;
307 data.level = level;
308 data.format = format;
309 va_copy(data.args, args);
310
311 this->mutex->lock(this->mutex);
312 /* we use the remove() method to invoke all listeners with small overhead */
313 this->listeners->remove(this->listeners, &data, (void*)signal_cb);
314 this->mutex->unlock(this->mutex);
315
316 va_end(data.args);
317 }
318
319 /**
320 * Implementation of bus_t.signal.
321 */
322 static void signal_(private_bus_t *this, signal_t signal, level_t level,
323 char* format, ...)
324 {
325 va_list args;
326
327 va_start(args, format);
328 vsignal(this, signal, level, format, args);
329 va_end(args);
330 }
331
332 /**
333 * Implementation of bus_t.destroy.
334 */
335 static void destroy(private_bus_t *this)
336 {
337 this->mutex->destroy(this->mutex);
338 this->listeners->destroy_function(this->listeners, (void*)entry_destroy);
339 free(this);
340 }
341
342 /*
343 * Described in header.
344 */
345 bus_t *bus_create()
346 {
347 private_bus_t *this = malloc_thing(private_bus_t);
348
349 this->public.add_listener = (void(*)(bus_t*,bus_listener_t*))add_listener;
350 this->public.remove_listener = (void(*)(bus_t*,bus_listener_t*))remove_listener;
351 this->public.listen = (void(*)(bus_t*, bus_listener_t *listener, job_t *job))listen_;
352 this->public.set_sa = (void(*)(bus_t*,ike_sa_t*))set_sa;
353 this->public.signal = (void(*)(bus_t*,signal_t,level_t,char*,...))signal_;
354 this->public.vsignal = (void(*)(bus_t*,signal_t,level_t,char*,va_list))vsignal;
355 this->public.destroy = (void(*)(bus_t*)) destroy;
356
357 this->listeners = linked_list_create();
358 this->mutex = mutex_create(MUTEX_DEFAULT);
359 pthread_key_create(&this->thread_id, NULL);
360 pthread_key_create(&this->thread_sa, NULL);
361
362 return &this->public;
363 }
364