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