vici: Ensure we have no active users before mangling event client registrations
[strongswan.git] / src / libcharon / plugins / vici / vici_dispatcher.c
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
16 #include "vici_dispatcher.h"
17 #include "vici_socket.h"
18
19 #include <bio/bio_reader.h>
20 #include <bio/bio_writer.h>
21 #include <threading/mutex.h>
22 #include <threading/condvar.h>
23 #include <threading/thread.h>
24 #include <collections/array.h>
25 #include <collections/hashtable.h>
26
27 typedef struct private_vici_dispatcher_t private_vici_dispatcher_t;
28
29 /**
30 * Private data of an vici_dispatcher_t object.
31 */
32 struct private_vici_dispatcher_t {
33
34 /**
35 * Public vici_dispatcher_t interface.
36 */
37 vici_dispatcher_t public;
38
39 /**
40 * Socket to send/receive messages
41 */
42 vici_socket_t *socket;
43
44 /**
45 * List of registered commands (char* => command_t*)
46 */
47 hashtable_t *cmds;
48
49 /**
50 * List of known events, and registered clients (char* => event_t*)
51 */
52 hashtable_t *events;
53
54 /**
55 * Mutex to lock hashtables
56 */
57 mutex_t *mutex;
58
59 /**
60 * Condvar to signal command termination
61 */
62 condvar_t *cond;
63 };
64
65 /**
66 * Registered command
67 */
68 typedef struct {
69 /** command name */
70 char *name;
71 /** callback for command */
72 vici_command_cb_t cb;
73 /** user data to pass to callback */
74 void *user;
75 /** command currently in use? */
76 u_int uses;
77 } command_t;
78
79 /**
80 * Registered event
81 */
82 typedef struct {
83 /** event name */
84 char *name;
85 /** registered clients, as u_int */
86 array_t *clients;
87 /** event currently in use? */
88 u_int uses;
89 } event_t;
90
91 /**
92 * Send a operation code, optionally with name and message
93 */
94 static void send_op(private_vici_dispatcher_t *this, u_int id,
95 vici_operation_t op, char *name, vici_message_t *message)
96 {
97 bio_writer_t *writer;
98 u_int len;
99
100 len = sizeof(u_int8_t);
101 if (name)
102 {
103 len += sizeof(u_int8_t) + strlen(name);
104 }
105 if (message)
106 {
107 len += message->get_encoding(message).len;
108 }
109 writer = bio_writer_create(len);
110 writer->write_uint8(writer, op);
111 if (name)
112 {
113 writer->write_data8(writer, chunk_from_str(name));
114 }
115 if (message)
116 {
117 writer->write_data(writer, message->get_encoding(message));
118 }
119 this->socket->send(this->socket, id, writer->extract_buf(writer));
120 writer->destroy(writer);
121 }
122
123 /**
124 * Register client for event
125 */
126 static void register_event(private_vici_dispatcher_t *this, char *name,
127 u_int id)
128 {
129 event_t *event;
130
131 this->mutex->lock(this->mutex);
132 while (TRUE)
133 {
134 event = this->events->get(this->events, name);
135 if (!event)
136 {
137 break;
138 }
139 if (!event->uses)
140 {
141 array_insert(event->clients, ARRAY_TAIL, &id);
142 break;
143 }
144 this->cond->wait(this->cond, this->mutex);
145 }
146 this->mutex->unlock(this->mutex);
147
148 if (event)
149 {
150 DBG2(DBG_CFG, "vici client %u registered for: %s", id, name);
151 send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
152 }
153 else
154 {
155 DBG1(DBG_CFG, "vici client %u invalid registration: %s", id, name);
156 send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
157 }
158 }
159
160 /**
161 * Unregister client for event
162 */
163 static void unregister_event(private_vici_dispatcher_t *this, char *name,
164 u_int id)
165 {
166 enumerator_t *enumerator;
167 event_t *event;
168 u_int *current;
169 bool found = FALSE;
170
171 this->mutex->lock(this->mutex);
172 while (TRUE)
173 {
174 event = this->events->get(this->events, name);
175 if (!event)
176 {
177 break;
178 }
179 if (!event->uses)
180 {
181 enumerator = array_create_enumerator(event->clients);
182 while (enumerator->enumerate(enumerator, &current))
183 {
184 if (*current == id)
185 {
186 array_remove_at(event->clients, enumerator);
187 found = TRUE;
188 break;
189 }
190 }
191 enumerator->destroy(enumerator);
192 break;
193 }
194 this->cond->wait(this->cond, this->mutex);
195 }
196 this->mutex->unlock(this->mutex);
197
198 DBG2(DBG_CFG, "vici client %u unregistered for: %s", id, name);
199
200 if (found)
201 {
202 send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
203 }
204 else
205 {
206 send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
207 }
208 }
209
210 /**
211 * Data to release on thread cancellation
212 */
213 typedef struct {
214 private_vici_dispatcher_t *this;
215 command_t *cmd;
216 vici_message_t *request;
217 } release_data_t;
218
219 /**
220 * Release command after execution/cancellation
221 */
222 CALLBACK(release_command, void,
223 release_data_t *release)
224 {
225 release->request->destroy(release->request);
226
227 release->this->mutex->lock(release->this->mutex);
228 if (--release->cmd->uses == 0)
229 {
230 release->this->cond->broadcast(release->this->cond);
231 }
232 release->this->mutex->unlock(release->this->mutex);
233
234 free(release);
235 }
236
237 /**
238 * Process a request message
239 */
240 void process_request(private_vici_dispatcher_t *this, char *name, u_int id,
241 chunk_t data)
242 {
243 vici_message_t *response = NULL;
244 release_data_t *release;
245 command_t *cmd;
246
247 this->mutex->lock(this->mutex);
248 cmd = this->cmds->get(this->cmds, name);
249 if (cmd)
250 {
251 cmd->uses++;
252 }
253 this->mutex->unlock(this->mutex);
254
255 if (cmd)
256 {
257 INIT(release,
258 .this = this,
259 .cmd = cmd,
260 );
261
262 DBG2(DBG_CFG, "vici client %u requests: %s", id, name);
263
264 thread_cleanup_push(release_command, release);
265
266 release->request = vici_message_create_from_data(data, FALSE);
267 response = release->cmd->cb(cmd->user, cmd->name, id, release->request);
268
269 thread_cleanup_pop(TRUE);
270
271 if (response)
272 {
273 send_op(this, id, VICI_CMD_RESPONSE, NULL, response);
274 response->destroy(response);
275 }
276 }
277 else
278 {
279 DBG1(DBG_CFG, "vici client %u invalid request: %s", id, name);
280 send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL);
281 }
282 }
283
284 CALLBACK(inbound, void,
285 private_vici_dispatcher_t *this, u_int id, chunk_t data)
286 {
287 bio_reader_t *reader;
288 chunk_t chunk;
289 u_int8_t type;
290 char name[257];
291
292 reader = bio_reader_create(data);
293 if (reader->read_uint8(reader, &type))
294 {
295 switch (type)
296 {
297 case VICI_EVENT_REGISTER:
298 if (reader->read_data8(reader, &chunk) &&
299 vici_stringify(chunk, name, sizeof(name)))
300 {
301 register_event(this, name, id);
302 }
303 else
304 {
305 DBG1(DBG_CFG, "invalid vici register message");
306 }
307 break;
308 case VICI_EVENT_UNREGISTER:
309 if (reader->read_data8(reader, &chunk) &&
310 vici_stringify(chunk, name, sizeof(name)))
311 {
312 unregister_event(this, name, id);
313 }
314 else
315 {
316 DBG1(DBG_CFG, "invalid vici unregister message");
317 }
318 break;
319 case VICI_CMD_REQUEST:
320 if (reader->read_data8(reader, &chunk) &&
321 vici_stringify(chunk, name, sizeof(name)))
322 {
323 thread_cleanup_push((void*)reader->destroy, reader);
324 process_request(this, name, id, reader->peek(reader));
325 thread_cleanup_pop(FALSE);
326 }
327 else
328 {
329 DBG1(DBG_CFG, "invalid vici request message");
330 }
331 break;
332 case VICI_CMD_RESPONSE:
333 case VICI_EVENT_CONFIRM:
334 case VICI_EVENT_UNKNOWN:
335 case VICI_EVENT:
336 default:
337 DBG1(DBG_CFG, "unsupported vici operation: %u", type);
338 break;
339 }
340 }
341 else
342 {
343 DBG1(DBG_CFG, "invalid vici message");
344 }
345 reader->destroy(reader);
346 }
347
348 CALLBACK(connect_, void,
349 private_vici_dispatcher_t *this, u_int id)
350 {
351 DBG2(DBG_CFG, "vici client %u connected", id);
352 }
353
354 CALLBACK(disconnect, void,
355 private_vici_dispatcher_t *this, u_int id)
356 {
357 enumerator_t *events, *ids;
358 event_t *event;
359 u_int *current;
360
361 /* deregister client from all events */
362 this->mutex->lock(this->mutex);
363 events = this->events->create_enumerator(this->events);
364 while (events->enumerate(events, NULL, &event))
365 {
366 while (event->uses)
367 {
368 this->cond->wait(this->cond, this->mutex);
369 }
370 ids = array_create_enumerator(event->clients);
371 while (ids->enumerate(ids, &current))
372 {
373 if (id == *current)
374 {
375 array_remove_at(event->clients, ids);
376 }
377 }
378 ids->destroy(ids);
379 }
380 events->destroy(events);
381 this->mutex->unlock(this->mutex);
382
383 DBG2(DBG_CFG, "vici client %u disconnected", id);
384 }
385
386 METHOD(vici_dispatcher_t, manage_command, void,
387 private_vici_dispatcher_t *this, char *name,
388 vici_command_cb_t cb, void *user)
389 {
390 command_t *cmd;
391
392 this->mutex->lock(this->mutex);
393 if (cb)
394 {
395 INIT(cmd,
396 .name = strdup(name),
397 .cb = cb,
398 .user = user,
399 );
400 cmd = this->cmds->put(this->cmds, cmd->name, cmd);
401 }
402 else
403 {
404 cmd = this->cmds->remove(this->cmds, name);
405 }
406 if (cmd)
407 {
408 while (cmd->uses)
409 {
410 this->cond->wait(this->cond, this->mutex);
411 }
412 free(cmd->name);
413 free(cmd);
414 }
415 this->mutex->unlock(this->mutex);
416 }
417
418 METHOD(vici_dispatcher_t, manage_event, void,
419 private_vici_dispatcher_t *this, char *name, bool reg)
420 {
421 event_t *event;
422
423 this->mutex->lock(this->mutex);
424 if (reg)
425 {
426 INIT(event,
427 .name = strdup(name),
428 .clients = array_create(sizeof(u_int), 0),
429 );
430 event = this->events->put(this->events, event->name, event);
431 }
432 else
433 {
434 event = this->events->remove(this->events, name);
435 }
436 if (event)
437 {
438 while (event->uses)
439 {
440 this->cond->wait(this->cond, this->mutex);
441 }
442 array_destroy(event->clients);
443 free(event->name);
444 free(event);
445 }
446 this->mutex->unlock(this->mutex);
447 }
448
449 METHOD(vici_dispatcher_t, raise_event, void,
450 private_vici_dispatcher_t *this, char *name, u_int id,
451 vici_message_t *message)
452 {
453 enumerator_t *enumerator;
454 event_t *event;
455 u_int *current;
456
457 this->mutex->lock(this->mutex);
458 event = this->events->get(this->events, name);
459 if (event)
460 {
461 event->uses++;
462 this->mutex->unlock(this->mutex);
463
464 enumerator = array_create_enumerator(event->clients);
465 while (enumerator->enumerate(enumerator, &current))
466 {
467 if (id == 0 || id == *current)
468 {
469 send_op(this, *current, VICI_EVENT, name, message);
470 }
471 }
472 enumerator->destroy(enumerator);
473
474 this->mutex->lock(this->mutex);
475 if (--event->uses == 0)
476 {
477 this->cond->broadcast(this->cond);
478 }
479 }
480 this->mutex->unlock(this->mutex);
481
482 message->destroy(message);
483 }
484
485 METHOD(vici_dispatcher_t, destroy, void,
486 private_vici_dispatcher_t *this)
487 {
488 DESTROY_IF(this->socket);
489 this->mutex->destroy(this->mutex);
490 this->cond->destroy(this->cond);
491 this->cmds->destroy(this->cmds);
492 this->events->destroy(this->events);
493 free(this);
494 }
495
496 /**
497 * See header
498 */
499 vici_dispatcher_t *vici_dispatcher_create(char *uri)
500 {
501 private_vici_dispatcher_t *this;
502
503 INIT(this,
504 .public = {
505 .manage_command = _manage_command,
506 .manage_event = _manage_event,
507 .raise_event = _raise_event,
508 .destroy = _destroy,
509 },
510 .cmds = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
511 .events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
512 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
513 .cond = condvar_create(CONDVAR_TYPE_DEFAULT),
514 );
515
516 this->socket = vici_socket_create(uri, inbound, connect_, disconnect, this);
517 if (!this->socket)
518 {
519 destroy(this);
520 return NULL;
521 }
522
523 return &this->public;
524 }