Return to vici_dispatcher.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libcharon / plugins / vici |
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: }