Fixed job handling in controller_t
[strongswan.git] / src / libcharon / control / controller.c
1 /*
2 * Copyright (C) 2011-2012 Tobias Brunner
3 * Copyright (C) 2007-2011 Martin Willi
4 * Copyright (C) 2011 revosec AG
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "controller.h"
19
20 #include <sys/types.h>
21 #include <dirent.h>
22 #include <sys/stat.h>
23 #include <dlfcn.h>
24
25 #include <daemon.h>
26 #include <library.h>
27 #include <threading/thread.h>
28 #include <threading/semaphore.h>
29
30 typedef struct private_controller_t private_controller_t;
31 typedef struct interface_listener_t interface_listener_t;
32 typedef struct interface_logger_t interface_logger_t;
33
34 /**
35 * Private data of an stroke_t object.
36 */
37 struct private_controller_t {
38
39 /**
40 * Public part of stroke_t object.
41 */
42 controller_t public;
43 };
44
45 /**
46 * helper struct for the logger interface
47 */
48 struct interface_logger_t {
49 /**
50 * public logger interface
51 */
52 logger_t public;
53
54 /**
55 * reference to the listener
56 */
57 interface_listener_t *listener;
58
59 /**
60 * interface callback (listener gets redirected to here)
61 */
62 controller_cb_t callback;
63
64 /**
65 * user parameter to pass to callback
66 */
67 void *param;
68 };
69
70 /**
71 * helper struct to map listener callbacks to interface callbacks
72 */
73 struct interface_listener_t {
74
75 /**
76 * public bus listener interface
77 */
78 listener_t public;
79
80 /**
81 * logger interface
82 */
83 interface_logger_t logger;
84
85 /**
86 * status of the operation, return to method callers
87 */
88 status_t status;
89
90 /**
91 * child configuration, used for initiate
92 */
93 child_cfg_t *child_cfg;
94
95 /**
96 * peer configuration, used for initiate
97 */
98 peer_cfg_t *peer_cfg;
99
100 /**
101 * IKE_SA to handle
102 */
103 ike_sa_t *ike_sa;
104
105 /**
106 * unique ID, used for various methods
107 */
108 u_int32_t id;
109
110 /**
111 * semaphore to implement wait_for_listener()
112 */
113 semaphore_t *done;
114 };
115
116
117 typedef struct interface_job_t interface_job_t;
118
119 /**
120 * job for asynchronous listen operations
121 */
122 struct interface_job_t {
123
124 /**
125 * job interface
126 */
127 job_t public;
128
129 /**
130 * associated listener
131 */
132 interface_listener_t listener;
133
134 /**
135 * the job is reference counted as the thread executing a job as well as
136 * the thread waiting in wait_for_listener() require it but either of them
137 * could be done first
138 */
139 refcount_t refcount;
140 };
141
142 /**
143 * This function wakes a thread that is waiting in wait_for_listener(),
144 * either from a listener or from a job.
145 */
146 static inline bool listener_done(interface_listener_t *listener)
147 {
148 if (listener->done)
149 {
150 listener->done->post(listener->done);
151 }
152 return FALSE;
153 }
154
155 /**
156 * thread_cleanup_t handler to unregister a listener.
157 */
158 static void listener_unregister(interface_listener_t *listener)
159 {
160 charon->bus->remove_listener(charon->bus, &listener->public);
161 charon->bus->remove_logger(charon->bus, &listener->logger.public);
162 }
163
164 /**
165 * Registers the listener, executes the job and then waits synchronously until
166 * the listener is done or the timeout occurred.
167 *
168 * @note Use 'return listener_done(listener)' to properly unregister a listener
169 *
170 * @param listener listener to register
171 * @param job job to execute asynchronously when registered, or NULL
172 * @param timeout max timeout in ms to listen for events, 0 to disable
173 * @return TRUE if timed out
174 */
175 static bool wait_for_listener(interface_job_t *job, u_int timeout)
176 {
177 interface_listener_t *listener = &job->listener;
178 bool old, timed_out = FALSE;
179
180 /* avoid that the job is destroyed too early */
181 ref_get(&job->refcount);
182
183 listener->done = semaphore_create(0);
184
185 charon->bus->add_logger(charon->bus, &listener->logger.public);
186 charon->bus->add_listener(charon->bus, &listener->public);
187 lib->processor->queue_job(lib->processor, &job->public);
188
189 thread_cleanup_push((thread_cleanup_t)listener_unregister, listener);
190 old = thread_cancelability(TRUE);
191 if (timeout)
192 {
193 timed_out = listener->done->timed_wait(listener->done, timeout);
194 }
195 else
196 {
197 listener->done->wait(listener->done);
198 }
199 thread_cancelability(old);
200 thread_cleanup_pop(TRUE);
201 return timed_out;
202 }
203
204 METHOD(logger_t, listener_log, void,
205 interface_logger_t *this, debug_t group, level_t level, int thread,
206 ike_sa_t *ike_sa, char* message)
207 {
208 if (this->listener->ike_sa == ike_sa)
209 {
210 if (!this->callback(this->param, group, level, ike_sa, message))
211 {
212 this->listener->status = NEED_MORE;
213 listener_done(this->listener);
214 }
215 }
216 }
217
218 METHOD(logger_t, listener_get_level, level_t,
219 interface_logger_t *this, debug_t group)
220 {
221 /* in order to allow callback listeners to decide what they want to log
222 * we request any log message, but only if we actually want logging */
223 return this->callback == controller_cb_empty ? LEVEL_SILENT : LEVEL_PRIVATE;
224 }
225
226 METHOD(job_t, get_priority_medium, job_priority_t,
227 job_t *this)
228 {
229 return JOB_PRIO_MEDIUM;
230 }
231
232 METHOD(listener_t, ike_state_change, bool,
233 interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
234 {
235 if (this->ike_sa == ike_sa)
236 {
237 switch (state)
238 {
239 #ifdef ME
240 case IKE_ESTABLISHED:
241 { /* mediation connections are complete without CHILD_SA */
242 peer_cfg_t *peer_cfg = ike_sa->get_peer_cfg(ike_sa);
243
244 if (peer_cfg->is_mediation(peer_cfg))
245 {
246 this->status = SUCCESS;
247 return listener_done(this);
248 }
249 break;
250 }
251 #endif /* ME */
252 case IKE_DESTROYING:
253 if (ike_sa->get_state(ike_sa) == IKE_DELETING)
254 { /* proper termination */
255 this->status = SUCCESS;
256 }
257 return listener_done(this);
258 default:
259 break;
260 }
261 }
262 return TRUE;
263 }
264
265 METHOD(listener_t, child_state_change, bool,
266 interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
267 child_sa_state_t state)
268 {
269 if (this->ike_sa == ike_sa)
270 {
271 switch (state)
272 {
273 case CHILD_INSTALLED:
274 this->status = SUCCESS;
275 return listener_done(this);
276 case CHILD_DESTROYING:
277 switch (child_sa->get_state(child_sa))
278 {
279 case CHILD_DELETING:
280 /* proper delete */
281 this->status = SUCCESS;
282 break;
283 default:
284 break;
285 }
286 return listener_done(this);
287 default:
288 break;
289 }
290 }
291 return TRUE;
292 }
293
294 METHOD(job_t, destroy_job, void,
295 interface_job_t *this)
296 {
297 if (ref_put(&this->refcount))
298 {
299 DESTROY_IF(this->listener.done);
300 free(this);
301 }
302 }
303
304 METHOD(controller_t, create_ike_sa_enumerator, enumerator_t*,
305 private_controller_t *this, bool wait)
306 {
307 return charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager,
308 wait);
309 }
310
311 METHOD(job_t, initiate_execute, job_requeue_t,
312 interface_job_t *job)
313 {
314 ike_sa_t *ike_sa;
315 interface_listener_t *listener = &job->listener;
316 peer_cfg_t *peer_cfg = listener->peer_cfg;
317
318 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
319 peer_cfg);
320 if (!ike_sa)
321 {
322 listener->child_cfg->destroy(listener->child_cfg);
323 peer_cfg->destroy(peer_cfg);
324 listener->status = FAILED;
325 /* release listener */
326 listener_done(listener);
327 return JOB_REQUEUE_NONE;
328 }
329 listener->ike_sa = ike_sa;
330
331 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
332 {
333 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
334 }
335 peer_cfg->destroy(peer_cfg);
336
337 if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
338 {
339 listener->status = SUCCESS;
340 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
341 }
342 else
343 {
344 listener->status = FAILED;
345 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
346 ike_sa);
347 }
348 return JOB_REQUEUE_NONE;
349 }
350
351 METHOD(controller_t, initiate, status_t,
352 private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
353 controller_cb_t callback, void *param, u_int timeout)
354 {
355 interface_job_t *job;
356 status_t status;
357
358 INIT(job,
359 .listener = {
360 .public = {
361 .ike_state_change = _ike_state_change,
362 .child_state_change = _child_state_change,
363 },
364 .logger = {
365 .public = {
366 .log = _listener_log,
367 .get_level = _listener_get_level,
368 },
369 .callback = callback,
370 .param = param,
371 },
372 .status = FAILED,
373 .child_cfg = child_cfg,
374 .peer_cfg = peer_cfg,
375 },
376 .public = {
377 .execute = _initiate_execute,
378 .get_priority = _get_priority_medium,
379 .destroy = _destroy_job,
380 },
381 .refcount = 1,
382 );
383 job->listener.logger.listener = &job->listener;
384
385 if (callback == NULL)
386 {
387 initiate_execute(job);
388 }
389 else
390 {
391 if (wait_for_listener(job, timeout))
392 {
393 job->listener.status = OUT_OF_RES;
394 }
395 }
396 status = job->listener.status;
397 destroy_job(job);
398 return status;
399 }
400
401 METHOD(job_t, terminate_ike_execute, job_requeue_t,
402 interface_job_t *job)
403 {
404 interface_listener_t *listener = &job->listener;
405 u_int32_t unique_id = listener->id;
406 ike_sa_t *ike_sa;
407
408 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
409 unique_id, FALSE);
410 if (!ike_sa)
411 {
412 DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
413 listener->status = NOT_FOUND;
414 /* release listener */
415 listener_done(listener);
416 return JOB_REQUEUE_NONE;
417 }
418 listener->ike_sa = ike_sa;
419
420 if (ike_sa->delete(ike_sa) != DESTROY_ME)
421 { /* delete failed */
422 listener->status = FAILED;
423 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
424 }
425 else
426 {
427 listener->status = SUCCESS;
428 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
429 ike_sa);
430 }
431 return JOB_REQUEUE_NONE;
432 }
433
434 METHOD(controller_t, terminate_ike, status_t,
435 controller_t *this, u_int32_t unique_id,
436 controller_cb_t callback, void *param, u_int timeout)
437 {
438 interface_job_t *job;
439 status_t status;
440
441 INIT(job,
442 .listener = {
443 .public = {
444 .ike_state_change = _ike_state_change,
445 .child_state_change = _child_state_change,
446 },
447 .logger = {
448 .public = {
449 .log = _listener_log,
450 .get_level = _listener_get_level,
451 },
452 .callback = callback,
453 .param = param,
454 },
455 .status = FAILED,
456 .id = unique_id,
457 },
458 .public = {
459 .execute = _terminate_ike_execute,
460 .get_priority = _get_priority_medium,
461 .destroy = _destroy_job,
462 },
463 .refcount = 1,
464 );
465 job->listener.logger.listener = &job->listener;
466
467 if (callback == NULL)
468 {
469 terminate_ike_execute(job);
470 }
471 else
472 {
473 if (wait_for_listener(job, timeout))
474 {
475 job->listener.status = OUT_OF_RES;
476 }
477 }
478 status = job->listener.status;
479 destroy_job(job);
480 return status;
481 }
482
483 METHOD(job_t, terminate_child_execute, job_requeue_t,
484 interface_job_t *job)
485 {
486 interface_listener_t *listener = &job->listener;
487 u_int32_t reqid = listener->id;
488 enumerator_t *enumerator;
489 child_sa_t *child_sa;
490 ike_sa_t *ike_sa;
491
492 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
493 reqid, TRUE);
494 if (!ike_sa)
495 {
496 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
497 reqid);
498 listener->status = NOT_FOUND;
499 /* release listener */
500 listener_done(listener);
501 return JOB_REQUEUE_NONE;
502 }
503 job->listener.ike_sa = ike_sa;
504
505 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
506 while (enumerator->enumerate(enumerator, (void**)&child_sa))
507 {
508 if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
509 child_sa->get_reqid(child_sa) == reqid)
510 {
511 break;
512 }
513 child_sa = NULL;
514 }
515 enumerator->destroy(enumerator);
516
517 if (!child_sa)
518 {
519 DBG1(DBG_IKE, "unable to terminate, established "
520 "CHILD_SA with ID %d not found", reqid);
521 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
522 listener->status = NOT_FOUND;
523 /* release listener */
524 listener_done(listener);
525 return JOB_REQUEUE_NONE;
526 }
527
528 if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
529 child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
530 {
531 listener->status = SUCCESS;
532 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
533 }
534 else
535 {
536 listener->status = FAILED;
537 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
538 ike_sa);
539 }
540 return JOB_REQUEUE_NONE;
541 }
542
543 METHOD(controller_t, terminate_child, status_t,
544 controller_t *this, u_int32_t reqid,
545 controller_cb_t callback, void *param, u_int timeout)
546 {
547 interface_job_t *job;
548 status_t status;
549
550 INIT(job,
551 .listener = {
552 .public = {
553 .ike_state_change = _ike_state_change,
554 .child_state_change = _child_state_change,
555 },
556 .logger = {
557 .public = {
558 .log = _listener_log,
559 .get_level = _listener_get_level,
560 },
561 .callback = callback,
562 .param = param,
563 },
564 .status = FAILED,
565 .id = reqid,
566 },
567 .public = {
568 .execute = _terminate_child_execute,
569 .get_priority = _get_priority_medium,
570 .destroy = _destroy_job,
571 },
572 .refcount = 1,
573 );
574 job->listener.logger.listener = &job->listener;
575
576 if (callback == NULL)
577 {
578 terminate_child_execute(job);
579 }
580 else
581 {
582 if (wait_for_listener(job, timeout))
583 {
584 job->listener.status = OUT_OF_RES;
585 }
586 }
587 status = job->listener.status;
588 destroy_job(job);
589 return status;
590 }
591
592 /**
593 * See header
594 */
595 bool controller_cb_empty(void *param, debug_t group, level_t level,
596 ike_sa_t *ike_sa, char *message)
597 {
598 return TRUE;
599 }
600
601 METHOD(controller_t, destroy, void,
602 private_controller_t *this)
603 {
604 free(this);
605 }
606
607 /*
608 * Described in header-file
609 */
610 controller_t *controller_create(void)
611 {
612 private_controller_t *this;
613
614 INIT(this,
615 .public = {
616 .create_ike_sa_enumerator = _create_ike_sa_enumerator,
617 .initiate = _initiate,
618 .terminate_ike = _terminate_ike,
619 .terminate_child = _terminate_child,
620 .destroy = _destroy,
621 },
622 );
623
624 return &this->public;
625 }
626