vici: Provide a command dispatcher handling request and event registration
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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, ¤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;
|
||||
}
|
||||
@@ -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_ @}*/
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_ @}*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user