Annotation of embedaddon/strongswan/src/libcharon/plugins/vici/vici_dispatcher.c, revision 1.1.1.1
1.1 misho 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: /*
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:
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>
45: #include <threading/thread.h>
46: #include <collections/array.h>
47: #include <collections/hashtable.h>
48:
49: typedef struct private_vici_dispatcher_t private_vici_dispatcher_t;
50:
51: /**
52: * Private data of an vici_dispatcher_t object.
53: */
54: struct 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: */
90: typedef 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: */
104: typedef 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: */
116: static 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:
122: len = sizeof(uint8_t);
123: if (name)
124: {
125: len += sizeof(uint8_t) + strlen(name);
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: */
148: static 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);
154: while (TRUE)
155: {
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);
167: }
168: this->mutex->unlock(this->mutex);
169:
170: if (event)
171: {
172: DBG2(DBG_CFG, "vici client %u registered for: %s", id, name);
173: send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL);
174: }
175: else
176: {
177: DBG1(DBG_CFG, "vici client %u invalid registration: %s", id, name);
178: send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL);
179: }
180: }
181:
182: /**
183: * Unregister client for event
184: */
185: static 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);
194: while (TRUE)
195: {
196: event = this->events->get(this->events, name);
197: if (!event)
198: {
199: break;
200: }
201: if (!event->uses)
202: {
203: enumerator = array_create_enumerator(event->clients);
204: while (enumerator->enumerate(enumerator, ¤t))
205: {
206: if (*current == id)
207: {
208: array_remove_at(event->clients, enumerator);
209: found = TRUE;
210: break;
211: }
212: }
213: enumerator->destroy(enumerator);
214: break;
215: }
216: this->cond->wait(this->cond, this->mutex);
217: }
218: this->mutex->unlock(this->mutex);
219:
220: DBG2(DBG_CFG, "vici client %u unregistered for: %s", id, name);
221:
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:
232: /**
233: * Data to release on thread cancellation
234: */
235: typedef 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: */
244: CALLBACK(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:
259: /**
260: * Process a request message
261: */
262: void process_request(private_vici_dispatcher_t *this, char *name, u_int id,
263: chunk_t data)
264: {
265: vici_message_t *response = NULL;
266: release_data_t *release;
267: command_t *cmd;
268:
269: this->mutex->lock(this->mutex);
270: cmd = this->cmds->get(this->cmds, name);
271: if (cmd)
272: {
273: cmd->uses++;
274: }
275: this->mutex->unlock(this->mutex);
276:
277: if (cmd)
278: {
279: INIT(release,
280: .this = this,
281: .cmd = cmd,
282: );
283:
284: DBG2(DBG_CFG, "vici client %u requests: %s", id, name);
285:
286: thread_cleanup_push(release_command, release);
287:
288: release->request = vici_message_create_from_data(data, FALSE);
289: response = release->cmd->cb(cmd->user, cmd->name, id, release->request);
290:
291: thread_cleanup_pop(TRUE);
292:
293: if (response)
294: {
295: send_op(this, id, VICI_CMD_RESPONSE, NULL, response);
296: response->destroy(response);
297: }
298: }
299: else
300: {
301: DBG1(DBG_CFG, "vici client %u invalid request: %s", id, name);
302: send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL);
303: }
304: }
305:
306: CALLBACK(inbound, void,
307: private_vici_dispatcher_t *this, u_int id, chunk_t data)
308: {
309: bio_reader_t *reader;
310: chunk_t chunk;
311: uint8_t type;
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: {
345: thread_cleanup_push((void*)reader->destroy, reader);
346: process_request(this, name, id, reader->peek(reader));
347: thread_cleanup_pop(FALSE);
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:
370: CALLBACK(connect_, void,
371: private_vici_dispatcher_t *this, u_int id)
372: {
373: DBG2(DBG_CFG, "vici client %u connected", id);
374: }
375:
376: CALLBACK(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:
383: /* deregister client from all events */
384: this->mutex->lock(this->mutex);
385: events = this->events->create_enumerator(this->events);
386: while (events->enumerate(events, NULL, &event))
387: {
388: while (event->uses)
389: {
390: this->cond->wait(this->cond, this->mutex);
391: }
392: ids = array_create_enumerator(event->clients);
393: while (ids->enumerate(ids, ¤t))
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);
404:
405: DBG2(DBG_CFG, "vici client %u disconnected", id);
406: }
407:
408: METHOD(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:
440: METHOD(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:
471: METHOD(vici_dispatcher_t, has_event_listeners, bool,
472: private_vici_dispatcher_t *this, char *name)
473: {
474: event_t *event;
475: bool retval = FALSE;
476:
477: this->mutex->lock(this->mutex);
478: event = this->events->get(this->events, name);
479: if (event)
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. */
484: retval = array_count(event->clients);
485: }
486: this->mutex->unlock(this->mutex);
487:
488: return retval;
489: }
490:
491: METHOD(vici_dispatcher_t, raise_event, void,
492: private_vici_dispatcher_t *this, char *name, u_int id,
493: vici_message_t *message)
494: {
495: enumerator_t *enumerator;
496: event_t *event;
497: u_int *current;
498:
499: this->mutex->lock(this->mutex);
500: event = this->events->get(this->events, name);
501: if (event)
502: {
503: event->uses++;
504: this->mutex->unlock(this->mutex);
505:
506: enumerator = array_create_enumerator(event->clients);
507: while (enumerator->enumerate(enumerator, ¤t))
508: {
509: if (id == 0 || id == *current)
510: {
511: send_op(this, *current, VICI_EVENT, name, message);
512: }
513: }
514: enumerator->destroy(enumerator);
515:
516: this->mutex->lock(this->mutex);
517: if (--event->uses == 0)
518: {
519: this->cond->broadcast(this->cond);
520: }
521: }
522: this->mutex->unlock(this->mutex);
523:
524: message->destroy(message);
525: }
526:
527: METHOD(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: */
541: vici_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,
549: .has_event_listeners = _has_event_listeners,
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>