vici: Add a test case raising events during request, checks in-order delivery
[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
224 INIT(release,
225 .this = this,
226 );
227
228 this->mutex->lock(this->mutex);
229 release->cmd = this->cmds->get(this->cmds, name);
230 if (release->cmd)
231 {
232 release->cmd->uses++;
233 }
234 this->mutex->unlock(this->mutex);
235
236 if (release->cmd)
237 {
238 thread_cleanup_push(release_command, release);
239
240 release->request = vici_message_create_from_data(data, FALSE);
241 response = release->cmd->cb(release->cmd->user, release->cmd->name,
242 id, release->request);
243
244 thread_cleanup_pop(TRUE);
245
246 if (response)
247 {
248 send_op(this, id, VICI_CMD_RESPONSE, NULL, response);
249 response->destroy(response);
250 }
251 }
252 else
253 {
254 send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL);
255 }
256 }
257
258 CALLBACK(inbound, void,
259 private_vici_dispatcher_t *this, u_int id, chunk_t data)
260 {
261 bio_reader_t *reader;
262 chunk_t chunk;
263 u_int8_t type;
264 char name[257];
265
266 reader = bio_reader_create(data);
267 if (reader->read_uint8(reader, &type))
268 {
269 switch (type)
270 {
271 case VICI_EVENT_REGISTER:
272 if (reader->read_data8(reader, &chunk) &&
273 vici_stringify(chunk, name, sizeof(name)))
274 {
275 register_event(this, name, id);
276 }
277 else
278 {
279 DBG1(DBG_CFG, "invalid vici register message");
280 }
281 break;
282 case VICI_EVENT_UNREGISTER:
283 if (reader->read_data8(reader, &chunk) &&
284 vici_stringify(chunk, name, sizeof(name)))
285 {
286 unregister_event(this, name, id);
287 }
288 else
289 {
290 DBG1(DBG_CFG, "invalid vici unregister message");
291 }
292 break;
293 case VICI_CMD_REQUEST:
294 if (reader->read_data8(reader, &chunk) &&
295 vici_stringify(chunk, name, sizeof(name)))
296 {
297 thread_cleanup_push((void*)reader->destroy, reader);
298 process_request(this, name, id, reader->peek(reader));
299 thread_cleanup_pop(FALSE);
300 }
301 else
302 {
303 DBG1(DBG_CFG, "invalid vici request message");
304 }
305 break;
306 case VICI_CMD_RESPONSE:
307 case VICI_EVENT_CONFIRM:
308 case VICI_EVENT_UNKNOWN:
309 case VICI_EVENT:
310 default:
311 DBG1(DBG_CFG, "unsupported vici operation: %u", type);
312 break;
313 }
314 }
315 else
316 {
317 DBG1(DBG_CFG, "invalid vici message");
318 }
319 reader->destroy(reader);
320 }
321
322 CALLBACK(connect_, void,
323 private_vici_dispatcher_t *this, u_int id)
324 {
325 }
326
327 CALLBACK(disconnect, void,
328 private_vici_dispatcher_t *this, u_int id)
329 {
330 enumerator_t *events, *ids;
331 event_t *event;
332 u_int *current;
333
334 /* deregister all clients */
335 this->mutex->lock(this->mutex);
336 events = this->events->create_enumerator(this->events);
337 while (events->enumerate(events, NULL, &event))
338 {
339 ids = array_create_enumerator(event->clients);
340 while (ids->enumerate(ids, &current))
341 {
342 if (id == *current)
343 {
344 array_remove_at(event->clients, ids);
345 }
346 }
347 ids->destroy(ids);
348 }
349 events->destroy(events);
350 this->mutex->unlock(this->mutex);
351 }
352
353 METHOD(vici_dispatcher_t, manage_command, void,
354 private_vici_dispatcher_t *this, char *name,
355 vici_command_cb_t cb, void *user)
356 {
357 command_t *cmd;
358
359 this->mutex->lock(this->mutex);
360 if (cb)
361 {
362 INIT(cmd,
363 .name = strdup(name),
364 .cb = cb,
365 .user = user,
366 );
367 cmd = this->cmds->put(this->cmds, cmd->name, cmd);
368 }
369 else
370 {
371 cmd = this->cmds->remove(this->cmds, name);
372 }
373 if (cmd)
374 {
375 while (cmd->uses)
376 {
377 this->cond->wait(this->cond, this->mutex);
378 }
379 free(cmd->name);
380 free(cmd);
381 }
382 this->mutex->unlock(this->mutex);
383 }
384
385 METHOD(vici_dispatcher_t, manage_event, void,
386 private_vici_dispatcher_t *this, char *name, bool reg)
387 {
388 event_t *event;
389
390 this->mutex->lock(this->mutex);
391 if (reg)
392 {
393 INIT(event,
394 .name = strdup(name),
395 .clients = array_create(sizeof(u_int), 0),
396 );
397 event = this->events->put(this->events, event->name, event);
398 }
399 else
400 {
401 event = this->events->remove(this->events, name);
402 }
403 if (event)
404 {
405 while (event->uses)
406 {
407 this->cond->wait(this->cond, this->mutex);
408 }
409 array_destroy(event->clients);
410 free(event->name);
411 free(event);
412 }
413 this->mutex->unlock(this->mutex);
414 }
415
416 METHOD(vici_dispatcher_t, raise_event, void,
417 private_vici_dispatcher_t *this, char *name, u_int id,
418 vici_message_t *message)
419 {
420 enumerator_t *enumerator;
421 event_t *event;
422 u_int *current;
423
424 this->mutex->lock(this->mutex);
425 event = this->events->get(this->events, name);
426 if (event)
427 {
428 event->uses++;
429 }
430 this->mutex->unlock(this->mutex);
431
432 enumerator = array_create_enumerator(event->clients);
433 while (enumerator->enumerate(enumerator, &current))
434 {
435 if (id == 0 || id == *current)
436 {
437 send_op(this, *current, VICI_EVENT, name, message);
438 }
439 }
440 enumerator->destroy(enumerator);
441
442 this->mutex->lock(this->mutex);
443 if (--event->uses == 0)
444 {
445 this->cond->broadcast(this->cond);
446 }
447 this->mutex->unlock(this->mutex);
448
449 message->destroy(message);
450 }
451
452 METHOD(vici_dispatcher_t, destroy, void,
453 private_vici_dispatcher_t *this)
454 {
455 DESTROY_IF(this->socket);
456 this->mutex->destroy(this->mutex);
457 this->cond->destroy(this->cond);
458 this->cmds->destroy(this->cmds);
459 this->events->destroy(this->events);
460 free(this);
461 }
462
463 /**
464 * See header
465 */
466 vici_dispatcher_t *vici_dispatcher_create(char *uri)
467 {
468 private_vici_dispatcher_t *this;
469
470 INIT(this,
471 .public = {
472 .manage_command = _manage_command,
473 .manage_event = _manage_event,
474 .raise_event = _raise_event,
475 .destroy = _destroy,
476 },
477 .cmds = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
478 .events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
479 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
480 .cond = condvar_create(CONDVAR_TYPE_DEFAULT),
481 );
482
483 this->socket = vici_socket_create(uri, inbound, connect_, disconnect, this);
484 if (!this->socket)
485 {
486 destroy(this);
487 return NULL;
488 }
489
490 return &this->public;
491 }