vici: Provide a command dispatcher handling request and event registration

This commit is contained in:
Martin Willi
2014-05-07 14:13:34 +02:00
parent 8457da7528
commit 8383d626b9
9 changed files with 1115 additions and 138 deletions
+3
View 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 = \
@@ -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;
}
+253
View File
@@ -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;
}
+129
View File
@@ -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_ @}*/
@@ -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;
}
@@ -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_ @}*/
+66 -136
View 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;
}
@@ -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);
}
+7 -2
View 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_ @}*/
+20
View File
@@ -14,8 +14,10 @@
*/
#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;
}