diff --git a/src/libcharon/plugins/vici/Makefile.am b/src/libcharon/plugins/vici/Makefile.am index d166d594a..3723966ff 100644 --- a/src/libcharon/plugins/vici/Makefile.am +++ b/src/libcharon/plugins/vici/Makefile.am @@ -16,6 +16,8 @@ endif libstrongswan_vici_la_SOURCES = \ vici_socket.h vici_socket.c \ vici_message.h vici_message.c \ + vici_builder.h vici_builder.c \ + vici_dispatcher.h vici_dispatcher.c \ vici_plugin.h vici_plugin.c libstrongswan_vici_la_LDFLAGS = -module -avoid-version @@ -29,6 +31,7 @@ vici_tests_SOURCES = \ suites/test_message.c \ vici_socket.c \ vici_message.c \ + vici_builder.c \ vici_tests.h vici_tests.c vici_tests_CFLAGS = \ diff --git a/src/libcharon/plugins/vici/suites/test_message.c b/src/libcharon/plugins/vici/suites/test_message.c index a35c30905..a8196bd63 100644 --- a/src/libcharon/plugins/vici/suites/test_message.c +++ b/src/libcharon/plugins/vici/suites/test_message.c @@ -16,6 +16,7 @@ #include #include "../vici_message.h" +#include "../vici_builder.h" #include @@ -247,6 +248,62 @@ START_TEST(test_vararg) } END_TEST +START_TEST(test_builder) +{ + enumerator_t *parse, *tmpl; + vici_message_t *m; + vici_builder_t *b; + + b = vici_builder_create(); + b->add(b, VICI_SECTION_START, "section1"); + b->add(b, VICI_LIST_START, "list1"); + b->add(b, VICI_LIST_ITEM, chunk_from_str("item1")); + b->add(b, VICI_LIST_ITEM, chunk_from_str("item2")); + b->add(b, VICI_LIST_END); + b->add(b, VICI_KEY_VALUE, "key1", chunk_from_str("value1")); + b->add(b, VICI_SECTION_END); + m = b->finalize(b); + ck_assert(m); + tmpl = endecode_create_enumerator(endecode_test_list); + parse = m->create_enumerator(m); + ck_assert(parse); + + compare_vici(parse, tmpl); + + m->destroy(m); + tmpl->destroy(tmpl); + parse->destroy(parse); +} +END_TEST + +START_TEST(test_builder_fmt) +{ + enumerator_t *parse, *tmpl; + vici_message_t *m; + vici_builder_t *b; + + b = vici_builder_create(); + b->begin_section(b, "section1"); + b->begin_list(b, "list1"); + b->add_li(b, "item%u", 1); + b->add_li(b, "%s%u", "item", 2); + b->end_list(b); + b->add_kv(b, "key1", "value%u", 1); + b->end_section(b); + m = b->finalize(b); + ck_assert(m); + tmpl = endecode_create_enumerator(endecode_test_list); + parse = m->create_enumerator(m); + ck_assert(parse); + + compare_vici(parse, tmpl); + + m->destroy(m); + tmpl->destroy(tmpl); + parse->destroy(parse); +} +END_TEST + Suite *message_suite_create() { Suite *s; @@ -262,5 +319,13 @@ Suite *message_suite_create() tcase_add_test(tc, test_vararg); suite_add_tcase(s, tc); + tc = tcase_create("builder encode"); + tcase_add_test(tc, test_builder); + suite_add_tcase(s, tc); + + tc = tcase_create("builder format encode"); + tcase_add_test(tc, test_builder_fmt); + suite_add_tcase(s, tc); + return s; } diff --git a/src/libcharon/plugins/vici/vici_builder.c b/src/libcharon/plugins/vici/vici_builder.c new file mode 100644 index 000000000..561632049 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_builder.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_builder.h" + +#include + +typedef struct private_vici_builder_t private_vici_builder_t; + +/** + * Private data of an vici_builder_t object. + */ +struct private_vici_builder_t { + + /** + * Public vici_builder_t interface. + */ + vici_builder_t public; + + /** + * Writer for elements + */ + bio_writer_t *writer; + + /** + * Errors encountered + */ + u_int error; + + /** + * Section nesting level + */ + u_int section; + + /** + * In list element? + */ + bool list; +}; + +METHOD(vici_builder_t, add, void, + private_vici_builder_t *this, vici_type_t type, ...) +{ + va_list args; + char *name = NULL; + chunk_t value = chunk_empty; + + va_start(args, type); + switch (type) + { + case VICI_SECTION_END: + case VICI_LIST_END: + case VICI_END: + break; + case VICI_LIST_START: + case VICI_SECTION_START: + name = va_arg(args, char*); + break; + case VICI_KEY_VALUE: + name = va_arg(args, char*); + value = va_arg(args, chunk_t); + break; + case VICI_LIST_ITEM: + value = va_arg(args, chunk_t); + break; + default: + va_end(args); + this->error++; + return; + } + va_end(args); + + if (value.len > 0xffff) + { + this->error++; + return; + } + if (!vici_verify_type(type, this->section, this->list)) + { + this->error++; + return; + } + if (type != VICI_END) + { + this->writer->write_uint8(this->writer, type); + } + switch (type) + { + case VICI_SECTION_START: + this->writer->write_data8(this->writer, chunk_from_str(name)); + this->section++; + break; + case VICI_SECTION_END: + this->section--; + break; + case VICI_KEY_VALUE: + this->writer->write_data8(this->writer, chunk_from_str(name)); + this->writer->write_data16(this->writer, value); + break; + case VICI_LIST_START: + this->writer->write_data8(this->writer, chunk_from_str(name)); + this->list = TRUE; + break; + case VICI_LIST_ITEM: + this->writer->write_data16(this->writer, value); + break; + case VICI_LIST_END: + this->list = FALSE; + break; + default: + this->error++; + break; + } +} + +METHOD(vici_builder_t, vadd_kv, void, + private_vici_builder_t *this, char *key, char *fmt, va_list args) +{ + char buf[2048]; + ssize_t len; + + len = vsnprintf(buf, sizeof(buf), fmt, args); + if (len < 0 || len >= sizeof(buf)) + { + DBG1(DBG_ENC, "vici builder format buffer exceeds limit"); + this->error++; + } + else + { + add(this, VICI_KEY_VALUE, key, chunk_create(buf, len)); + } +} + +METHOD(vici_builder_t, add_kv, void, + private_vici_builder_t *this, char *key, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vadd_kv(this, key, fmt, args); + va_end(args); +} + + +METHOD(vici_builder_t, vadd_li, void, + private_vici_builder_t *this, char *fmt, va_list args) +{ + char buf[2048]; + ssize_t len; + + len = vsnprintf(buf, sizeof(buf), fmt, args); + if (len < 0 || len >= sizeof(buf)) + { + DBG1(DBG_ENC, "vici builder format buffer exceeds limit"); + this->error++; + } + else + { + add(this, VICI_LIST_ITEM, chunk_create(buf, len)); + } +} + +METHOD(vici_builder_t, add_li, void, + private_vici_builder_t *this, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vadd_li(this, fmt, args); + va_end(args); +} + +METHOD(vici_builder_t, begin_section, void, + private_vici_builder_t *this, char *name) +{ + add(this, VICI_SECTION_START, name); +} + +METHOD(vici_builder_t, end_section, void, + private_vici_builder_t *this) +{ + add(this, VICI_SECTION_END); +} + +METHOD(vici_builder_t, begin_list, void, + private_vici_builder_t *this, char *name) +{ + add(this, VICI_LIST_START, name); +} + +METHOD(vici_builder_t, end_list, void, + private_vici_builder_t *this) +{ + add(this, VICI_LIST_END); +} + +METHOD(vici_builder_t, finalize, vici_message_t*, + private_vici_builder_t *this) +{ + vici_message_t *product; + + if (this->error || this->section || this->list) + { + DBG1(DBG_ENC, "vici builder error: %u errors (section: %u, list %u)", + this->error, this->section, this->list); + this->writer->destroy(this->writer); + free(this); + return NULL; + } + product = vici_message_create_from_data( + this->writer->extract_buf(this->writer), TRUE); + this->writer->destroy(this->writer); + free(this); + return product; +} + +/** + * See header + */ +vici_builder_t *vici_builder_create() +{ + private_vici_builder_t *this; + + INIT(this, + .public = { + .add = _add, + .add_kv = _add_kv, + .vadd_kv = _vadd_kv, + .add_li = _add_li, + .vadd_li = _vadd_li, + .begin_section = _begin_section, + .end_section = _end_section, + .begin_list = _begin_list, + .end_list = _end_list, + .finalize = _finalize, + }, + .writer = bio_writer_create(0), + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_builder.h b/src/libcharon/plugins/vici/vici_builder.h new file mode 100644 index 000000000..5a5cc8a03 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_builder.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_builder vici_builder + * @{ @ingroup vici + */ + +#ifndef VICI_BUILDER_H_ +#define VICI_BUILDER_H_ + +#include "vici_message.h" + +typedef struct vici_builder_t vici_builder_t; + +/** + * Build helper for vici message + */ +struct vici_builder_t { + + /** + * Append a generic message element to message. + * + * The additional arguments are type specific, it may be nothing, a string, + * a chunk value or both. + * + * @param type element type to add + * @param ... additional type specific arguments + */ + void (*add)(vici_builder_t *this, vici_type_t type, ...); + + /** + * Append a key/value element using a format string. + * + * Instead of passing the type specific value as a chunk, this method + * takes a printf() style format string followed by its arguments. The + * key name for a key/value type is still a fixed string. + * + * @param key key name of the key/value to add + * @param fmt value format string + * @param ... arguments to value format string + */ + void (*add_kv)(vici_builder_t *this, char *key, char *fmt, ...); + + /** + * Append a message element using a format string and va_list. + * + * Instead of passing the type specific value as a chunk, this method + * takes a printf() style format string followed by its arguments. The + * key name for a key/value type is still a fixed string. + * + * @param key key name of the key/value to add + * @param fmt value format string + * @param args arguments to value format string + */ + void (*vadd_kv)(vici_builder_t *this, char *key, char *fmt, va_list args); + + /** + * Append a list item element using a format string. + * + * Instead of passing the type specific value as a chunk, this method + * takes a printf() style format string followed by its arguments. + * + * @param fmt value format string + * @param ... arguments to value format string + */ + void (*add_li)(vici_builder_t *this, char *fmt, ...); + + /** + * Append a list item element using a format string and va_list. + * + * Instead of passing the type specific value as a chunk, this method + * takes a printf() style format string followed by its arguments. + * + * @param fmt value format string + * @param args arguments to value format string + */ + void (*vadd_li)(vici_builder_t *this, char *fmt, va_list args); + + /** + * Begin a new section. + * + * @param name name of section to begin + */ + void (*begin_section)(vici_builder_t *this, char *name); + + /** + * End the currently open section. + */ + void (*end_section)(vici_builder_t *this); + + /** + * Begin a new list. + * + * @param name name of list to begin + */ + void (*begin_list)(vici_builder_t *this, char *name); + + /** + * End the currently open list. + */ + void (*end_list)(vici_builder_t *this); + + /** + * Finalize a vici message with all added elements, destroy builder. + * + * @return vici message, NULL on error + */ + vici_message_t* (*finalize)(vici_builder_t *this); +}; + +/** + * Create a vici_builder instance. + */ +vici_builder_t *vici_builder_create(); + +#endif /** VICI_BUILDER_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_dispatcher.c b/src/libcharon/plugins/vici/vici_dispatcher.c new file mode 100644 index 000000000..797e51f61 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_dispatcher.c @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "vici_dispatcher.h" +#include "vici_socket.h" + +#include +#include +#include +#include +#include +#include + +typedef struct private_vici_dispatcher_t private_vici_dispatcher_t; + +/** + * Private data of an vici_dispatcher_t object. + */ +struct private_vici_dispatcher_t { + + /** + * Public vici_dispatcher_t interface. + */ + vici_dispatcher_t public; + + /** + * Socket to send/receive messages + */ + vici_socket_t *socket; + + /** + * List of registered commands (char* => command_t*) + */ + hashtable_t *cmds; + + /** + * List of known events, and registered clients (char* => event_t*) + */ + hashtable_t *events; + + /** + * Mutex to lock hashtables + */ + mutex_t *mutex; + + /** + * Condvar to signal command termination + */ + condvar_t *cond; +}; + +/** + * Registered command + */ +typedef struct { + /** command name */ + char *name; + /** callback for command */ + vici_command_cb_t cb; + /** user data to pass to callback */ + void *user; + /** command currently in use? */ + u_int uses; +} command_t; + +/** + * Registered event + */ +typedef struct { + /** event name */ + char *name; + /** registered clients, as u_int */ + array_t *clients; + /** event currently in use? */ + u_int uses; +} event_t; + +/** + * Send a operation code, optionally with name and message + */ +static void send_op(private_vici_dispatcher_t *this, u_int id, + vici_operation_t op, char *name, vici_message_t *message) +{ + bio_writer_t *writer; + u_int len; + + len = sizeof(u_int8_t); + if (name) + { + len += sizeof(u_int8_t) + strlen(name); + } + if (message) + { + len += message->get_encoding(message).len; + } + writer = bio_writer_create(len); + writer->write_uint8(writer, op); + if (name) + { + writer->write_data8(writer, chunk_from_str(name)); + } + if (message) + { + writer->write_data(writer, message->get_encoding(message)); + } + this->socket->send(this->socket, id, writer->extract_buf(writer)); + writer->destroy(writer); +} + +/** + * Register client for event + */ +static void register_event(private_vici_dispatcher_t *this, char *name, + u_int id) +{ + event_t *event; + + this->mutex->lock(this->mutex); + event = this->events->get(this->events, name); + if (event) + { + array_insert(event->clients, ARRAY_TAIL, &id); + } + this->mutex->unlock(this->mutex); + + if (event) + { + send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL); + } + else + { + send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL); + } +} + +/** + * Unregister client for event + */ +static void unregister_event(private_vici_dispatcher_t *this, char *name, + u_int id) +{ + enumerator_t *enumerator; + event_t *event; + u_int *current; + bool found = FALSE; + + this->mutex->lock(this->mutex); + event = this->events->get(this->events, name); + if (event) + { + enumerator = array_create_enumerator(event->clients); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (*current == id) + { + array_remove_at(event->clients, enumerator); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + } + this->mutex->unlock(this->mutex); + + if (found) + { + send_op(this, id, VICI_EVENT_CONFIRM, NULL, NULL); + } + else + { + send_op(this, id, VICI_EVENT_UNKNOWN, NULL, NULL); + } +} + +/** + * Process a request message + */ +void process_request(private_vici_dispatcher_t *this, char *name, u_int id, + chunk_t data) +{ + command_t *cmd; + vici_message_t *request, *response = NULL; + + this->mutex->lock(this->mutex); + cmd = this->cmds->get(this->cmds, name); + if (cmd) + { + cmd->uses++; + } + this->mutex->unlock(this->mutex); + + if (cmd) + { + request = vici_message_create_from_data(data, FALSE); + response = cmd->cb(cmd->user, cmd->name, id, request); + request->destroy(request); + + this->mutex->lock(this->mutex); + if (--cmd->uses == 0) + { + this->cond->broadcast(this->cond); + } + this->mutex->unlock(this->mutex); + } + if (response) + { + send_op(this, id, VICI_CMD_RESPONSE, NULL, response); + response->destroy(response); + } + else + { + send_op(this, id, VICI_CMD_UNKNOWN, NULL, NULL); + } +} + +CALLBACK(inbound, void, + private_vici_dispatcher_t *this, u_int id, chunk_t data) +{ + bio_reader_t *reader; + chunk_t chunk; + u_int8_t type; + char name[257]; + + reader = bio_reader_create(data); + if (reader->read_uint8(reader, &type)) + { + switch (type) + { + case VICI_EVENT_REGISTER: + if (reader->read_data8(reader, &chunk) && + vici_stringify(chunk, name, sizeof(name))) + { + register_event(this, name, id); + } + else + { + DBG1(DBG_CFG, "invalid vici register message"); + } + break; + case VICI_EVENT_UNREGISTER: + if (reader->read_data8(reader, &chunk) && + vici_stringify(chunk, name, sizeof(name))) + { + unregister_event(this, name, id); + } + else + { + DBG1(DBG_CFG, "invalid vici unregister message"); + } + break; + case VICI_CMD_REQUEST: + if (reader->read_data8(reader, &chunk) && + vici_stringify(chunk, name, sizeof(name))) + { + process_request(this, name, id, reader->peek(reader)); + } + else + { + DBG1(DBG_CFG, "invalid vici request message"); + } + break; + case VICI_CMD_RESPONSE: + case VICI_EVENT_CONFIRM: + case VICI_EVENT_UNKNOWN: + case VICI_EVENT: + default: + DBG1(DBG_CFG, "unsupported vici operation: %u", type); + break; + } + } + else + { + DBG1(DBG_CFG, "invalid vici message"); + } + reader->destroy(reader); +} + +CALLBACK(connect_, void, + private_vici_dispatcher_t *this, u_int id) +{ +} + +CALLBACK(disconnect, void, + private_vici_dispatcher_t *this, u_int id) +{ + enumerator_t *events, *ids; + event_t *event; + u_int *current; + + /* deregister all clients */ + this->mutex->lock(this->mutex); + events = this->events->create_enumerator(this->events); + while (events->enumerate(events, NULL, &event)) + { + ids = array_create_enumerator(event->clients); + while (ids->enumerate(ids, ¤t)) + { + if (id == *current) + { + array_remove_at(event->clients, ids); + } + } + ids->destroy(ids); + } + events->destroy(events); + this->mutex->unlock(this->mutex); +} + +METHOD(vici_dispatcher_t, manage_command, void, + private_vici_dispatcher_t *this, char *name, + vici_command_cb_t cb, void *user) +{ + command_t *cmd; + + this->mutex->lock(this->mutex); + if (cb) + { + INIT(cmd, + .name = strdup(name), + .cb = cb, + .user = user, + ); + cmd = this->cmds->put(this->cmds, cmd->name, cmd); + } + else + { + cmd = this->cmds->remove(this->cmds, name); + } + if (cmd) + { + while (cmd->uses) + { + this->cond->wait(this->cond, this->mutex); + } + free(cmd->name); + free(cmd); + } + this->mutex->unlock(this->mutex); +} + +METHOD(vici_dispatcher_t, manage_event, void, + private_vici_dispatcher_t *this, char *name, bool reg) +{ + event_t *event; + + this->mutex->lock(this->mutex); + if (reg) + { + INIT(event, + .name = strdup(name), + .clients = array_create(sizeof(u_int), 0), + ); + event = this->events->put(this->events, event->name, event); + } + else + { + event = this->events->remove(this->events, name); + } + if (event) + { + while (event->uses) + { + this->cond->wait(this->cond, this->mutex); + } + array_destroy(event->clients); + free(event->name); + free(event); + } + this->mutex->unlock(this->mutex); +} + +METHOD(vici_dispatcher_t, raise_event, void, + private_vici_dispatcher_t *this, char *name, vici_message_t *message) +{ + enumerator_t *enumerator; + event_t *event; + u_int *id; + + this->mutex->lock(this->mutex); + event = this->events->get(this->events, name); + if (event) + { + event->uses++; + } + this->mutex->unlock(this->mutex); + + enumerator = array_create_enumerator(event->clients); + while (enumerator->enumerate(enumerator, &id)) + { + send_op(this, *id, VICI_EVENT, name, message); + } + enumerator->destroy(enumerator); + + this->mutex->lock(this->mutex); + if (--event->uses == 0) + { + this->cond->broadcast(this->cond); + } + this->mutex->unlock(this->mutex); + + message->destroy(message); +} + +METHOD(vici_dispatcher_t, destroy, void, + private_vici_dispatcher_t *this) +{ + DESTROY_IF(this->socket); + this->mutex->destroy(this->mutex); + this->cond->destroy(this->cond); + this->cmds->destroy(this->cmds); + this->events->destroy(this->events); + free(this); +} + +/** + * See header + */ +vici_dispatcher_t *vici_dispatcher_create(char *uri) +{ + private_vici_dispatcher_t *this; + + INIT(this, + .public = { + .manage_command = _manage_command, + .manage_event = _manage_event, + .raise_event = _raise_event, + .destroy = _destroy, + }, + .cmds = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1), + .events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .cond = condvar_create(CONDVAR_TYPE_DEFAULT), + ); + + this->socket = vici_socket_create(uri, inbound, connect_, disconnect, this); + if (!this->socket) + { + destroy(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libcharon/plugins/vici/vici_dispatcher.h b/src/libcharon/plugins/vici/vici_dispatcher.h new file mode 100644 index 000000000..518550491 --- /dev/null +++ b/src/libcharon/plugins/vici/vici_dispatcher.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2013 Martin Willi + * Copyright (C) 2013 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup vici_dispatcher vici_dispatcher + * @{ @ingroup vici + */ + +#ifndef VICI_DISPATCHER_H_ +#define VICI_DISPATCHER_H_ + +#include "vici_message.h" + +typedef struct vici_dispatcher_t vici_dispatcher_t; +typedef enum vici_operation_t vici_operation_t; + +/** + * Default socket URI of vici service + */ +#define VICI_DEFAULT_URI "unix://" IPSEC_PIDDIR "/charon.vici" + +/** + * Kind of vici operation + */ +enum vici_operation_t { + /** a named request message */ + VICI_CMD_REQUEST, + /** an unnamed response message to a request */ + VICI_CMD_RESPONSE, + /** unnamed response if requested command is unknown */ + VICI_CMD_UNKNOWN, + /** a named event registration request */ + VICI_EVENT_REGISTER, + /** a named event unregistration request */ + VICI_EVENT_UNREGISTER, + /** unnamed response for successful event (un-)registration */ + VICI_EVENT_CONFIRM, + /** unnamed response if event (un-)registration failed */ + VICI_EVENT_UNKNOWN, + /** a named event message */ + VICI_EVENT, +}; + +/** + * Vici command callback function + * + * @param user user data, as supplied during registration + * @param name name of the command it has been registered under + * @param id client connection identifier + * @param request request message data + * @return response message + */ +typedef vici_message_t* (*vici_command_cb_t)(void *user, char *name, u_int id, + vici_message_t *request); + +/** + * Vici command dispatcher. + */ +struct vici_dispatcher_t { + + /** + * Register/Unregister a callback invoked for a specific command request. + * + * @param name name of the command + * @param cb callback function to register, NULL to unregister + * @param user user data to pass to callback + */ + void (*manage_command)(vici_dispatcher_t *this, char *name, + vici_command_cb_t cb, void *user); + + /** + * Register/Unregister an event type to send. + * + * The dispatcher internally manages event subscriptions. Clients registered + * for an event will receive such messages when the event is raised. + * + * @param name event name to manager + * @param reg TRUE to register, FALSE to unregister + */ + void (*manage_event)(vici_dispatcher_t *this, char *name, bool reg); + + /** + * Raise an event to all clients registered to that event. + * + * @param name event name to raise + * @param message event message to send, gets destroyed + */ + void (*raise_event)(vici_dispatcher_t *this, char *name, + vici_message_t *message); + + /** + * Destroy a vici_dispatcher_t. + */ + void (*destroy)(vici_dispatcher_t *this); +}; + +/** + * Create a vici_dispatcher instance. + * + * @param uri uri for listening stream service + * @return dispatcher instance + */ +vici_dispatcher_t *vici_dispatcher_create(char *uri); + +#endif /** VICI_DISPATCHER_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_message.c b/src/libcharon/plugins/vici/vici_message.c index 7a9e6b6ec..52094550c 100644 --- a/src/libcharon/plugins/vici/vici_message.c +++ b/src/libcharon/plugins/vici/vici_message.c @@ -14,6 +14,7 @@ */ #include "vici_message.h" +#include "vici_builder.h" #include #include @@ -65,9 +66,9 @@ bool vici_stringify(chunk_t chunk, char *buf, size_t size) } /** - * Verify the occurence of a given type for given section/list nesting + * See header. */ -static bool verify_type(vici_type_t type, int section, bool list) +bool vici_verify_type(vici_type_t type, u_int section, bool list) { if (list) { @@ -133,7 +134,7 @@ METHOD(enumerator_t, parse_enumerate, bool, *out = VICI_END; return TRUE; } - if (!verify_type(type, this->section, this->list)) + if (!vici_verify_type(type, this->section, this->list)) { return FALSE; } @@ -250,138 +251,44 @@ vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup) return &this->public; } -/** - * Write from enumerator to writer - */ -static bool write_from_enumerator(bio_writer_t *writer, - enumerator_t *enumerator) -{ - vici_type_t type; - char *name; - chunk_t value; - int section = 0; - bool list = FALSE; - - while (enumerator->enumerate(enumerator, &type, &name, &value)) - { - if (!verify_type(type, section, list)) - { - return FALSE; - } - - if (type != VICI_END) - { - writer->write_uint8(writer, type); - } - - switch (type) - { - case VICI_SECTION_START: - writer->write_data8(writer, chunk_from_str(name)); - section++; - break; - case VICI_SECTION_END: - section--; - break; - case VICI_KEY_VALUE: - writer->write_data8(writer, chunk_from_str(name)); - writer->write_data16(writer, value); - break; - case VICI_LIST_START: - writer->write_data8(writer, chunk_from_str(name)); - list = TRUE; - break; - case VICI_LIST_ITEM: - writer->write_data16(writer, value); - break; - case VICI_LIST_END: - list = FALSE; - break; - case VICI_END: - return TRUE; - default: - return FALSE; - } - } - return FALSE; -} - /** * See header */ vici_message_t *vici_message_create_from_enumerator(enumerator_t *enumerator) { - vici_message_t *message = NULL; - bio_writer_t *writer; - chunk_t data; + vici_builder_t *builder; + vici_type_t type; + char *name; + chunk_t value; - writer = bio_writer_create(0); - if (write_from_enumerator(writer, enumerator)) + builder = vici_builder_create(); + while (enumerator->enumerate(enumerator, &type, &name, &value)) { - data = chunk_clone(writer->get_buf(writer)); - message = vici_message_create_from_data(data, TRUE); + switch (type) + { + case VICI_SECTION_START: + case VICI_LIST_START: + builder->add(builder, type, name); + continue; + case VICI_KEY_VALUE: + builder->add(builder, type, name, value); + continue; + case VICI_LIST_ITEM: + builder->add(builder, type, value); + continue; + case VICI_SECTION_END: + case VICI_LIST_END: + default: + builder->add(builder, type); + continue; + case VICI_END: + break; + } + break; } enumerator->destroy(enumerator); - writer->destroy(writer); - return message; -} - -/** - * Enumerator for va_list arguments - */ -typedef struct { - /* implements enumerator */ - enumerator_t public; - /** arguments to enumerate */ - va_list args; - /** first type, if not yet processed */ - vici_type_t *first; -} va_enumerator_t; - -METHOD(enumerator_t, va_enumerate, bool, - va_enumerator_t *this, vici_type_t *out, char **name, chunk_t *value) -{ - vici_type_t type; - - if (this->first) - { - type = *this->first; - this->first = NULL; - } - else - { - type = va_arg(this->args, vici_type_t); - } - switch (type) - { - case VICI_SECTION_END: - case VICI_LIST_END: - case VICI_END: - break; - case VICI_LIST_START: - case VICI_SECTION_START: - *name = va_arg(this->args, char*); - break; - case VICI_KEY_VALUE: - *name = va_arg(this->args, char*); - *value = va_arg(this->args, chunk_t); - break; - case VICI_LIST_ITEM: - *value = va_arg(this->args, chunk_t); - break; - default: - return FALSE; - } - *out = type; - return TRUE; -} - -METHOD(enumerator_t, va_destroy, void, - va_enumerator_t *this) -{ - va_end(this->args); - free(this); + return builder->finalize(builder); } /** @@ -389,16 +296,39 @@ METHOD(enumerator_t, va_destroy, void, */ vici_message_t *vici_message_create_from_args(vici_type_t type, ...) { - va_enumerator_t *enumerator; + vici_builder_t *builder; + va_list args; + char *name; + chunk_t value; - INIT(enumerator, - .public = { - .enumerate = (void*)_va_enumerate, - .destroy = _va_destroy, - }, - .first = &type, - ); - va_start(enumerator->args, type); - - return vici_message_create_from_enumerator(&enumerator->public); + builder = vici_builder_create(); + va_start(args, type); + while (type != VICI_END) + { + switch (type) + { + case VICI_LIST_START: + case VICI_SECTION_START: + name = va_arg(args, char*); + builder->add(builder, type, name); + break; + case VICI_KEY_VALUE: + name = va_arg(args, char*); + value = va_arg(args, chunk_t); + builder->add(builder, type, name, value); + break; + case VICI_LIST_ITEM: + value = va_arg(args, chunk_t); + builder->add(builder, type, value); + break; + case VICI_SECTION_END: + case VICI_LIST_END: + default: + builder->add(builder, type); + break; + } + type = va_arg(args, vici_type_t); + } + va_end(args); + return builder->finalize(builder); } diff --git a/src/libcharon/plugins/vici/vici_message.h b/src/libcharon/plugins/vici/vici_message.h index 81c0be06d..a7f506a20 100644 --- a/src/libcharon/plugins/vici/vici_message.h +++ b/src/libcharon/plugins/vici/vici_message.h @@ -106,7 +106,7 @@ vici_message_t *vici_message_create_from_enumerator(enumerator_t *enumerator); /** * Create vici message from a variable argument list. * - * @param first first type beginning message + * @param type first type beginning message * @param ... vici_type_t and args, terminated by VICI_END * @return message representation, NULL on error */ @@ -115,11 +115,16 @@ vici_message_t *vici_message_create_from_args(vici_type_t type, ...); /** * Check if a chunk has a printable string, and print it to buf. * - * @param chunkt chunk containing potential string + * @param chunk chunk containing potential string * @param buf buffer to write string to * @param size size of buf * @return TRUE if printable and string written to buf */ bool vici_stringify(chunk_t chunk, char *buf, size_t size); +/** + * Verify the occurence of a given type for given section/list nesting + */ +bool vici_verify_type(vici_type_t type, u_int section, bool list); + #endif /** VICI_MESSAGE_H_ @}*/ diff --git a/src/libcharon/plugins/vici/vici_plugin.c b/src/libcharon/plugins/vici/vici_plugin.c index cbe3b15d4..855dbc930 100644 --- a/src/libcharon/plugins/vici/vici_plugin.c +++ b/src/libcharon/plugins/vici/vici_plugin.c @@ -14,8 +14,10 @@ */ #include "vici_plugin.h" +#include "vici_dispatcher.h" #include +#include typedef struct private_vici_plugin_t private_vici_plugin_t; @@ -28,6 +30,11 @@ struct private_vici_plugin_t { * public functions */ vici_plugin_t public; + + /** + * Dispatcher, creating socket + */ + vici_dispatcher_t *dispatcher; }; METHOD(plugin_t, get_name, char*, @@ -42,6 +49,19 @@ METHOD(plugin_t, get_name, char*, static bool register_vici(private_vici_plugin_t *this, plugin_feature_t *feature, bool reg, void *data) { + if (reg) + { + char *uri; + + uri = lib->settings->get_str(lib->settings, "%s.plugins.vici.socket", + VICI_DEFAULT_URI, lib->ns); + this->dispatcher = vici_dispatcher_create(uri); + return this->dispatcher != NULL; + } + else + { + this->dispatcher->destroy(this->dispatcher); + } return TRUE; }