Properly remove listener when listen() times out
[strongswan.git] / src / libcharon / 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
16 #include "bus.h"
17
18 #include <stdint.h>
19
20 #include <threading/thread.h>
21 #include <threading/thread_value.h>
22 #include <threading/condvar.h>
23 #include <threading/mutex.h>
24
25 typedef struct private_bus_t private_bus_t;
26
27 /**
28 * Private data of a bus_t object.
29 */
30 struct private_bus_t {
31 /**
32 * Public part of a bus_t object.
33 */
34 bus_t public;
35
36 /**
37 * List of registered listeners as entry_t's
38 */
39 linked_list_t *listeners;
40
41 /**
42 * mutex to synchronize active listeners, recursively
43 */
44 mutex_t *mutex;
45
46 /**
47 * Thread local storage the threads IKE_SA
48 */
49 thread_value_t *thread_sa;
50 };
51
52 typedef struct entry_t entry_t;
53
54 /**
55 * a listener entry, either active or passive
56 */
57 struct entry_t {
58
59 /**
60 * registered listener interface
61 */
62 listener_t *listener;
63
64 /**
65 * is this a active listen() call with a blocking thread
66 */
67 bool blocker;
68
69 /**
70 * are we currently calling this listener
71 */
72 int calling;
73
74 /**
75 * condvar where active listeners wait
76 */
77 condvar_t *condvar;
78 };
79
80 /**
81 * create a listener entry
82 */
83 static entry_t *entry_create(listener_t *listener, bool blocker)
84 {
85 entry_t *this = malloc_thing(entry_t);
86
87 this->listener = listener;
88 this->blocker = blocker;
89 this->calling = 0;
90 this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT);
91
92 return this;
93 }
94
95 /**
96 * destroy an entry_t
97 */
98 static void entry_destroy(entry_t *entry)
99 {
100 entry->condvar->destroy(entry->condvar);
101 free(entry);
102 }
103
104 METHOD(bus_t, add_listener, void,
105 private_bus_t *this, listener_t *listener)
106 {
107 this->mutex->lock(this->mutex);
108 this->listeners->insert_last(this->listeners, entry_create(listener, FALSE));
109 this->mutex->unlock(this->mutex);
110 }
111
112 METHOD(bus_t, remove_listener, void,
113 private_bus_t *this, listener_t *listener)
114 {
115 enumerator_t *enumerator;
116 entry_t *entry;
117
118 this->mutex->lock(this->mutex);
119 enumerator = this->listeners->create_enumerator(this->listeners);
120 while (enumerator->enumerate(enumerator, &entry))
121 {
122 if (entry->listener == listener)
123 {
124 this->listeners->remove_at(this->listeners, enumerator);
125 entry_destroy(entry);
126 break;
127 }
128 }
129 enumerator->destroy(enumerator);
130 this->mutex->unlock(this->mutex);
131 }
132
133 typedef struct cleanup_data_t cleanup_data_t;
134
135 /**
136 * data to remove a listener using thread_cleanup_t handler
137 */
138 struct cleanup_data_t {
139 /** bus instance */
140 private_bus_t *this;
141 /** listener entry */
142 entry_t *entry;
143 };
144
145 /**
146 * thread_cleanup_t handler to remove a listener
147 */
148 static void listener_cleanup(cleanup_data_t *data)
149 {
150 data->this->listeners->remove(data->this->listeners, data->entry, NULL);
151 entry_destroy(data->entry);
152 }
153
154 METHOD(bus_t, listen_, bool,
155 private_bus_t *this, listener_t *listener, job_t *job, u_int timeout)
156 {
157 bool old, timed_out = FALSE;
158 cleanup_data_t data;
159 timeval_t tv, add;
160
161 if (timeout)
162 {
163 add.tv_sec = timeout / 1000;
164 add.tv_usec = (timeout - (add.tv_sec * 1000)) * 1000;
165 time_monotonic(&tv);
166 timeradd(&tv, &add, &tv);
167 }
168
169 data.this = this;
170 data.entry = entry_create(listener, TRUE);
171
172 this->mutex->lock(this->mutex);
173 this->listeners->insert_last(this->listeners, data.entry);
174 lib->processor->queue_job(lib->processor, job);
175 thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex);
176 thread_cleanup_push((thread_cleanup_t)listener_cleanup, &data);
177 old = thread_cancelability(TRUE);
178 while (data.entry->blocker)
179 {
180 if (timeout)
181 {
182 if (data.entry->condvar->timed_wait_abs(data.entry->condvar,
183 this->mutex, tv))
184 {
185 this->listeners->remove(this->listeners, data.entry, NULL);
186 timed_out = TRUE;
187 break;
188 }
189 }
190 else
191 {
192 data.entry->condvar->wait(data.entry->condvar, this->mutex);
193 }
194 }
195 thread_cancelability(old);
196 thread_cleanup_pop(FALSE);
197 /* unlock mutex */
198 thread_cleanup_pop(TRUE);
199 entry_destroy(data.entry);
200 return timed_out;
201 }
202
203 METHOD(bus_t, set_sa, void,
204 private_bus_t *this, ike_sa_t *ike_sa)
205 {
206 this->thread_sa->set(this->thread_sa, ike_sa);
207 }
208
209 METHOD(bus_t, get_sa, ike_sa_t*,
210 private_bus_t *this)
211 {
212 return this->thread_sa->get(this->thread_sa);
213 }
214
215 /**
216 * data associated to a signal, passed to callback
217 */
218 typedef struct {
219 /** associated IKE_SA */
220 ike_sa_t *ike_sa;
221 /** invoking thread */
222 long thread;
223 /** debug group */
224 debug_t group;
225 /** debug level */
226 level_t level;
227 /** format string */
228 char *format;
229 /** argument list */
230 va_list args;
231 } log_data_t;
232
233 /**
234 * listener->log() invocation as a list remove callback
235 */
236 static bool log_cb(entry_t *entry, log_data_t *data)
237 {
238 va_list args;
239
240 if (entry->calling || !entry->listener->log)
241 { /* avoid recursive calls */
242 return FALSE;
243 }
244 entry->calling++;
245 va_copy(args, data->args);
246 if (!entry->listener->log(entry->listener, data->group, data->level,
247 data->thread, data->ike_sa, data->format, args))
248 {
249 if (entry->blocker)
250 {
251 entry->blocker = FALSE;
252 entry->condvar->signal(entry->condvar);
253 entry->calling--;
254 }
255 else
256 {
257 entry_destroy(entry);
258 }
259 va_end(args);
260 return TRUE;
261 }
262 va_end(args);
263 entry->calling--;
264 return FALSE;
265 }
266
267 METHOD(bus_t, vlog, void,
268 private_bus_t *this, debug_t group, level_t level,
269 char* format, va_list args)
270 {
271 log_data_t data;
272
273 data.ike_sa = this->thread_sa->get(this->thread_sa);
274 data.thread = thread_current_id();
275 data.group = group;
276 data.level = level;
277 data.format = format;
278 va_copy(data.args, args);
279
280 this->mutex->lock(this->mutex);
281 /* We use the remove() method to invoke all listeners. This is cheap and
282 * does not require an allocation for this performance critical function. */
283 this->listeners->remove(this->listeners, &data, (void*)log_cb);
284 this->mutex->unlock(this->mutex);
285
286 va_end(data.args);
287 }
288
289 METHOD(bus_t, log_, void,
290 private_bus_t *this, debug_t group, level_t level, char* format, ...)
291 {
292 va_list args;
293
294 va_start(args, format);
295 vlog(this, group, level, format, args);
296 va_end(args);
297 }
298
299 /**
300 * unregister a listener
301 */
302 static void unregister_listener(private_bus_t *this, entry_t *entry,
303 enumerator_t *enumerator)
304 {
305 if (entry->blocker)
306 {
307 entry->blocker = FALSE;
308 entry->condvar->signal(entry->condvar);
309 }
310 else
311 {
312 entry_destroy(entry);
313 }
314 this->listeners->remove_at(this->listeners, enumerator);
315 }
316
317 METHOD(bus_t, alert, void,
318 private_bus_t *this, alert_t alert, ...)
319 {
320 enumerator_t *enumerator;
321 ike_sa_t *ike_sa;
322 entry_t *entry;
323 va_list args;
324 bool keep;
325
326 ike_sa = this->thread_sa->get(this->thread_sa);
327
328 this->mutex->lock(this->mutex);
329 enumerator = this->listeners->create_enumerator(this->listeners);
330 while (enumerator->enumerate(enumerator, &entry))
331 {
332 if (entry->calling || !entry->listener->alert)
333 {
334 continue;
335 }
336 entry->calling++;
337 va_start(args, alert);
338 keep = entry->listener->alert(entry->listener, ike_sa, alert, args);
339 va_end(args);
340 entry->calling--;
341 if (!keep)
342 {
343 unregister_listener(this, entry, enumerator);
344 }
345 }
346 enumerator->destroy(enumerator);
347 this->mutex->unlock(this->mutex);
348 }
349
350 METHOD(bus_t, ike_state_change, void,
351 private_bus_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
352 {
353 enumerator_t *enumerator;
354 entry_t *entry;
355 bool keep;
356
357 this->mutex->lock(this->mutex);
358 enumerator = this->listeners->create_enumerator(this->listeners);
359 while (enumerator->enumerate(enumerator, &entry))
360 {
361 if (entry->calling || !entry->listener->ike_state_change)
362 {
363 continue;
364 }
365 entry->calling++;
366 keep = entry->listener->ike_state_change(entry->listener, ike_sa, state);
367 entry->calling--;
368 if (!keep)
369 {
370 unregister_listener(this, entry, enumerator);
371 }
372 }
373 enumerator->destroy(enumerator);
374 this->mutex->unlock(this->mutex);
375 }
376
377 METHOD(bus_t, child_state_change, void,
378 private_bus_t *this, child_sa_t *child_sa, child_sa_state_t state)
379 {
380 enumerator_t *enumerator;
381 ike_sa_t *ike_sa;
382 entry_t *entry;
383 bool keep;
384
385 ike_sa = this->thread_sa->get(this->thread_sa);
386
387 this->mutex->lock(this->mutex);
388 enumerator = this->listeners->create_enumerator(this->listeners);
389 while (enumerator->enumerate(enumerator, &entry))
390 {
391 if (entry->calling || !entry->listener->child_state_change)
392 {
393 continue;
394 }
395 entry->calling++;
396 keep = entry->listener->child_state_change(entry->listener, ike_sa,
397 child_sa, state);
398 entry->calling--;
399 if (!keep)
400 {
401 unregister_listener(this, entry, enumerator);
402 }
403 }
404 enumerator->destroy(enumerator);
405 this->mutex->unlock(this->mutex);
406 }
407
408 METHOD(bus_t, message, void,
409 private_bus_t *this, message_t *message, bool incoming)
410 {
411 enumerator_t *enumerator;
412 ike_sa_t *ike_sa;
413 entry_t *entry;
414 bool keep;
415
416 ike_sa = this->thread_sa->get(this->thread_sa);
417
418 this->mutex->lock(this->mutex);
419 enumerator = this->listeners->create_enumerator(this->listeners);
420 while (enumerator->enumerate(enumerator, &entry))
421 {
422 if (entry->calling || !entry->listener->message)
423 {
424 continue;
425 }
426 entry->calling++;
427 keep = entry->listener->message(entry->listener, ike_sa,
428 message, incoming);
429 entry->calling--;
430 if (!keep)
431 {
432 unregister_listener(this, entry, enumerator);
433 }
434 }
435 enumerator->destroy(enumerator);
436 this->mutex->unlock(this->mutex);
437 }
438
439 METHOD(bus_t, ike_keys, void,
440 private_bus_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh,
441 chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey)
442 {
443 enumerator_t *enumerator;
444 entry_t *entry;
445 bool keep;
446
447 this->mutex->lock(this->mutex);
448 enumerator = this->listeners->create_enumerator(this->listeners);
449 while (enumerator->enumerate(enumerator, &entry))
450 {
451 if (entry->calling || !entry->listener->ike_keys)
452 {
453 continue;
454 }
455 entry->calling++;
456 keep = entry->listener->ike_keys(entry->listener, ike_sa, dh,
457 nonce_i, nonce_r, rekey);
458 entry->calling--;
459 if (!keep)
460 {
461 unregister_listener(this, entry, enumerator);
462 }
463 }
464 enumerator->destroy(enumerator);
465 this->mutex->unlock(this->mutex);
466 }
467
468 METHOD(bus_t, child_keys, void,
469 private_bus_t *this, child_sa_t *child_sa, bool initiator,
470 diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r)
471 {
472 enumerator_t *enumerator;
473 ike_sa_t *ike_sa;
474 entry_t *entry;
475 bool keep;
476
477 ike_sa = this->thread_sa->get(this->thread_sa);
478
479 this->mutex->lock(this->mutex);
480 enumerator = this->listeners->create_enumerator(this->listeners);
481 while (enumerator->enumerate(enumerator, &entry))
482 {
483 if (entry->calling || !entry->listener->child_keys)
484 {
485 continue;
486 }
487 entry->calling++;
488 keep = entry->listener->child_keys(entry->listener, ike_sa, child_sa,
489 initiator, dh, nonce_i, nonce_r);
490 entry->calling--;
491 if (!keep)
492 {
493 unregister_listener(this, entry, enumerator);
494 }
495 }
496 enumerator->destroy(enumerator);
497 this->mutex->unlock(this->mutex);
498 }
499
500 METHOD(bus_t, child_updown, void,
501 private_bus_t *this, child_sa_t *child_sa, bool up)
502 {
503 enumerator_t *enumerator;
504 ike_sa_t *ike_sa;
505 entry_t *entry;
506 bool keep;
507
508 ike_sa = this->thread_sa->get(this->thread_sa);
509
510 this->mutex->lock(this->mutex);
511 enumerator = this->listeners->create_enumerator(this->listeners);
512 while (enumerator->enumerate(enumerator, &entry))
513 {
514 if (entry->calling || !entry->listener->child_updown)
515 {
516 continue;
517 }
518 entry->calling++;
519 keep = entry->listener->child_updown(entry->listener,
520 ike_sa, child_sa, up);
521 entry->calling--;
522 if (!keep)
523 {
524 unregister_listener(this, entry, enumerator);
525 }
526 }
527 enumerator->destroy(enumerator);
528 this->mutex->unlock(this->mutex);
529 }
530
531 METHOD(bus_t, child_rekey, void,
532 private_bus_t *this, child_sa_t *old, child_sa_t *new)
533 {
534 enumerator_t *enumerator;
535 ike_sa_t *ike_sa;
536 entry_t *entry;
537 bool keep;
538
539 ike_sa = this->thread_sa->get(this->thread_sa);
540
541 this->mutex->lock(this->mutex);
542 enumerator = this->listeners->create_enumerator(this->listeners);
543 while (enumerator->enumerate(enumerator, &entry))
544 {
545 if (entry->calling || !entry->listener->child_rekey)
546 {
547 continue;
548 }
549 entry->calling++;
550 keep = entry->listener->child_rekey(entry->listener, ike_sa, old, new);
551 entry->calling--;
552 if (!keep)
553 {
554 unregister_listener(this, entry, enumerator);
555 }
556 }
557 enumerator->destroy(enumerator);
558 this->mutex->unlock(this->mutex);
559 }
560
561 METHOD(bus_t, ike_updown, void,
562 private_bus_t *this, ike_sa_t *ike_sa, bool up)
563 {
564 enumerator_t *enumerator;
565 entry_t *entry;
566 bool keep;
567
568 this->mutex->lock(this->mutex);
569 enumerator = this->listeners->create_enumerator(this->listeners);
570 while (enumerator->enumerate(enumerator, &entry))
571 {
572 if (entry->calling || !entry->listener->ike_updown)
573 {
574 continue;
575 }
576 entry->calling++;
577 keep = entry->listener->ike_updown(entry->listener, ike_sa, up);
578 entry->calling--;
579 if (!keep)
580 {
581 unregister_listener(this, entry, enumerator);
582 }
583 }
584 enumerator->destroy(enumerator);
585 this->mutex->unlock(this->mutex);
586
587 /* a down event for IKE_SA implicitly downs all CHILD_SAs */
588 if (!up)
589 {
590 enumerator_t *enumerator;
591 child_sa_t *child_sa;
592
593 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
594 while (enumerator->enumerate(enumerator, (void**)&child_sa))
595 {
596 child_updown(this, child_sa, FALSE);
597 }
598 enumerator->destroy(enumerator);
599 }
600 }
601
602 METHOD(bus_t, ike_rekey, void,
603 private_bus_t *this, ike_sa_t *old, ike_sa_t *new)
604 {
605 enumerator_t *enumerator;
606 entry_t *entry;
607 bool keep;
608
609 this->mutex->lock(this->mutex);
610 enumerator = this->listeners->create_enumerator(this->listeners);
611 while (enumerator->enumerate(enumerator, &entry))
612 {
613 if (entry->calling || !entry->listener->ike_rekey)
614 {
615 continue;
616 }
617 entry->calling++;
618 keep = entry->listener->ike_rekey(entry->listener, old, new);
619 entry->calling--;
620 if (!keep)
621 {
622 unregister_listener(this, entry, enumerator);
623 }
624 }
625 enumerator->destroy(enumerator);
626 this->mutex->unlock(this->mutex);
627 }
628
629 METHOD(bus_t, authorize, bool,
630 private_bus_t *this, bool final)
631 {
632 enumerator_t *enumerator;
633 ike_sa_t *ike_sa;
634 entry_t *entry;
635 bool keep, success = TRUE;
636
637 ike_sa = this->thread_sa->get(this->thread_sa);
638
639 this->mutex->lock(this->mutex);
640 enumerator = this->listeners->create_enumerator(this->listeners);
641 while (enumerator->enumerate(enumerator, &entry))
642 {
643 if (entry->calling || !entry->listener->authorize)
644 {
645 continue;
646 }
647 entry->calling++;
648 keep = entry->listener->authorize(entry->listener, ike_sa,
649 final, &success);
650 entry->calling--;
651 if (!keep)
652 {
653 unregister_listener(this, entry, enumerator);
654 }
655 if (!success)
656 {
657 break;
658 }
659 }
660 enumerator->destroy(enumerator);
661 this->mutex->unlock(this->mutex);
662 return success;
663 }
664
665 METHOD(bus_t, narrow, void,
666 private_bus_t *this, child_sa_t *child_sa, narrow_hook_t type,
667 linked_list_t *local, linked_list_t *remote)
668 {
669 enumerator_t *enumerator;
670 ike_sa_t *ike_sa;
671 entry_t *entry;
672 bool keep;
673
674 ike_sa = this->thread_sa->get(this->thread_sa);
675
676 this->mutex->lock(this->mutex);
677 enumerator = this->listeners->create_enumerator(this->listeners);
678 while (enumerator->enumerate(enumerator, &entry))
679 {
680 if (entry->calling || !entry->listener->narrow)
681 {
682 continue;
683 }
684 entry->calling++;
685 keep = entry->listener->narrow(entry->listener, ike_sa, child_sa,
686 type, local, remote);
687 entry->calling--;
688 if (!keep)
689 {
690 unregister_listener(this, entry, enumerator);
691 }
692 }
693 enumerator->destroy(enumerator);
694 this->mutex->unlock(this->mutex);
695 }
696
697 METHOD(bus_t, destroy, void,
698 private_bus_t *this)
699 {
700 this->thread_sa->destroy(this->thread_sa);
701 this->mutex->destroy(this->mutex);
702 this->listeners->destroy_function(this->listeners, (void*)entry_destroy);
703 free(this);
704 }
705
706 /*
707 * Described in header.
708 */
709 bus_t *bus_create()
710 {
711 private_bus_t *this;
712
713 INIT(this,
714 .public = {
715 .add_listener = _add_listener,
716 .remove_listener = _remove_listener,
717 .listen = _listen_,
718 .set_sa = _set_sa,
719 .get_sa = _get_sa,
720 .log = _log_,
721 .vlog = _vlog,
722 .alert = _alert,
723 .ike_state_change = _ike_state_change,
724 .child_state_change = _child_state_change,
725 .message = _message,
726 .ike_keys = _ike_keys,
727 .child_keys = _child_keys,
728 .ike_updown = _ike_updown,
729 .ike_rekey = _ike_rekey,
730 .child_updown = _child_updown,
731 .child_rekey = _child_rekey,
732 .authorize = _authorize,
733 .narrow = _narrow,
734 .destroy = _destroy,
735 },
736 .listeners = linked_list_create(),
737 .mutex = mutex_create(MUTEX_TYPE_RECURSIVE),
738 .thread_sa = thread_value_create(NULL),
739 );
740
741 return &this->public;
742 }
743