Annotation of embedaddon/strongswan/src/libcharon/plugins/vici/vici_dispatcher.c, revision 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>