vici: Fix dispatcher leak when handling unknown request
[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 event = this->events->get(this->events, name);
133 if (event)
134 {
135 array_insert(event->clients, ARRAY_TAIL, &id);
136 }
137 this->mutex->unlock(this->mutex);
138
139 if (event)
140 {
141 send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
142 }
143 else
144 {
145 send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
146 }
147 }
148
149 /**
150 * Unregister client for event
151 */
152 static void unregister_event(private_vici_dispatcher_t *this, char *name,
153 u_int id)
154 {
155 enumerator_t *enumerator;
156 event_t *event;
157 u_int *current;
158 bool found = FALSE;
159
160 this->mutex->lock(this->mutex);
161 event = this->events->get(this->events, name);
162 if (event)
163 {
164 enumerator = array_create_enumerator(event->clients);
165 while (enumerator->enumerate(enumerator, &current))
166 {
167 if (*current == id)
168 {
169 array_remove_at(event->clients, enumerator);
170 found = TRUE;
171 break;
172 }
173 }
174 enumerator->destroy(enumerator);
175 }
176 this->mutex->unlock(this->mutex);
177
178 if (found)
179 {
180 send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
181 }
182 else
183 {
184 send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
185 }
186 }
187
188 /**
189 * Data to release on thread cancellation
190 */
191 typedef struct {
192 private_vici_dispatcher_t *this;
193 command_t *cmd;
194 vici_message_t *request;
195 } release_data_t;
196
197 /**
198 * Release command after execution/cancellation
199 */
200 CALLBACK(release_command, void,
201 release_data_t *release)
202 {
203 release->request->destroy(release->request);
204
205 release->this->mutex->lock(release->this->mutex);
206 if (--release->cmd->uses == 0)
207 {
208 release->this->cond->broadcast(release->this->cond);
209 }
210 release->this->mutex->unlock(release->this->mutex);
211
212 free(release);
213 }
214
215 /**
216 * Process a request message
217 */
218 void process_request(private_vici_dispatcher_t *this, char *name, u_int id,
219 chunk_t data)
220 {
221 vici_message_t *response = NULL;
222 release_data_t *release;
223 command_t *cmd;
224
225 this->mutex->lock(this->mutex);
226 cmd = this->cmds->get(this->cmds, name);
227 if (cmd)
228 {
229 cmd->uses++;
230 }
231 this->mutex->unlock(this->mutex);
232
233 if (cmd)
234 {
235 INIT(release,
236 .this = this,
237 .cmd = cmd,
238 );
239
240 thread_cleanup_push(release_command, release);
241
242 release->request = vici_message_create_from_data(data, FALSE);
243 response = release->cmd->cb(cmd->user, cmd->name, id, release->request);
244
245 thread_cleanup_pop(TRUE);
246
247 if (response)
248 {
249 send_op(this, id, VICI_CMD_RESPONSE, NULL, response);
250 response->destroy(response);
251 }
252 }
253 else
254 {
255 send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL);
256 }
257 }
258
259 CALLBACK(inbound, void,
260 private_vici_dispatcher_t *this, u_int id, chunk_t data)
261 {
262 bio_reader_t *reader;
263 chunk_t chunk;
264 u_int8_t type;
265 char name[257];
266
267 reader = bio_reader_create(data);
268 if (reader->read_uint8(reader, &type))
269 {
270 switch (type)
271 {
272 case VICI_EVENT_REGISTER:
273 if (reader->read_data8(reader, &chunk) &&
274 vici_stringify(chunk, name, sizeof(name)))
275 {
276 register_event(this, name, id);
277 }
278 else
279 {
280 DBG1(DBG_CFG, "invalid vici register message");
281 }
282 break;
283 case VICI_EVENT_UNREGISTER:
284 if (reader->read_data8(reader, &chunk) &&
285 vici_stringify(chunk, name, sizeof(name)))
286 {
287 unregister_event(this, name, id);
288 }
289 else
290 {
291 DBG1(DBG_CFG, "invalid vici unregister message");
292 }
293 break;
294 case VICI_CMD_REQUEST:
295 if (reader->read_data8(reader, &chunk) &&
296 vici_stringify(chunk, name, sizeof(name)))
297 {
298 thread_cleanup_push((void*)reader->destroy, reader);
299 process_request(this, name, id, reader->peek(reader));
300 thread_cleanup_pop(FALSE);
301 }
302 else
303 {
304 DBG1(DBG_CFG, "invalid vici request message");
305 }
306 break;
307 case VICI_CMD_RESPONSE:
308 case VICI_EVENT_CONFIRM:
309 case VICI_EVENT_UNKNOWN:
310 case VICI_EVENT:
311 default:
312 DBG1(DBG_CFG, "unsupported vici operation: %u", type);
313 break;
314 }
315 }
316 else
317 {
318 DBG1(DBG_CFG, "invalid vici message");
319 }
320 reader->destroy(reader);
321 }
322
323 CALLBACK(connect_, void,
324 private_vici_dispatcher_t *this, u_int id)
325 {
326 }
327
328 CALLBACK(disconnect, void,
329 private_vici_dispatcher_t *this, u_int id)
330 {
331 enumerator_t *events, *ids;
332 event_t *event;
333 u_int *current;
334
335 /* deregister all clients */
336 this->mutex->lock(this->mutex);
337 events = this->events->create_enumerator(this->events);
338 while (events->enumerate(events, NULL, &event))
339 {
340 ids = array_create_enumerator(event->clients);
341 while (ids->enumerate(ids, &current))
342 {
343 if (id == *current)
344 {
345 array_remove_at(event->clients, ids);
346 }
347 }
348 ids->destroy(ids);
349 }
350 events->destroy(events);
351 this->mutex->unlock(this->mutex);
352 }
353
354 METHOD(vici_dispatcher_t, manage_command, void,
355 private_vici_dispatcher_t *this, char *name,
356 vici_command_cb_t cb, void *user)
357 {
358 command_t *cmd;
359
360 this->mutex->lock(this->mutex);
361 if (cb)
362 {
363 INIT(cmd,
364 .name = strdup(name),
365 .cb = cb,
366 .user = user,
367 );
368 cmd = this->cmds->put(this->cmds, cmd->name, cmd);
369 }
370 else
371 {
372 cmd = this->cmds->remove(this->cmds, name);
373 }
374 if (cmd)
375 {
376 while (cmd->uses)
377 {
378 this->cond->wait(this->cond, this->mutex);
379 }
380 free(cmd->name);
381 free(cmd);
382 }
383 this->mutex->unlock(this->mutex);
384 }
385
386 METHOD(vici_dispatcher_t, manage_event, void,
387 private_vici_dispatcher_t *this, char *name, bool reg)
388 {
389 event_t *event;
390
391 this->mutex->lock(this->mutex);
392 if (reg)
393 {
394 INIT(event,
395 .name = strdup(name),
396 .clients = array_create(sizeof(u_int), 0),
397 );
398 event = this->events->put(this->events, event->name, event);
399 }
400 else
401 {
402 event = this->events->remove(this->events, name);
403 }
404 if (event)
405 {
406 while (event->uses)
407 {
408 this->cond->wait(this->cond, this->mutex);
409 }
410 array_destroy(event->clients);
411 free(event->name);
412 free(event);
413 }
414 this->mutex->unlock(this->mutex);
415 }
416
417 METHOD(vici_dispatcher_t, raise_event, void,
418 private_vici_dispatcher_t *this, char *name, u_int id,
419 vici_message_t *message)
420 {
421 enumerator_t *enumerator;
422 event_t *event;
423 u_int *current;
424
425 this->mutex->lock(this->mutex);
426 event = this->events->get(this->events, name);
427 if (event)
428 {
429 event->uses++;
430 }
431 this->mutex->unlock(this->mutex);
432
433 enumerator = array_create_enumerator(event->clients);
434 while (enumerator->enumerate(enumerator, &current))
435 {
436 if (id == 0 || id == *current)
437 {
438 send_op(this, *current, VICI_EVENT, name, message);
439 }
440 }
441 enumerator->destroy(enumerator);
442
443 this->mutex->lock(this->mutex);
444 if (--event->uses == 0)
445 {
446 this->cond->broadcast(this->cond);
447 }
448 this->mutex->unlock(this->mutex);
449
450 message->destroy(message);
451 }
452
453 METHOD(vici_dispatcher_t, destroy, void,
454 private_vici_dispatcher_t *this)
455 {
456 DESTROY_IF(this->socket);
457 this->mutex->destroy(this->mutex);
458 this->cond->destroy(this->cond);
459 this->cmds->destroy(this->cmds);
460 this->events->destroy(this->events);
461 free(this);
462 }
463
464 /**
465 * See header
466 */
467 vici_dispatcher_t *vici_dispatcher_create(char *uri)
468 {
469 private_vici_dispatcher_t *this;
470
471 INIT(this,
472 .public = {
473 .manage_command = _manage_command,
474 .manage_event = _manage_event,
475 .raise_event = _raise_event,
476 .destroy = _destroy,
477 },
478 .cmds = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
479 .events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
480 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
481 .cond = condvar_create(CONDVAR_TYPE_DEFAULT),
482 );
483
484 this->socket = vici_socket_create(uri, inbound, connect_, disconnect, this);
485 if (!this->socket)
486 {
487 destroy(this);
488 return NULL;
489 }
490
491 return &this->public;
492 }