Give processor_t more control over the lifecycle of a job
[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 * CHILD_SA to handle
107 */
108 child_sa_t *child_sa;
109
110 /**
111 * unique ID, used for various methods
112 */
113 u_int32_t id;
114
115 /**
116 * semaphore to implement wait_for_listener()
117 */
118 semaphore_t *done;
119 };
120
121
122 typedef struct interface_job_t interface_job_t;
123
124 /**
125 * job for asynchronous listen operations
126 */
127 struct interface_job_t {
128
129 /**
130 * job interface
131 */
132 job_t public;
133
134 /**
135 * associated listener
136 */
137 interface_listener_t listener;
138 };
139
140 /**
141 * This function properly unregisters a listener that is used
142 * with wait_for_listener()
143 */
144 static inline bool listener_done(interface_listener_t *listener)
145 {
146 if (listener->done)
147 {
148 listener->done->post(listener->done);
149 }
150 return FALSE;
151 }
152
153 /**
154 * thread_cleanup_t handler to unregister and cleanup a listener
155 */
156 static void listener_cleanup(interface_listener_t *listener)
157 {
158 charon->bus->remove_listener(charon->bus, &listener->public);
159 charon->bus->remove_logger(charon->bus, &listener->logger.public);
160 listener->done->destroy(listener->done);
161 }
162
163 /**
164 * Registers the listener, executes the job and then waits synchronously until
165 * the listener is done or the timeout occured.
166 *
167 * @note Use 'return listener_done(listener)' to properly unregister a listener
168 *
169 * @param listener listener to register
170 * @param job job to execute asynchronously when registered, or NULL
171 * @param timeout max timeout in ms to listen for events, 0 to disable
172 * @return TRUE if timed out
173 */
174 static bool wait_for_listener(interface_listener_t *listener, job_t *job,
175 u_int timeout)
176 {
177 bool old, timed_out = FALSE;
178
179 listener->done = semaphore_create(0);
180
181 charon->bus->add_logger(charon->bus, &listener->logger.public);
182 charon->bus->add_listener(charon->bus, &listener->public);
183 lib->processor->queue_job(lib->processor, job);
184
185 thread_cleanup_push((thread_cleanup_t)listener_cleanup, listener);
186 old = thread_cancelability(TRUE);
187 if (timeout)
188 {
189 timed_out = listener->done->timed_wait(listener->done, timeout);
190 }
191 else
192 {
193 listener->done->wait(listener->done);
194 }
195 thread_cancelability(old);
196 thread_cleanup_pop(TRUE);
197 return timed_out;
198 }
199
200 METHOD(logger_t, listener_log, void,
201 interface_logger_t *this, debug_t group, level_t level, int thread,
202 ike_sa_t *ike_sa, char* message)
203 {
204 if (this->listener->ike_sa == ike_sa)
205 {
206 if (!this->callback(this->param, group, level, ike_sa, message))
207 {
208 this->listener->status = NEED_MORE;
209 listener_done(this->listener);
210 }
211 }
212 }
213
214 METHOD(logger_t, listener_get_level, level_t,
215 interface_logger_t *this, debug_t group)
216 {
217 /* in order to allow callback listeners to decide what they want to log
218 * we request any log message, but only if we actually want logging */
219 return this->callback == controller_cb_empty ? LEVEL_SILENT : LEVEL_PRIVATE;
220 }
221
222 METHOD(job_t, get_priority_medium, job_priority_t,
223 job_t *this)
224 {
225 return JOB_PRIO_MEDIUM;
226 }
227
228 METHOD(listener_t, ike_state_change, bool,
229 interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
230 {
231 if (this->ike_sa == ike_sa)
232 {
233 switch (state)
234 {
235 #ifdef ME
236 case IKE_ESTABLISHED:
237 { /* mediation connections are complete without CHILD_SA */
238 peer_cfg_t *peer_cfg = ike_sa->get_peer_cfg(ike_sa);
239
240 if (peer_cfg->is_mediation(peer_cfg))
241 {
242 this->status = SUCCESS;
243 return listener_done(this);
244 }
245 break;
246 }
247 #endif /* ME */
248 case IKE_DESTROYING:
249 if (ike_sa->get_state(ike_sa) == IKE_DELETING)
250 { /* proper termination */
251 this->status = SUCCESS;
252 }
253 return listener_done(this);
254 default:
255 break;
256 }
257 }
258 return TRUE;
259 }
260
261 METHOD(listener_t, child_state_change, bool,
262 interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
263 child_sa_state_t state)
264 {
265 if (this->ike_sa == ike_sa)
266 {
267 switch (state)
268 {
269 case CHILD_INSTALLED:
270 this->status = SUCCESS;
271 return listener_done(this);
272 case CHILD_DESTROYING:
273 switch (child_sa->get_state(child_sa))
274 {
275 case CHILD_DELETING:
276 /* proper delete */
277 this->status = SUCCESS;
278 break;
279 default:
280 break;
281 }
282 return listener_done(this);
283 default:
284 break;
285 }
286 }
287 return TRUE;
288 }
289
290 METHOD(job_t, recheckin, void,
291 interface_job_t *job)
292 {
293 if (job->public.status == JOB_STATUS_QUEUED &&
294 job->listener.ike_sa)
295 {
296 charon->ike_sa_manager->checkin(charon->ike_sa_manager,
297 job->listener.ike_sa);
298 }
299 }
300
301 METHOD(controller_t, create_ike_sa_enumerator, enumerator_t*,
302 private_controller_t *this, bool wait)
303 {
304 return charon->ike_sa_manager->create_enumerator(charon->ike_sa_manager,
305 wait);
306 }
307
308 METHOD(job_t, initiate_execute, job_requeue_t,
309 interface_job_t *job)
310 {
311 ike_sa_t *ike_sa;
312 interface_listener_t *listener = &job->listener;
313 peer_cfg_t *peer_cfg = listener->peer_cfg;
314
315 ike_sa = charon->ike_sa_manager->checkout_by_config(charon->ike_sa_manager,
316 peer_cfg);
317 if (!ike_sa)
318 {
319 listener->child_cfg->destroy(listener->child_cfg);
320 peer_cfg->destroy(peer_cfg);
321 /* trigger down event to release listener */
322 listener->ike_sa = charon->ike_sa_manager->checkout_new(
323 charon->ike_sa_manager, IKE_ANY, TRUE);
324 DESTROY_IF(listener->ike_sa);
325 listener->status = FAILED;
326 return JOB_REQUEUE_NONE;
327 }
328 listener->ike_sa = ike_sa;
329
330 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
331 {
332 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
333 }
334 peer_cfg->destroy(peer_cfg);
335
336 if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
337 {
338 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
339 listener->status = SUCCESS;
340 }
341 else
342 {
343 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
344 ike_sa);
345 listener->status = FAILED;
346 }
347 return JOB_REQUEUE_NONE;
348 }
349
350 METHOD(controller_t, initiate, status_t,
351 private_controller_t *this, peer_cfg_t *peer_cfg, child_cfg_t *child_cfg,
352 controller_cb_t callback, void *param, u_int timeout)
353 {
354 interface_job_t job = {
355 .listener = {
356 .public = {
357 .ike_state_change = _ike_state_change,
358 .child_state_change = _child_state_change,
359 },
360 .logger = {
361 .public = {
362 .log = _listener_log,
363 .get_level = _listener_get_level,
364 },
365 .callback = callback,
366 .param = param,
367 },
368 .status = FAILED,
369 .child_cfg = child_cfg,
370 .peer_cfg = peer_cfg,
371 },
372 .public = {
373 .execute = _initiate_execute,
374 .get_priority = _get_priority_medium,
375 .destroy = _recheckin,
376 },
377 };
378 job.listener.logger.listener = &job.listener;
379
380 if (callback == NULL)
381 {
382 initiate_execute(&job);
383 }
384 else
385 {
386 if (wait_for_listener(&job.listener, &job.public, timeout))
387 {
388 job.listener.status = OUT_OF_RES;
389 }
390 }
391 return job.listener.status;
392 }
393
394 METHOD(job_t, terminate_ike_execute, job_requeue_t,
395 interface_job_t *job)
396 {
397 interface_listener_t *listener = &job->listener;
398 ike_sa_t *ike_sa = listener->ike_sa;
399
400 charon->bus->set_sa(charon->bus, ike_sa);
401
402 if (ike_sa->delete(ike_sa) != DESTROY_ME)
403 {
404 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
405 /* delete failed */
406 listener->status = FAILED;
407 }
408 else
409 {
410 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
411 ike_sa);
412 listener->status = SUCCESS;
413 }
414 return JOB_REQUEUE_NONE;
415 }
416
417 METHOD(controller_t, terminate_ike, status_t,
418 controller_t *this, u_int32_t unique_id,
419 controller_cb_t callback, void *param, u_int timeout)
420 {
421 ike_sa_t *ike_sa;
422 interface_job_t job = {
423 .listener = {
424 .public = {
425 .ike_state_change = _ike_state_change,
426 .child_state_change = _child_state_change,
427 },
428 .logger = {
429 .public = {
430 .log = _listener_log,
431 .get_level = _listener_get_level,
432 },
433 .callback = callback,
434 .param = param,
435 },
436 .status = FAILED,
437 .id = unique_id,
438 },
439 .public = {
440 .execute = _terminate_ike_execute,
441 .get_priority = _get_priority_medium,
442 .destroy = _recheckin,
443 },
444 };
445 job.listener.logger.listener = &job.listener;
446
447 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
448 unique_id, FALSE);
449 if (ike_sa == NULL)
450 {
451 DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
452 return NOT_FOUND;
453 }
454 job.listener.ike_sa = ike_sa;
455
456 if (callback == NULL)
457 {
458 terminate_ike_execute(&job);
459 }
460 else
461 {
462 if (wait_for_listener(&job.listener, &job.public, timeout))
463 {
464 job.listener.status = OUT_OF_RES;
465 }
466 /* checkin of the ike_sa happened in the thread that executed the job */
467 charon->bus->set_sa(charon->bus, NULL);
468 }
469 return job.listener.status;
470 }
471
472 METHOD(job_t, terminate_child_execute, job_requeue_t,
473 interface_job_t *job)
474 {
475 interface_listener_t *listener = &job->listener;
476 ike_sa_t *ike_sa = listener->ike_sa;
477 child_sa_t *child_sa = listener->child_sa;
478
479 charon->bus->set_sa(charon->bus, ike_sa);
480 if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
481 child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
482 {
483 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
484 listener->status = SUCCESS;
485 }
486 else
487 {
488 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
489 ike_sa);
490 listener->status = FAILED;
491 }
492 return JOB_REQUEUE_NONE;
493 }
494
495 METHOD(controller_t, terminate_child, status_t,
496 controller_t *this, u_int32_t reqid,
497 controller_cb_t callback, void *param, u_int timeout)
498 {
499 ike_sa_t *ike_sa;
500 child_sa_t *child_sa;
501 enumerator_t *enumerator;
502 interface_job_t job = {
503 .listener = {
504 .public = {
505 .ike_state_change = _ike_state_change,
506 .child_state_change = _child_state_change,
507 },
508 .logger = {
509 .public = {
510 .log = _listener_log,
511 .get_level = _listener_get_level,
512 },
513 .callback = callback,
514 .param = param,
515 },
516 .status = FAILED,
517 .id = reqid,
518 },
519 .public = {
520 .execute = _terminate_child_execute,
521 .get_priority = _get_priority_medium,
522 .destroy = _recheckin,
523 },
524 };
525 job.listener.logger.listener = &job.listener;
526
527 ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
528 reqid, TRUE);
529 if (ike_sa == NULL)
530 {
531 DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
532 reqid);
533 return NOT_FOUND;
534 }
535 job.listener.ike_sa = ike_sa;
536
537 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
538 while (enumerator->enumerate(enumerator, (void**)&child_sa))
539 {
540 if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
541 child_sa->get_reqid(child_sa) == reqid)
542 {
543 break;
544 }
545 child_sa = NULL;
546 }
547 enumerator->destroy(enumerator);
548
549 if (child_sa == NULL)
550 {
551 DBG1(DBG_IKE, "unable to terminate, established "
552 "CHILD_SA with ID %d not found", reqid);
553 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
554 return NOT_FOUND;
555 }
556 job.listener.child_sa = child_sa;
557
558 if (callback == NULL)
559 {
560 terminate_child_execute(&job);
561 }
562 else
563 {
564 if (wait_for_listener(&job.listener, &job.public, timeout))
565 {
566 job.listener.status = OUT_OF_RES;
567 }
568 /* checkin of the ike_sa happened in the thread that executed the job */
569 charon->bus->set_sa(charon->bus, NULL);
570 }
571 return job.listener.status;
572 }
573
574 /**
575 * See header
576 */
577 bool controller_cb_empty(void *param, debug_t group, level_t level,
578 ike_sa_t *ike_sa, char *message)
579 {
580 return TRUE;
581 }
582
583 METHOD(controller_t, destroy, void,
584 private_controller_t *this)
585 {
586 free(this);
587 }
588
589 /*
590 * Described in header-file
591 */
592 controller_t *controller_create(void)
593 {
594 private_controller_t *this;
595
596 INIT(this,
597 .public = {
598 .create_ike_sa_enumerator = _create_ike_sa_enumerator,
599 .initiate = _initiate,
600 .terminate_ike = _terminate_ike,
601 .terminate_child = _terminate_child,
602 .destroy = _destroy,
603 },
604 );
605
606 return &this->public;
607 }
608