4 * @brief Implementation of bus_t.
9 * Copyright (C) 2006 Martin Willi
10 * Hochschule fuer Technik Rapperswil
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 ENUM(signal_names
, SIG_ANY
, SIG_MAX
,
28 /** should not get printed */
30 /** debugging message types */
41 /** should not get printed */
43 /** all level0 signals are AUDIT signals */
52 /** should not get printed */
56 typedef struct active_listener_t active_listener_t
;
59 * information for a active listener
61 struct active_listener_t
{
69 * condvar to wait for a signal
77 /** not registered, do not wait for thread */
79 /** registered, if a signal occurs, wait until it is LISTENING */
81 /** listening, deliver signal */
86 * currently processed signals type
91 * verbosity level of the signal
96 * current processed signals thread number
101 * currently processed signals ike_sa
106 * currently processed signals format string
111 * currently processed signals format varargs
117 typedef struct private_bus_t private_bus_t
;
120 * Private data of a bus_t object.
122 struct private_bus_t
{
124 * Public part of a bus_t object.
129 * List of registered listeners implementing the bus_t interface
131 linked_list_t
*listeners
;
134 * List of active listeners with listener_state TRUE
136 linked_list_t
*active_listeners
;
139 * mutex to synchronize active listeners
141 pthread_mutex_t mutex
;
144 * Thread local storage for a unique, simple thread ID
146 pthread_key_t thread_id
;
149 * Thread local storage the threads IKE_SA
151 pthread_key_t thread_sa
;
156 * Get a unique thread number for a calling thread. Since
157 * pthread_self returns large and ugly numbers, use this function
158 * for logging; these numbers are incremental starting at 1
160 static int get_thread_number(private_bus_t
*this)
162 static long current_num
= 0;
163 static long stored_num
;
165 stored_num
= (long)pthread_getspecific(this->thread_id
);
167 { /* first call of current thread */
168 pthread_setspecific(this->thread_id
, (void*)++current_num
);
178 * Implementation of bus_t.add_listener.
180 static void add_listener(private_bus_t
*this, bus_listener_t
*listener
)
182 pthread_mutex_lock(&this->mutex
);
183 this->listeners
->insert_last(this->listeners
, (void*)listener
);
184 pthread_mutex_unlock(&this->mutex
);
188 * Get the listener object for the calling thread
190 static active_listener_t
*get_active_listener(private_bus_t
*this)
192 active_listener_t
*current
, *found
= NULL
;
193 iterator_t
*iterator
;
195 /* if the thread was here once before, we have a active_listener record */
196 iterator
= this->active_listeners
->create_iterator(this->active_listeners
, TRUE
);
197 while (iterator
->iterate(iterator
, (void**)¤t
))
199 if (current
->id
== pthread_self())
205 iterator
->destroy(iterator
);
209 /* create a new object for a never-seen thread */
210 found
= malloc_thing(active_listener_t
);
211 found
->id
= pthread_self();
212 pthread_cond_init(&found
->cond
, NULL
);
213 this->active_listeners
->insert_last(this->active_listeners
, found
);
220 * Implementation of bus_t.listen.
222 static signal_t
listen_(private_bus_t
*this, level_t
*level
, int *thread
,
223 ike_sa_t
**ike_sa
, char** format
, va_list* args
)
225 active_listener_t
*listener
;
227 pthread_mutex_lock(&this->mutex
);
228 listener
= get_active_listener(this);
229 /* go "listening", say hello to a thread which have a signal for us */
230 listener
->state
= LISTENING
;
231 pthread_cond_broadcast(&listener
->cond
);
232 /* wait until it has us delivered a signal, and go back to "registered" */
233 pthread_cond_wait(&listener
->cond
, &this->mutex
);
234 pthread_mutex_unlock(&this->mutex
);
236 /* return signal values */
237 *level
= listener
->level
;
238 *thread
= listener
->thread
;
239 *ike_sa
= listener
->ike_sa
;
240 *format
= listener
->format
;
241 va_copy(*args
, listener
->args
);
242 va_end(listener
->args
);
244 return listener
->signal
;
248 * Implementation of bus_t.set_listen_state.
250 static void set_listen_state(private_bus_t
*this, bool active
)
252 active_listener_t
*listener
;
254 pthread_mutex_lock(&this->mutex
);
256 listener
= get_active_listener(this);
259 listener
->state
= REGISTERED
;
263 listener
->state
= UNREGISTERED
;
264 /* say hello to signal emitter; we are finished processing the signal */
265 pthread_cond_signal(&listener
->cond
);
268 pthread_mutex_unlock(&this->mutex
);
273 * Implementation of bus_t.set_sa.
275 static void set_sa(private_bus_t
*this, ike_sa_t
*ike_sa
)
277 pthread_setspecific(this->thread_sa
, ike_sa
);
281 * Implementation of bus_t.vsignal.
283 static void vsignal(private_bus_t
*this, signal_t signal
, level_t level
,
284 char* format
, va_list args
)
286 iterator_t
*iterator
;
287 bus_listener_t
*listener
;
288 active_listener_t
*active_listener
;
292 ike_sa
= pthread_getspecific(this->thread_sa
);
293 thread
= get_thread_number(this);
295 pthread_mutex_lock(&this->mutex
);
297 /* do the job for all passive bus_listeners */
298 iterator
= this->listeners
->create_iterator(this->listeners
, TRUE
);
299 while (iterator
->iterate(iterator
, (void**)&listener
))
303 va_copy(args_copy
, args
);
304 listener
->signal(listener
, signal
, level
, thread
, ike_sa
, format
, args_copy
);
307 iterator
->destroy(iterator
);
309 /* wake up all active listeners */
310 iterator
= this->active_listeners
->create_iterator(this->active_listeners
, TRUE
);
311 while (iterator
->iterate(iterator
, (void**)&active_listener
))
313 /* wait until it is back */
314 while (active_listener
->state
== REGISTERED
)
316 pthread_cond_wait(&active_listener
->cond
, &this->mutex
);
318 /* if thread is listening now, give it the signal to process */
319 if (active_listener
->state
== LISTENING
)
321 active_listener
->level
= level
;
322 active_listener
->thread
= thread
;
323 active_listener
->ike_sa
= ike_sa
;
324 active_listener
->signal
= signal
;
325 active_listener
->format
= format
;
326 va_copy(active_listener
->args
, args
);
327 active_listener
->state
= REGISTERED
;
328 pthread_cond_signal(&active_listener
->cond
);
332 /* we must wait now until all are not in state REGISTERED,
333 * as they may still use our arguments */
334 iterator
->reset(iterator
);
335 while (iterator
->iterate(iterator
, (void**)&active_listener
))
337 while (active_listener
->state
== REGISTERED
)
339 pthread_cond_wait(&active_listener
->cond
, &this->mutex
);
342 iterator
->destroy(iterator
);
344 pthread_mutex_unlock(&this->mutex
);
348 * Implementation of bus_t.signal.
350 static void signal_(private_bus_t
*this, signal_t signal
, level_t level
,
355 va_start(args
, format
);
356 vsignal(this, signal
, level
, format
, args
);
361 * Implementation of bus_t.destroy.
363 static void destroy(private_bus_t
*this)
365 this->active_listeners
->destroy_function(this->active_listeners
, free
);
366 this->listeners
->destroy(this->listeners
);
371 * Described in header.
375 private_bus_t
*this = malloc_thing(private_bus_t
);
377 this->public.add_listener
= (void(*)(bus_t
*,bus_listener_t
*))add_listener
;
378 this->public.listen
= (signal_t(*)(bus_t
*,level_t
*,int*,ike_sa_t
**,char**,va_list*))listen_
;
379 this->public.set_listen_state
= (void(*)(bus_t
*,bool))set_listen_state
;
380 this->public.set_sa
= (void(*)(bus_t
*,ike_sa_t
*))set_sa
;
381 this->public.signal
= (void(*)(bus_t
*,signal_t
,level_t
,char*,...))signal_
;
382 this->public.vsignal
= (void(*)(bus_t
*,signal_t
,level_t
,char*,va_list))vsignal
;
383 this->public.destroy
= (void(*)(bus_t
*)) destroy
;
385 this->listeners
= linked_list_create();
386 this->active_listeners
= linked_list_create();
387 pthread_mutex_init(&this->mutex
, NULL
);
388 pthread_key_create(&this->thread_id
, NULL
);
389 pthread_key_create(&this->thread_sa
, NULL
);
391 return &(this->public);