vici: Provide a command dispatcher handling request and event registration
authorMartin Willi <martin@revosec.ch>
Thu, 23 Jan 2014 14:32:23 +0000 (15:32 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 7 May 2014 12:13:34 +0000 (14:13 +0200)
src/libcharon/plugins/vici/Makefile.am
src/libcharon/plugins/vici/suites/test_message.c
src/libcharon/plugins/vici/vici_builder.c [new file with mode: 0644]
src/libcharon/plugins/vici/vici_builder.h [new file with mode: 0644]
src/libcharon/plugins/vici/vici_dispatcher.c [new file with mode: 0644]
src/libcharon/plugins/vici/vici_dispatcher.h [new file with mode: 0644]
src/libcharon/plugins/vici/vici_message.c
src/libcharon/plugins/vici/vici_message.h
src/libcharon/plugins/vici/vici_plugin.c

index d166d59..3723966 100644 (file)
@@ -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 = \
index a35c309..a8196bd 100644 (file)
@@ -16,6 +16,7 @@
 #include <test_suite.h>
 
 #include "../vici_message.h"
+#include "../vici_builder.h"
 
 #include <unistd.h>
 
@@ -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 (file)
index 0000000..5616320
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <bio/bio_writer.h>
+
+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 (file)
index 0000000..5a5cc8a
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 (file)
index 0000000..797e51f
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <bio/bio_reader.h>
+#include <bio/bio_writer.h>
+#include <threading/mutex.h>
+#include <threading/condvar.h>
+#include <collections/array.h>
+#include <collections/hashtable.h>
+
+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, &current))
+               {
+                       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, &current))
+               {
+                       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 (file)
index 0000000..5185504
--- /dev/null
@@ -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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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_ @}*/
index 7a9e6b6..5209455 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include "vici_message.h"
+#include "vici_builder.h"
 
 #include <bio/bio_reader.h>
 #include <bio/bio_writer.h>
@@ -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;
        }
@@ -251,137 +252,43 @@ vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup)
 }
 
 /**
- * Write from enumerator to writer
+ * See header
  */
-static bool write_from_enumerator(bio_writer_t *writer,
-                                                                 enumerator_t *enumerator)
+vici_message_t *vici_message_create_from_enumerator(enumerator_t *enumerator)
 {
+       vici_builder_t *builder;
        vici_type_t type;
        char *name;
        chunk_t value;
-       int section = 0;
-       bool list = FALSE;
 
+       builder = vici_builder_create();
        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;
+                               builder->add(builder, type, name);
+                               continue;
+                       case VICI_KEY_VALUE:
+                               builder->add(builder, type, name, value);
+                               continue;
                        case VICI_LIST_ITEM:
-                               writer->write_data16(writer, value);
-                               break;
+                               builder->add(builder, type, value);
+                               continue;
+                       case VICI_SECTION_END:
                        case VICI_LIST_END:
-                               list = FALSE;
-                               break;
-                       case VICI_END:
-                               return TRUE;
                        default:
-                               return FALSE;
+                               builder->add(builder, type);
+                               continue;
+                       case VICI_END:
+                               break;
                }
-       }
-       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;
-
-       writer = bio_writer_create(0);
-       if (write_from_enumerator(writer, enumerator))
-       {
-               data = chunk_clone(writer->get_buf(writer));
-               message = vici_message_create_from_data(data, TRUE);
+               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;
-
-       INIT(enumerator,
-               .public = {
-                       .enumerate = (void*)_va_enumerate,
-                       .destroy = _va_destroy,
-               },
-               .first = &type,
-       );
-       va_start(enumerator->args, type);
+       vici_builder_t *builder;
+       va_list args;
+       char *name;
+       chunk_t value;
 
-       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);
 }
index 81c0be0..a7f506a 100644 (file)
@@ -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_ @}*/
index cbe3b15..855dbc9 100644 (file)
  */
 
 #include "vici_plugin.h"
+#include "vici_dispatcher.h"
 
 #include <library.h>
+#include <daemon.h>
 
 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;
 }