]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libcharon/plugins/vici/vici_dispatcher.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libcharon / plugins / vici / vici_dispatcher.c
1 /*
2 * Copyright (C) 2014 Martin Willi
3 *
4 * Copyright (C) secunet Security Networks AG
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 /*
18 * Copyright (C) 2014 Timo Teräs <timo.teras@iki.fi>
19 *
20 * Permission is hereby granted, free of charge, to any person obtaining a copy
21 * of this software and associated documentation files (the "Software"), to deal
22 * in the Software without restriction, including without limitation the rights
23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24 * copies of the Software, and to permit persons to whom the Software is
25 * furnished to do so, subject to the following conditions:
26 *
27 * The above copyright notice and this permission notice shall be included in
28 * all copies or substantial portions of the Software.
29 *
30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
36 * THE SOFTWARE.
37 */
38
39 #include "vici_dispatcher.h"
40 #include "vici_socket.h"
41
42 #include <bio/bio_reader.h>
43 #include <bio/bio_writer.h>
44 #include <threading/mutex.h>
45 #include <threading/condvar.h>
46 #include <threading/thread.h>
47 #include <collections/array.h>
48 #include <collections/hashtable.h>
49
50 typedef struct private_vici_dispatcher_t private_vici_dispatcher_t;
51
52 /**
53 * Private data of an vici_dispatcher_t object.
54 */
55 struct private_vici_dispatcher_t {
56
57 /**
58 * Public vici_dispatcher_t interface.
59 */
60 vici_dispatcher_t public;
61
62 /**
63 * Socket to send/receive messages
64 */
65 vici_socket_t *socket;
66
67 /**
68 * List of registered commands (char* => command_t*)
69 */
70 hashtable_t *cmds;
71
72 /**
73 * List of known events, and registered clients (char* => event_t*)
74 */
75 hashtable_t *events;
76
77 /**
78 * Mutex to lock hashtables
79 */
80 mutex_t *mutex;
81
82 /**
83 * Condvar to signal command termination
84 */
85 condvar_t *cond;
86 };
87
88 /**
89 * Registered command
90 */
91 typedef struct {
92 /** command name */
93 char *name;
94 /** callback for command */
95 vici_command_cb_t cb;
96 /** user data to pass to callback */
97 void *user;
98 /** command currently in use? */
99 u_int uses;
100 } command_t;
101
102 /**
103 * Registered event
104 */
105 typedef struct {
106 /** event name */
107 char *name;
108 /** registered clients, as u_int */
109 array_t *clients;
110 /** event currently in use? */
111 u_int uses;
112 } event_t;
113
114 /**
115 * Send a operation code, optionally with name and message
116 */
117 static void send_op(private_vici_dispatcher_t *this, u_int id,
118 vici_operation_t op, char *name, vici_message_t *message)
119 {
120 bio_writer_t *writer;
121 u_int len;
122
123 len = sizeof(uint8_t);
124 if (name)
125 {
126 len += sizeof(uint8_t) + strlen(name);
127 }
128 if (message)
129 {
130 len += message->get_encoding(message).len;
131 }
132 writer = bio_writer_create(len);
133 writer->write_uint8(writer, op);
134 if (name)
135 {
136 writer->write_data8(writer, chunk_from_str(name));
137 }
138 if (message)
139 {
140 writer->write_data(writer, message->get_encoding(message));
141 }
142 this->socket->send(this->socket, id, writer->extract_buf(writer));
143 writer->destroy(writer);
144 }
145
146 /**
147 * Register client for event
148 */
149 static void register_event(private_vici_dispatcher_t *this, char *name,
150 u_int id)
151 {
152 event_t *event;
153
154 this->mutex->lock(this->mutex);
155 while (TRUE)
156 {
157 event = this->events->get(this->events, name);
158 if (!event)
159 {
160 break;
161 }
162 if (!event->uses)
163 {
164 array_insert(event->clients, ARRAY_TAIL, &id);
165 break;
166 }
167 this->cond->wait(this->cond, this->mutex);
168 }
169 this->mutex->unlock(this->mutex);
170
171 if (event)
172 {
173 DBG2(DBG_CFG, "vici client %u registered for: %s", id, name);
174 send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
175 }
176 else
177 {
178 DBG1(DBG_CFG, "vici client %u invalid registration: %s", id, name);
179 send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
180 }
181 }
182
183 /**
184 * Unregister client for event
185 */
186 static void unregister_event(private_vici_dispatcher_t *this, char *name,
187 u_int id)
188 {
189 enumerator_t *enumerator;
190 event_t *event;
191 u_int *current;
192 bool found = FALSE;
193
194 this->mutex->lock(this->mutex);
195 while (TRUE)
196 {
197 event = this->events->get(this->events, name);
198 if (!event)
199 {
200 break;
201 }
202 if (!event->uses)
203 {
204 enumerator = array_create_enumerator(event->clients);
205 while (enumerator->enumerate(enumerator, &current))
206 {
207 if (*current == id)
208 {
209 array_remove_at(event->clients, enumerator);
210 found = TRUE;
211 break;
212 }
213 }
214 enumerator->destroy(enumerator);
215 break;
216 }
217 this->cond->wait(this->cond, this->mutex);
218 }
219 this->mutex->unlock(this->mutex);
220
221 DBG2(DBG_CFG, "vici client %u unregistered for: %s", id, name);
222
223 if (found)
224 {
225 send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
226 }
227 else
228 {
229 send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
230 }
231 }
232
233 /**
234 * Data to release on thread cancellation
235 */
236 typedef struct {
237 private_vici_dispatcher_t *this;
238 command_t *cmd;
239 vici_message_t *request;
240 } release_data_t;
241
242 /**
243 * Release command after execution/cancellation
244 */
245 CALLBACK(release_command, void,
246 release_data_t *release)
247 {
248 release->request->destroy(release->request);
249
250 release->this->mutex->lock(release->this->mutex);
251 if (--release->cmd->uses == 0)
252 {
253 release->this->cond->broadcast(release->this->cond);
254 }
255 release->this->mutex->unlock(release->this->mutex);
256
257 free(release);
258 }
259
260 /**
261 * Process a request message
262 */
263 void process_request(private_vici_dispatcher_t *this, char *name, u_int id,
264 chunk_t data)
265 {
266 vici_message_t *response = NULL;
267 release_data_t *release;
268 command_t *cmd;
269
270 this->mutex->lock(this->mutex);
271 cmd = this->cmds->get(this->cmds, name);
272 if (cmd)
273 {
274 cmd->uses++;
275 }
276 this->mutex->unlock(this->mutex);
277
278 if (cmd)
279 {
280 INIT(release,
281 .this = this,
282 .cmd = cmd,
283 );
284
285 DBG2(DBG_CFG, "vici client %u requests: %s", id, name);
286
287 thread_cleanup_push(release_command, release);
288
289 release->request = vici_message_create_from_data(data, FALSE);
290 response = release->cmd->cb(cmd->user, cmd->name, id, release->request);
291
292 thread_cleanup_pop(TRUE);
293
294 if (response)
295 {
296 send_op(this, id, VICI_CMD_RESPONSE, NULL, response);
297 response->destroy(response);
298 }
299 }
300 else
301 {
302 DBG1(DBG_CFG, "vici client %u invalid request: %s", id, name);
303 send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL);
304 }
305 }
306
307 CALLBACK(inbound, void,
308 private_vici_dispatcher_t *this, u_int id, chunk_t data)
309 {
310 bio_reader_t *reader;
311 chunk_t chunk;
312 uint8_t type;
313 char name[257];
314
315 reader = bio_reader_create(data);
316 if (reader->read_uint8(reader, &type))
317 {
318 switch (type)
319 {
320 case VICI_EVENT_REGISTER:
321 if (reader->read_data8(reader, &chunk) &&
322 vici_stringify(chunk, name, sizeof(name)))
323 {
324 register_event(this, name, id);
325 }
326 else
327 {
328 DBG1(DBG_CFG, "invalid vici register message");
329 }
330 break;
331 case VICI_EVENT_UNREGISTER:
332 if (reader->read_data8(reader, &chunk) &&
333 vici_stringify(chunk, name, sizeof(name)))
334 {
335 unregister_event(this, name, id);
336 }
337 else
338 {
339 DBG1(DBG_CFG, "invalid vici unregister message");
340 }
341 break;
342 case VICI_CMD_REQUEST:
343 if (reader->read_data8(reader, &chunk) &&
344 vici_stringify(chunk, name, sizeof(name)))
345 {
346 thread_cleanup_push((void*)reader->destroy, reader);
347 process_request(this, name, id, reader->peek(reader));
348 thread_cleanup_pop(FALSE);
349 }
350 else
351 {
352 DBG1(DBG_CFG, "invalid vici request message");
353 }
354 break;
355 case VICI_CMD_RESPONSE:
356 case VICI_EVENT_CONFIRM:
357 case VICI_EVENT_UNKNOWN:
358 case VICI_EVENT:
359 default:
360 DBG1(DBG_CFG, "unsupported vici operation: %u", type);
361 break;
362 }
363 }
364 else
365 {
366 DBG1(DBG_CFG, "invalid vici message");
367 }
368 reader->destroy(reader);
369 }
370
371 CALLBACK(connect_, void,
372 private_vici_dispatcher_t *this, u_int id)
373 {
374 DBG2(DBG_CFG, "vici client %u connected", id);
375 }
376
377 CALLBACK(disconnect, void,
378 private_vici_dispatcher_t *this, u_int id)
379 {
380 enumerator_t *events, *ids;
381 event_t *event;
382 u_int *current;
383
384 /* deregister client from all events */
385 this->mutex->lock(this->mutex);
386 events = this->events->create_enumerator(this->events);
387 while (events->enumerate(events, NULL, &event))
388 {
389 while (event->uses)
390 {
391 this->cond->wait(this->cond, this->mutex);
392 }
393 ids = array_create_enumerator(event->clients);
394 while (ids->enumerate(ids, &current))
395 {
396 if (id == *current)
397 {
398 array_remove_at(event->clients, ids);
399 }
400 }
401 ids->destroy(ids);
402 }
403 events->destroy(events);
404 this->mutex->unlock(this->mutex);
405
406 DBG2(DBG_CFG, "vici client %u disconnected", id);
407 }
408
409 METHOD(vici_dispatcher_t, manage_command, void,
410 private_vici_dispatcher_t *this, char *name,
411 vici_command_cb_t cb, void *user)
412 {
413 command_t *cmd;
414
415 this->mutex->lock(this->mutex);
416 if (cb)
417 {
418 INIT(cmd,
419 .name = strdup(name),
420 .cb = cb,
421 .user = user,
422 );
423 cmd = this->cmds->put(this->cmds, cmd->name, cmd);
424 }
425 else
426 {
427 cmd = this->cmds->remove(this->cmds, name);
428 }
429 if (cmd)
430 {
431 while (cmd->uses)
432 {
433 this->cond->wait(this->cond, this->mutex);
434 }
435 free(cmd->name);
436 free(cmd);
437 }
438 this->mutex->unlock(this->mutex);
439 }
440
441 METHOD(vici_dispatcher_t, manage_event, void,
442 private_vici_dispatcher_t *this, char *name, bool reg)
443 {
444 event_t *event;
445
446 this->mutex->lock(this->mutex);
447 if (reg)
448 {
449 INIT(event,
450 .name = strdup(name),
451 .clients = array_create(sizeof(u_int), 0),
452 );
453 event = this->events->put(this->events, event->name, event);
454 }
455 else
456 {
457 event = this->events->remove(this->events, name);
458 }
459 if (event)
460 {
461 while (event->uses)
462 {
463 this->cond->wait(this->cond, this->mutex);
464 }
465 array_destroy(event->clients);
466 free(event->name);
467 free(event);
468 }
469 this->mutex->unlock(this->mutex);
470 }
471
472 METHOD(vici_dispatcher_t, has_event_listeners, bool,
473 private_vici_dispatcher_t *this, char *name)
474 {
475 event_t *event;
476 bool retval = FALSE;
477
478 this->mutex->lock(this->mutex);
479 event = this->events->get(this->events, name);
480 if (event)
481 {
482 /* the entry might be getting destroyed, but returning
483 * false positive is not a problem as a later raise_event
484 * will check things again. */
485 retval = array_count(event->clients);
486 }
487 this->mutex->unlock(this->mutex);
488
489 return retval;
490 }
491
492 METHOD(vici_dispatcher_t, raise_event, void,
493 private_vici_dispatcher_t *this, char *name, u_int id,
494 vici_message_t *message)
495 {
496 enumerator_t *enumerator;
497 event_t *event;
498 u_int *current;
499
500 this->mutex->lock(this->mutex);
501 event = this->events->get(this->events, name);
502 if (event)
503 {
504 event->uses++;
505 this->mutex->unlock(this->mutex);
506
507 enumerator = array_create_enumerator(event->clients);
508 while (enumerator->enumerate(enumerator, &current))
509 {
510 if (id == 0 || id == *current)
511 {
512 send_op(this, *current, VICI_EVENT, name, message);
513 }
514 }
515 enumerator->destroy(enumerator);
516
517 this->mutex->lock(this->mutex);
518 if (--event->uses == 0)
519 {
520 this->cond->broadcast(this->cond);
521 }
522 }
523 this->mutex->unlock(this->mutex);
524
525 message->destroy(message);
526 }
527
528 METHOD(vici_dispatcher_t, destroy, void,
529 private_vici_dispatcher_t *this)
530 {
531 DESTROY_IF(this->socket);
532 this->mutex->destroy(this->mutex);
533 this->cond->destroy(this->cond);
534 this->cmds->destroy(this->cmds);
535 this->events->destroy(this->events);
536 free(this);
537 }
538
539 /**
540 * See header
541 */
542 vici_dispatcher_t *vici_dispatcher_create(char *uri)
543 {
544 private_vici_dispatcher_t *this;
545
546 INIT(this,
547 .public = {
548 .manage_command = _manage_command,
549 .manage_event = _manage_event,
550 .has_event_listeners = _has_event_listeners,
551 .raise_event = _raise_event,
552 .destroy = _destroy,
553 },
554 .cmds = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
555 .events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
556 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
557 .cond = condvar_create(CONDVAR_TYPE_DEFAULT),
558 );
559
560 this->socket = vici_socket_create(uri, inbound, connect_, disconnect, this);
561 if (!this->socket)
562 {
563 destroy(this);
564 return NULL;
565 }
566
567 return &this->public;
568 }