vici: Add a libvici low-level client library
This commit is contained in:
parent
8383d626b9
commit
eb4fd014b8
|
@ -22,6 +22,17 @@ libstrongswan_vici_la_SOURCES = \
|
|||
|
||||
libstrongswan_vici_la_LDFLAGS = -module -avoid-version
|
||||
|
||||
|
||||
lib_LTLIBRARIES = libvici.la
|
||||
|
||||
libvici_la_SOURCES = \
|
||||
vici_message.c vici_message.h \
|
||||
vici_builder.c vici_builder.h \
|
||||
libvici.c libvici.h
|
||||
|
||||
libvici_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
|
||||
|
||||
|
||||
TESTS = vici_tests
|
||||
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
@ -29,9 +40,13 @@ check_PROGRAMS = $(TESTS)
|
|||
vici_tests_SOURCES = \
|
||||
suites/test_socket.c \
|
||||
suites/test_message.c \
|
||||
suites/test_request.c \
|
||||
suites/test_event.c \
|
||||
vici_socket.c \
|
||||
vici_message.c \
|
||||
vici_builder.c \
|
||||
vici_dispatcher.c \
|
||||
libvici.c \
|
||||
vici_tests.h vici_tests.c
|
||||
|
||||
vici_tests_CFLAGS = \
|
||||
|
|
|
@ -0,0 +1,665 @@
|
|||
/*
|
||||
* 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 "libvici.h"
|
||||
#include "vici_builder.h"
|
||||
#include "vici_dispatcher.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <threading/mutex.h>
|
||||
#include <threading/condvar.h>
|
||||
#include <collections/hashtable.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/**
|
||||
* Event registration
|
||||
*/
|
||||
typedef struct {
|
||||
/** name of event */
|
||||
char *name;
|
||||
/** callback function */
|
||||
vici_event_cb_t cb;
|
||||
/** user data for callback */
|
||||
void *user;
|
||||
} event_t;
|
||||
|
||||
/**
|
||||
* Wait state signaled by asynchronous on_read callback
|
||||
*/
|
||||
typedef enum {
|
||||
WAIT_IDLE = 0,
|
||||
WAIT_SUCCESS,
|
||||
WAIT_FAILED,
|
||||
WAIT_READ_ERROR,
|
||||
} wait_state_t;
|
||||
|
||||
/**
|
||||
* Private vici connection contex.
|
||||
*/
|
||||
struct vici_conn_t {
|
||||
/** connection stream */
|
||||
stream_t *stream;
|
||||
/** event registrations, as char* => event_t */
|
||||
hashtable_t *events;
|
||||
/** connection lock */
|
||||
mutex_t *mutex;
|
||||
/** condvar to signal incoming response */
|
||||
condvar_t *cond;
|
||||
/** queued response message */
|
||||
chunk_t queue;
|
||||
/** asynchronous read error */
|
||||
int error;
|
||||
/** wait state */
|
||||
wait_state_t wait;
|
||||
};
|
||||
|
||||
/**
|
||||
* Private vici request message.
|
||||
*/
|
||||
struct vici_req_t {
|
||||
/** connection context */
|
||||
vici_conn_t *conn;
|
||||
/** name of request message */
|
||||
char *name;
|
||||
/** message builder */
|
||||
vici_builder_t *b;
|
||||
};
|
||||
|
||||
/**
|
||||
* Private vici response/event message.
|
||||
*/
|
||||
struct vici_res_t {
|
||||
/** response message */
|
||||
vici_message_t *message;
|
||||
/** allocated strings */
|
||||
linked_list_t *strings;
|
||||
/** item enumerator */
|
||||
enumerator_t *enumerator;
|
||||
/** currently enumerating type */
|
||||
vici_type_t type;
|
||||
/** currently enumerating name */
|
||||
char *name;
|
||||
/** currently enumerating value */
|
||||
chunk_t value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Signal wait result for waiting user thread
|
||||
*/
|
||||
static bool wait_result(vici_conn_t *conn, wait_state_t wait)
|
||||
{
|
||||
conn->mutex->lock(conn->mutex);
|
||||
conn->wait = wait;
|
||||
conn->mutex->unlock(conn->mutex);
|
||||
conn->cond->signal(conn->cond);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal wait error result for waiting user thread
|
||||
*/
|
||||
static bool read_error(vici_conn_t *conn, int err)
|
||||
{
|
||||
conn->error = err;
|
||||
return wait_result(conn, WAIT_READ_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a command response message
|
||||
*/
|
||||
static bool handle_response(vici_conn_t *conn, u_int16_t len)
|
||||
{
|
||||
chunk_t buf;
|
||||
|
||||
buf = chunk_alloc(len);
|
||||
if (!conn->stream->read_all(conn->stream, buf.ptr, buf.len))
|
||||
{
|
||||
free(buf.ptr);
|
||||
return read_error(conn, errno);
|
||||
}
|
||||
conn->queue = buf;
|
||||
return wait_result(conn, WAIT_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch received event message
|
||||
*/
|
||||
static bool handle_event(vici_conn_t *conn, u_int16_t len)
|
||||
{
|
||||
vici_message_t *message;
|
||||
event_t *event;
|
||||
u_int8_t namelen;
|
||||
char name[257], *buf;
|
||||
|
||||
if (len < sizeof(namelen))
|
||||
{
|
||||
return read_error(conn, EBADMSG);
|
||||
}
|
||||
if (!conn->stream->read_all(conn->stream, &namelen, sizeof(namelen)))
|
||||
{
|
||||
return read_error(conn, errno);
|
||||
}
|
||||
if (namelen > len - sizeof(namelen))
|
||||
{
|
||||
return read_error(conn, EBADMSG);
|
||||
}
|
||||
if (!conn->stream->read_all(conn->stream, name, namelen))
|
||||
{
|
||||
return read_error(conn, errno);
|
||||
}
|
||||
name[namelen] = '\0';
|
||||
len -= sizeof(namelen) + namelen;
|
||||
buf = malloc(len);
|
||||
if (!conn->stream->read_all(conn->stream, buf, len))
|
||||
{
|
||||
free(buf);
|
||||
return read_error(conn, errno);
|
||||
}
|
||||
message = vici_message_create_from_data(chunk_create(buf, len), TRUE);
|
||||
|
||||
conn->mutex->lock(conn->mutex);
|
||||
event = conn->events->get(conn->events, name);
|
||||
if (event)
|
||||
{
|
||||
vici_res_t res = {
|
||||
.message = message,
|
||||
.enumerator = message->create_enumerator(message),
|
||||
.strings = linked_list_create(),
|
||||
};
|
||||
|
||||
event->cb(event->user, name, &res);
|
||||
|
||||
res.enumerator->destroy(res.enumerator);
|
||||
res.strings->destroy_function(res.strings, free);
|
||||
}
|
||||
conn->mutex->unlock(conn->mutex);
|
||||
|
||||
message->destroy(message);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
CALLBACK(on_read, bool,
|
||||
vici_conn_t *conn, stream_t *stream)
|
||||
{
|
||||
u_int16_t len;
|
||||
u_int8_t op;
|
||||
|
||||
if (!stream->read_all(stream, &len, sizeof(len)))
|
||||
{
|
||||
return read_error(conn, errno);
|
||||
}
|
||||
len = ntohs(len);
|
||||
if (len-- < sizeof(op))
|
||||
{
|
||||
return read_error(conn, EBADMSG);
|
||||
}
|
||||
if (!stream->read_all(stream, &op, sizeof(op)))
|
||||
{
|
||||
return read_error(conn, errno);
|
||||
}
|
||||
switch (op)
|
||||
{
|
||||
case VICI_EVENT:
|
||||
return handle_event(conn, len);
|
||||
case VICI_CMD_RESPONSE:
|
||||
return handle_response(conn, len);
|
||||
case VICI_EVENT_CONFIRM:
|
||||
return wait_result(conn, WAIT_SUCCESS);
|
||||
case VICI_CMD_UNKNOWN:
|
||||
case VICI_EVENT_UNKNOWN:
|
||||
return wait_result(conn, WAIT_FAILED);
|
||||
case VICI_CMD_REQUEST:
|
||||
case VICI_EVENT_REGISTER:
|
||||
case VICI_EVENT_UNREGISTER:
|
||||
default:
|
||||
return read_error(conn, EBADMSG);
|
||||
}
|
||||
}
|
||||
|
||||
vici_conn_t* vici_connect(char *uri)
|
||||
{
|
||||
vici_conn_t *conn;
|
||||
stream_t *stream;
|
||||
|
||||
stream = lib->streams->connect(lib->streams, uri ?: VICI_DEFAULT_URI);
|
||||
if (!stream)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT(conn,
|
||||
.stream = stream,
|
||||
.events = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1),
|
||||
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
||||
.cond = condvar_create(CONDVAR_TYPE_DEFAULT),
|
||||
);
|
||||
|
||||
stream->on_read(stream, on_read, conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
void vici_disconnect(vici_conn_t *conn)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
event_t *event;
|
||||
|
||||
conn->stream->destroy(conn->stream);
|
||||
enumerator = conn->events->create_enumerator(conn->events);
|
||||
while (enumerator->enumerate(enumerator, NULL, &event))
|
||||
{
|
||||
free(event->name);
|
||||
free(event);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
conn->events->destroy(conn->events);
|
||||
conn->mutex->destroy(conn->mutex);
|
||||
conn->cond->destroy(conn->cond);
|
||||
free(conn);
|
||||
}
|
||||
|
||||
vici_req_t* vici_begin(char *name)
|
||||
{
|
||||
vici_req_t *req;
|
||||
|
||||
INIT(req,
|
||||
.name = strdup(name),
|
||||
.b = vici_builder_create(),
|
||||
);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
void vici_begin_section(vici_req_t *req, char *name)
|
||||
{
|
||||
req->b->add(req->b, VICI_SECTION_START, name);
|
||||
}
|
||||
|
||||
void vici_end_section(vici_req_t *req)
|
||||
{
|
||||
req->b->add(req->b, VICI_SECTION_END);
|
||||
}
|
||||
|
||||
void vici_add_key_value(vici_req_t *req, char *key, void *buf, int len)
|
||||
{
|
||||
req->b->add(req->b, VICI_KEY_VALUE, key, chunk_create(buf, len));
|
||||
}
|
||||
|
||||
void vici_add_key_valuef(vici_req_t *req, char *key, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
req->b->vadd_kv(req->b, key, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void vici_begin_list(vici_req_t *req, char *name)
|
||||
{
|
||||
req->b->add(req->b, VICI_LIST_START, name);
|
||||
}
|
||||
|
||||
void vici_add_list_item(vici_req_t *req, void *buf, int len)
|
||||
{
|
||||
req->b->add(req->b, VICI_LIST_ITEM, chunk_create(buf, len));
|
||||
}
|
||||
|
||||
void vici_add_list_itemf(vici_req_t *req, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
req->b->vadd_li(req->b, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void vici_end_list(vici_req_t *req)
|
||||
{
|
||||
req->b->add(req->b, VICI_LIST_END);
|
||||
}
|
||||
|
||||
vici_res_t* vici_submit(vici_req_t *req, vici_conn_t *conn)
|
||||
{
|
||||
vici_message_t *message;
|
||||
vici_res_t *res;
|
||||
chunk_t data;
|
||||
u_int16_t len;
|
||||
u_int8_t namelen, op;
|
||||
|
||||
message = req->b->finalize(req->b);
|
||||
if (!message)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
op = VICI_CMD_REQUEST;
|
||||
namelen = strlen(req->name);
|
||||
data = message->get_encoding(message);
|
||||
len = htons(sizeof(op) + sizeof(namelen) + namelen + data.len);
|
||||
|
||||
if (!conn->stream->write_all(conn->stream, &len, sizeof(len)) ||
|
||||
!conn->stream->write_all(conn->stream, &op, sizeof(op)) ||
|
||||
!conn->stream->write_all(conn->stream, &namelen, sizeof(namelen)) ||
|
||||
!conn->stream->write_all(conn->stream, req->name, namelen) ||
|
||||
!conn->stream->write_all(conn->stream, data.ptr, data.len))
|
||||
{
|
||||
free(req->name);
|
||||
free(req);
|
||||
message->destroy(message);
|
||||
return NULL;
|
||||
}
|
||||
free(req->name);
|
||||
free(req);
|
||||
message->destroy(message);
|
||||
|
||||
message = NULL;
|
||||
conn->mutex->lock(conn->mutex);
|
||||
while (conn->wait == WAIT_IDLE)
|
||||
{
|
||||
conn->cond->wait(conn->cond, conn->mutex);
|
||||
}
|
||||
switch (conn->wait)
|
||||
{
|
||||
case WAIT_SUCCESS:
|
||||
message = vici_message_create_from_data(conn->queue, TRUE);
|
||||
conn->queue = chunk_empty;
|
||||
break;
|
||||
case WAIT_READ_ERROR:
|
||||
errno = conn->error;
|
||||
break;
|
||||
case WAIT_FAILED:
|
||||
default:
|
||||
errno = ENOENT;
|
||||
break;
|
||||
}
|
||||
conn->wait = WAIT_IDLE;
|
||||
conn->mutex->unlock(conn->mutex);
|
||||
|
||||
conn->stream->on_read(conn->stream, on_read, conn);
|
||||
|
||||
if (message)
|
||||
{
|
||||
INIT(res,
|
||||
.message = message,
|
||||
.enumerator = message->create_enumerator(message),
|
||||
.strings = linked_list_create(),
|
||||
);
|
||||
return res;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void vici_free_req(vici_req_t *req)
|
||||
{
|
||||
vici_message_t *message;
|
||||
|
||||
free(req->name);
|
||||
message = req->b->finalize(req->b);
|
||||
if (message)
|
||||
{
|
||||
message->destroy(message);
|
||||
}
|
||||
free(req);
|
||||
}
|
||||
|
||||
int vici_dump(vici_res_t *res, FILE *out)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
int ident = 0, delta = 2;
|
||||
vici_type_t type;
|
||||
char *name;
|
||||
chunk_t value;
|
||||
|
||||
enumerator = res->message->create_enumerator(res->message);
|
||||
while (enumerator->enumerate(enumerator, &type, &name, &value))
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VICI_SECTION_START:
|
||||
fprintf(out, "%*s%s {\n", ident, "", name);
|
||||
ident += delta;
|
||||
break;
|
||||
case VICI_SECTION_END:
|
||||
ident -= delta;
|
||||
fprintf(out, "%*s}\n", ident, "");
|
||||
break;
|
||||
case VICI_KEY_VALUE:
|
||||
if (chunk_printable(value, NULL, ' '))
|
||||
{
|
||||
fprintf(out, "%*s%s = %.*s\n",
|
||||
ident, "", name, (int)value.len, value.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(out, "%*s%s = 0x%+#B\n",
|
||||
ident, "", name, &value);
|
||||
}
|
||||
break;
|
||||
case VICI_LIST_START:
|
||||
fprintf(out, "%*s%s = [\n", ident, "", name);
|
||||
ident += delta;
|
||||
break;
|
||||
case VICI_LIST_END:
|
||||
ident -= delta;
|
||||
fprintf(out, "%*s]\n", ident, "");
|
||||
break;
|
||||
case VICI_LIST_ITEM:
|
||||
if (chunk_printable(value, NULL, ' '))
|
||||
{
|
||||
fprintf(out, "%*s%.*s\n",
|
||||
ident, "", (int)value.len, value.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(out, "%*s 0x%+#B\n", ident, "", &value);
|
||||
}
|
||||
break;
|
||||
case VICI_END:
|
||||
enumerator->destroy(enumerator);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
errno = EBADMSG;
|
||||
return 1;
|
||||
}
|
||||
|
||||
vici_parse_t vici_parse(vici_res_t *res)
|
||||
{
|
||||
if (!res->enumerator->enumerate(res->enumerator,
|
||||
&res->type, &res->name, &res->value))
|
||||
{
|
||||
return VICI_PARSE_ERROR;
|
||||
}
|
||||
switch (res->type)
|
||||
{
|
||||
case VICI_END:
|
||||
return VICI_PARSE_END;
|
||||
case VICI_SECTION_START:
|
||||
return VICI_PARSE_BEGIN_SECTION;
|
||||
case VICI_SECTION_END:
|
||||
return VICI_PARSE_END_SECTION;
|
||||
case VICI_LIST_START:
|
||||
return VICI_PARSE_BEGIN_LIST;
|
||||
case VICI_LIST_ITEM:
|
||||
return VICI_PARSE_LIST_ITEM;
|
||||
case VICI_LIST_END:
|
||||
return VICI_PARSE_END_LIST;
|
||||
case VICI_KEY_VALUE:
|
||||
return VICI_PARSE_KEY_VALUE;
|
||||
default:
|
||||
return VICI_PARSE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
char* vici_parse_name(vici_res_t *res)
|
||||
{
|
||||
char *name;
|
||||
|
||||
switch (res->type)
|
||||
{
|
||||
case VICI_SECTION_START:
|
||||
case VICI_LIST_START:
|
||||
case VICI_KEY_VALUE:
|
||||
name = strdup(res->name);
|
||||
res->strings->insert_last(res->strings, name);
|
||||
return name;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int vici_parse_name_eq(vici_res_t *res, char *name)
|
||||
{
|
||||
switch (res->type)
|
||||
{
|
||||
case VICI_SECTION_START:
|
||||
case VICI_LIST_START:
|
||||
case VICI_KEY_VALUE:
|
||||
return streq(name, res->name) ? 1 : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void* vici_parse_value(vici_res_t *res, int *len)
|
||||
{
|
||||
switch (res->type)
|
||||
{
|
||||
case VICI_LIST_ITEM:
|
||||
case VICI_KEY_VALUE:
|
||||
*len = res->value.len;
|
||||
return res->value.ptr;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
char* vici_parse_value_str(vici_res_t *res)
|
||||
{
|
||||
char *val;
|
||||
|
||||
switch (res->type)
|
||||
{
|
||||
case VICI_LIST_ITEM:
|
||||
case VICI_KEY_VALUE:
|
||||
if (!chunk_printable(res->value, NULL, 0))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
val = strndup(res->value.ptr, res->value.len);
|
||||
res->strings->insert_last(res->strings, val);
|
||||
return val;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void vici_free_res(vici_res_t *res)
|
||||
{
|
||||
res->strings->destroy_function(res->strings, free);
|
||||
res->message->destroy(res->message);
|
||||
res->enumerator->destroy(res->enumerator);
|
||||
free(res);
|
||||
}
|
||||
|
||||
int vici_register(vici_conn_t *conn, char *name, vici_event_cb_t cb, void *user)
|
||||
{
|
||||
event_t *event;
|
||||
u_int16_t len;
|
||||
u_int8_t namelen, op;
|
||||
int ret = 1;
|
||||
|
||||
op = cb ? VICI_EVENT_REGISTER : VICI_EVENT_UNREGISTER;
|
||||
namelen = strlen(name);
|
||||
len = htons(sizeof(op) + sizeof(namelen) + namelen);
|
||||
if (!conn->stream->write_all(conn->stream, &len, sizeof(len)) ||
|
||||
!conn->stream->write_all(conn->stream, &op, sizeof(op)) ||
|
||||
!conn->stream->write_all(conn->stream, &namelen, sizeof(namelen)) ||
|
||||
!conn->stream->write_all(conn->stream, name, namelen))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
conn->mutex->lock(conn->mutex);
|
||||
while (conn->wait == WAIT_IDLE)
|
||||
{
|
||||
conn->cond->wait(conn->cond, conn->mutex);
|
||||
}
|
||||
switch (conn->wait)
|
||||
{
|
||||
case WAIT_SUCCESS:
|
||||
ret = 0;
|
||||
break;
|
||||
case WAIT_READ_ERROR:
|
||||
errno = conn->error;
|
||||
break;
|
||||
case WAIT_FAILED:
|
||||
default:
|
||||
errno = ENOENT;
|
||||
break;
|
||||
}
|
||||
conn->wait = WAIT_IDLE;
|
||||
conn->mutex->unlock(conn->mutex);
|
||||
|
||||
conn->stream->on_read(conn->stream, on_read, conn);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
conn->mutex->lock(conn->mutex);
|
||||
if (cb)
|
||||
{
|
||||
INIT(event,
|
||||
.name = strdup(name),
|
||||
.cb = cb,
|
||||
.user = user,
|
||||
);
|
||||
event = conn->events->put(conn->events, event->name, event);
|
||||
}
|
||||
else
|
||||
{
|
||||
event = conn->events->remove(conn->events, name);
|
||||
}
|
||||
conn->mutex->unlock(conn->mutex);
|
||||
|
||||
if (event)
|
||||
{
|
||||
free(event->name);
|
||||
free(event);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void vici_init()
|
||||
{
|
||||
library_init(NULL, "vici");
|
||||
if (lib->processor->get_total_threads(lib->processor) < 4)
|
||||
{
|
||||
lib->processor->set_threads(lib->processor, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void vici_deinit()
|
||||
{
|
||||
library_deinit();
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* 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 libvici libvici
|
||||
* @{ @ingroup vici
|
||||
*
|
||||
* libvici is a low-level client library for the "Versatile IKE Control
|
||||
* Interface" protocol. While it uses libstrongswan and its thread-pool for
|
||||
* asynchronous message delivery, this interface does not directly depend on
|
||||
* libstrongswan interfaces and should be stable.
|
||||
*
|
||||
* This interface provides the following basic functions:
|
||||
*
|
||||
* - vici_init()/vici_deinit(): Library initialization functions
|
||||
* - vici_connect(): Connect to a vici service
|
||||
* - vici_disconnect(): Disconnect from a vici service
|
||||
*
|
||||
* Library initialization is basically required to set up libstrongswan and
|
||||
* a small thread pool. Initialize libstrongswan manually instead.
|
||||
*
|
||||
* Connecting requires an uri, which is currently either a UNIX socket path
|
||||
* prefixed with unix://, or a hostname:port touple prefixed with tcp://.
|
||||
* Passing NULL takes the system default socket path.
|
||||
*
|
||||
* After the connection has been established, request messages can be sent.
|
||||
* Only a single thread may operate on a single connection instance
|
||||
* simultaneously. To construct request messages, use the following functions:
|
||||
*
|
||||
* - vici_add_key_value() / vici_add_key_valuef(): Add key/value pairs
|
||||
* - vici_begin(): Start constructing a new request message
|
||||
* - vici_begin_section(): Open a new section to add contents to
|
||||
* - vici_end_section(): Close a previously opened session
|
||||
* - vici_begin_list(): Open a new list to add list items to
|
||||
* - vici_end_list(): Close a previously opened list
|
||||
* - vici_add_list_item() / vici_add_list_itemf(): Add list item
|
||||
*
|
||||
* Once the request message is complete, it can be sent or cancelled with:
|
||||
*
|
||||
* - vici_submit()
|
||||
* - vici_free_req()
|
||||
*
|
||||
* If submitting a message is successful, a response message is returned. It
|
||||
* can be processed using the following functions:
|
||||
*
|
||||
* - vici_parse(): Parse content type
|
||||
* - vici_parse_name(): Parse name if content type provides one
|
||||
* - vici_parse_name_eq(): Parse name and check if matches string
|
||||
* - vici_parse_value() / vici_parse_value_str(): Parse value for content type
|
||||
* - vici_dump(): Dump a full response to a FILE stream
|
||||
* - vici_free_res(): Free response after use
|
||||
*
|
||||
* Usually vici_parse() is called in a loop, and depending on the returned
|
||||
* type the name and value can be inspected.
|
||||
*
|
||||
* To register or unregister for asynchronous event messages vici_register() is
|
||||
* used. The registered callback gets invoked by an asynchronous thread. To
|
||||
* parse the event message, the vici_parse*() functions can be used.
|
||||
*/
|
||||
|
||||
#ifndef LIBVICI_H_
|
||||
#define LIBVICI_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* Opaque vici connection contex.
|
||||
*/
|
||||
typedef struct vici_conn_t vici_conn_t;
|
||||
|
||||
/**
|
||||
* Opaque vici request message.
|
||||
*/
|
||||
typedef struct vici_req_t vici_req_t;
|
||||
|
||||
/**
|
||||
* Opaque vici response/event message.
|
||||
*/
|
||||
typedef struct vici_res_t vici_res_t;
|
||||
|
||||
/**
|
||||
* Vici parse result, as returned by vici_parse().
|
||||
*/
|
||||
typedef enum {
|
||||
/** encountered a section start, has a name */
|
||||
VICI_PARSE_BEGIN_SECTION,
|
||||
/** encountered a section end */
|
||||
VICI_PARSE_END_SECTION,
|
||||
/** encountered a list start, has a name */
|
||||
VICI_PARSE_BEGIN_LIST,
|
||||
/** encountered a list element, has a value */
|
||||
VICI_PARSE_LIST_ITEM,
|
||||
/** encountered a list end */
|
||||
VICI_PARSE_END_LIST,
|
||||
/** encountered a key/value pair, has a name and a value */
|
||||
VICI_PARSE_KEY_VALUE,
|
||||
/** encountered valid end of message */
|
||||
VICI_PARSE_END,
|
||||
/** parse error */
|
||||
VICI_PARSE_ERROR,
|
||||
} vici_parse_t;
|
||||
|
||||
/**
|
||||
* Callback function invoked for received event messages.
|
||||
*
|
||||
* It is not allowed to call vici_submit() from this callback.
|
||||
*
|
||||
* @param user user data, as passed to vici_connect
|
||||
* @param name name of received event
|
||||
* @param msg associated event message, destroyed by libvici
|
||||
*/
|
||||
typedef void (*vici_event_cb_t)(void *user, char *name, vici_res_t *msg);
|
||||
|
||||
/**
|
||||
* Open a new vici connection.
|
||||
*
|
||||
* On error, NULL is returned and errno is set appropriately.
|
||||
*
|
||||
* @param uri URI to connect to, NULL to use system default
|
||||
* @return opaque vici connection context, NULL on error
|
||||
*/
|
||||
vici_conn_t* vici_connect(char *uri);
|
||||
|
||||
/**
|
||||
* Close a vici connection.
|
||||
*
|
||||
* @param conn connection context
|
||||
*/
|
||||
void vici_disconnect(vici_conn_t *conn);
|
||||
|
||||
/**
|
||||
* Begin a new vici message request.
|
||||
*
|
||||
* This function always succeeds.
|
||||
*
|
||||
* @param name name of request command
|
||||
* @return request message, to add contents
|
||||
*/
|
||||
vici_req_t* vici_begin(char *name);
|
||||
|
||||
/**
|
||||
* Begin a new section in a vici request message.
|
||||
*
|
||||
* @param req request message to create a new section in
|
||||
* @param name name of section to create
|
||||
*/
|
||||
void vici_begin_section(vici_req_t *req, char *name);
|
||||
|
||||
/**
|
||||
* End a previously opened section.
|
||||
*
|
||||
* @param req request message to close an open section in
|
||||
*/
|
||||
void vici_end_section(vici_req_t *req);
|
||||
|
||||
/**
|
||||
* Add a key/value pair, using an as-is blob as value.
|
||||
*
|
||||
* @param req request message to add key/value pair to
|
||||
* @param key key name of key/value pair
|
||||
* @param buf pointer to blob to add as value
|
||||
* @param len length of value blob to add
|
||||
*/
|
||||
void vici_add_key_value(vici_req_t *req, char *key, void *buf, int len);
|
||||
|
||||
/**
|
||||
* Add a key/value pair, setting value from a printf() format string.
|
||||
*
|
||||
* @param req request message to add key/value pair to
|
||||
* @param key key name of key/value pair
|
||||
* @param fmt format string for value
|
||||
* @param ... arguments to format string
|
||||
*/
|
||||
void vici_add_key_valuef(vici_req_t *req, char *key, char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Begin a list in a request message.
|
||||
*
|
||||
* After starting a list, only list items can be added until the list gets
|
||||
* closed by vici_end_list().
|
||||
*
|
||||
* @param req request message to begin list in
|
||||
* @param name name of list to begin
|
||||
*/
|
||||
void vici_begin_list(vici_req_t *req, char *name);
|
||||
|
||||
/**
|
||||
* Add a list item to a currently open list, using an as-is blob.
|
||||
*
|
||||
* @param req request message to add list item to
|
||||
* @param buf pointer to blob to add as value
|
||||
* @param len length of value blob to add
|
||||
*/
|
||||
void vici_add_list_item(vici_req_t *req, void *buf, int len);
|
||||
|
||||
/**
|
||||
* Add a list item to a currently open list, using a printf() format string.
|
||||
*
|
||||
* @param req request message to add list item to
|
||||
* @param fmt format string to create value from
|
||||
* @param ... arguments to format string
|
||||
*/
|
||||
void vici_add_list_itemf(vici_req_t *req, char *fmt, ...);
|
||||
|
||||
/**
|
||||
* End a previously opened list in a request message.
|
||||
*
|
||||
* @param req request message to end list in
|
||||
*/
|
||||
void vici_end_list(vici_req_t *req);
|
||||
|
||||
/**
|
||||
* Submit a request message, and wait for response.
|
||||
*
|
||||
* On error, NULL is returned an errno is set appropriately. The request
|
||||
* messages gets cleaned up by this call and gets invalid.
|
||||
*
|
||||
* @param req request message to send
|
||||
* @param conn connection context to send message over
|
||||
* @return response message, NULL on error
|
||||
*/
|
||||
vici_res_t* vici_submit(vici_req_t *req, vici_conn_t *conn);
|
||||
|
||||
/**
|
||||
* Cancel a request message started.
|
||||
*
|
||||
* If a request created by vici_begin() does not get submitted using
|
||||
* vici_submit(), it has to get freed using this call.
|
||||
*
|
||||
* @param req request message to clean up
|
||||
*/
|
||||
void vici_free_req(vici_req_t *req);
|
||||
|
||||
/**
|
||||
* Dump a message text representation to a FILE stream.
|
||||
*
|
||||
* @param res response message to dump
|
||||
* @param out FILE to dump to
|
||||
* @return 0 if dumped complete message, 1 on error
|
||||
*/
|
||||
int vici_dump(vici_res_t *res, FILE *out);
|
||||
|
||||
/**
|
||||
* Parse next element from a vici response message.
|
||||
*
|
||||
* @param res response message to parse
|
||||
* @return parse result
|
||||
*/
|
||||
vici_parse_t vici_parse(vici_res_t *res);
|
||||
|
||||
/**
|
||||
* Parse name tag / key of a previously parsed element.
|
||||
*
|
||||
* This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE,
|
||||
* VICI_PARSE_BEGIN_SECTION or VICI_PARSE_BEGIN_LIST.
|
||||
*
|
||||
* The string is valid until vici_free_res() is called.
|
||||
*
|
||||
* @param res response message to parse
|
||||
* @return name tag / key, NULL on error
|
||||
*/
|
||||
char* vici_parse_name(vici_res_t *res);
|
||||
|
||||
/**
|
||||
* Compare name tag / key of a previusly parsed element.
|
||||
*
|
||||
* This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE,
|
||||
* VICI_PARSE_BEGIN_SECTION or VICI_PARSE_BEGIN_LIST.
|
||||
*
|
||||
* @param res response message to parse
|
||||
* @param name string to compare
|
||||
* @return 1 if name equals, 0 if not
|
||||
*/
|
||||
int vici_parse_name_eq(vici_res_t *res, char *name);
|
||||
|
||||
/**
|
||||
* Parse value of a previously parsed element, as a blob.
|
||||
*
|
||||
* This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE or
|
||||
* VICI_PARSE_LIST_ITEM.
|
||||
* The string is valid until vici_free_res() is called.
|
||||
*/
|
||||
void* vici_parse_value(vici_res_t *res, int *len);
|
||||
|
||||
/**
|
||||
* Parse value of a previously parsed element, as a string.
|
||||
*
|
||||
* This call is valid only after vici_parse() returned VICI_PARSE_KEY_VALUE or
|
||||
* VICI_PARSE_LIST_ITEM.
|
||||
* This call is successful only if the value contains no non-printable
|
||||
* characters. The string is valid until vici_free_res() is called.
|
||||
*
|
||||
* @param res response message to parse
|
||||
* @return value as string, NULL on error
|
||||
*/
|
||||
char* vici_parse_value_str(vici_res_t *res);
|
||||
|
||||
/**
|
||||
* Clean up a received response message.
|
||||
*
|
||||
* Event messages get cleaned up by the library, it is not allowed to call
|
||||
* vici_free_res() from within a vici_event_cb_t.
|
||||
*
|
||||
* @param res response message to free
|
||||
*/
|
||||
void vici_free_res(vici_res_t *res);
|
||||
|
||||
/**
|
||||
* (Un-)Register for events of a given kind.
|
||||
*
|
||||
* Events callbacks get invoked by a different thread from the libstrongswan
|
||||
* thread pool. On failure, errno is set appropriately.
|
||||
*
|
||||
* @param conn connection context
|
||||
* @param name name of event messages to register to
|
||||
* @param cb callback function to register, NULL to unregister
|
||||
* @param user user data passed to callback invocations
|
||||
* @return 0 if registered successfully
|
||||
*/
|
||||
int vici_register(vici_conn_t *conn, char *name, vici_event_cb_t cb, void *user);
|
||||
|
||||
/**
|
||||
* Initialize libvici before first time use.
|
||||
*/
|
||||
void vici_init();
|
||||
|
||||
/**
|
||||
* Deinitialize libvici after use.
|
||||
*/
|
||||
void vici_deinit();
|
||||
|
||||
#endif /** LIBVICI_H_ @}*/
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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 <test_suite.h>
|
||||
|
||||
#include "../vici_dispatcher.h"
|
||||
#include "../libvici.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define URI "unix:///tmp/strongswan-vici-event-test"
|
||||
|
||||
static void event_cb(void *user, char *name, vici_res_t *ev)
|
||||
{
|
||||
int *count = (int*)user;
|
||||
|
||||
ck_assert_str_eq(name, "test");
|
||||
ck_assert(vici_parse(ev) == VICI_PARSE_KEY_VALUE);
|
||||
ck_assert_str_eq(vici_parse_name(ev), "key1");
|
||||
ck_assert_str_eq(vici_parse_value_str(ev), "value1");
|
||||
ck_assert(vici_parse(ev) == VICI_PARSE_END);
|
||||
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
START_TEST(test_event)
|
||||
{
|
||||
vici_dispatcher_t *dispatcher;
|
||||
vici_conn_t *conn;
|
||||
int count = 0;
|
||||
|
||||
lib->processor->set_threads(lib->processor, 8);
|
||||
|
||||
dispatcher = vici_dispatcher_create(URI);
|
||||
ck_assert(dispatcher);
|
||||
|
||||
dispatcher->manage_event(dispatcher, "test", TRUE);
|
||||
|
||||
vici_init();
|
||||
conn = vici_connect(URI);
|
||||
ck_assert(conn);
|
||||
|
||||
ck_assert(vici_register(conn, "test", event_cb, &count) == 0);
|
||||
ck_assert(vici_register(conn, "nonexistent", event_cb, &count) != 0);
|
||||
|
||||
dispatcher->raise_event(dispatcher, "test", vici_message_create_from_args(
|
||||
VICI_KEY_VALUE, "key1", chunk_from_str("value1"),
|
||||
VICI_END));
|
||||
|
||||
while (count == 0)
|
||||
{
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
vici_disconnect(conn);
|
||||
|
||||
dispatcher->manage_event(dispatcher, "test", FALSE);
|
||||
|
||||
lib->processor->cancel(lib->processor);
|
||||
dispatcher->destroy(dispatcher);
|
||||
|
||||
vici_deinit();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_stress)
|
||||
{
|
||||
vici_dispatcher_t *dispatcher;
|
||||
vici_conn_t *conn;
|
||||
int count = 0, i, total = 50;
|
||||
|
||||
lib->processor->set_threads(lib->processor, 8);
|
||||
|
||||
dispatcher = vici_dispatcher_create(URI);
|
||||
ck_assert(dispatcher);
|
||||
|
||||
dispatcher->manage_event(dispatcher, "test", TRUE);
|
||||
dispatcher->manage_event(dispatcher, "dummy", TRUE);
|
||||
|
||||
vici_init();
|
||||
conn = vici_connect(URI);
|
||||
ck_assert(conn);
|
||||
|
||||
vici_register(conn, "test", event_cb, &count);
|
||||
|
||||
for (i = 0; i < total; i++)
|
||||
{
|
||||
/* do some event re/deregistration in between */
|
||||
ck_assert(vici_register(conn, "dummy", event_cb, NULL) == 0);
|
||||
|
||||
dispatcher->raise_event(dispatcher, "test",
|
||||
vici_message_create_from_args(
|
||||
VICI_KEY_VALUE, "key1", chunk_from_str("value1"),
|
||||
VICI_END));
|
||||
|
||||
ck_assert(vici_register(conn, "dummy", NULL, NULL) == 0);
|
||||
}
|
||||
|
||||
while (count < total)
|
||||
{
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
vici_disconnect(conn);
|
||||
|
||||
dispatcher->manage_event(dispatcher, "test", FALSE);
|
||||
dispatcher->manage_event(dispatcher, "dummy", FALSE);
|
||||
|
||||
lib->processor->cancel(lib->processor);
|
||||
dispatcher->destroy(dispatcher);
|
||||
|
||||
vici_deinit();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *event_suite_create()
|
||||
{
|
||||
Suite *s;
|
||||
TCase *tc;
|
||||
|
||||
s = suite_create("vici events");
|
||||
|
||||
tc = tcase_create("single");
|
||||
tcase_add_test(tc, test_event);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("stress");
|
||||
tcase_add_test(tc, test_stress);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
return s;
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* 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 <test_suite.h>
|
||||
|
||||
#include "../vici_dispatcher.h"
|
||||
#include "../libvici.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define URI "unix:///tmp/strongswan-vici-request-test"
|
||||
|
||||
static void encode_section(vici_req_t *req)
|
||||
{
|
||||
vici_begin_section(req, "section1");
|
||||
vici_add_key_valuef(req, "key1", "value%u", 1);
|
||||
vici_add_key_value(req, "key2", "value2", strlen("value2"));
|
||||
vici_end_section(req);
|
||||
}
|
||||
|
||||
static void decode_section(vici_res_t *res)
|
||||
{
|
||||
char *str;
|
||||
int len;
|
||||
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_BEGIN_SECTION);
|
||||
ck_assert_str_eq(vici_parse_name(res), "section1");
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_KEY_VALUE);
|
||||
ck_assert_str_eq(vici_parse_name(res), "key1");
|
||||
ck_assert_str_eq(vici_parse_value_str(res), "value1");
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_KEY_VALUE);
|
||||
ck_assert_str_eq(vici_parse_name(res), "key2");
|
||||
str = vici_parse_value(res, &len);
|
||||
ck_assert(chunk_equals(chunk_from_str("value2"), chunk_create(str, len)));
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_END_SECTION);
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_END);
|
||||
}
|
||||
|
||||
static void encode_list(vici_req_t *req)
|
||||
{
|
||||
vici_begin_list(req, "list1");
|
||||
vici_add_list_item(req, "item1", strlen("item1"));
|
||||
vici_add_list_itemf(req, "item%u", 2);
|
||||
vici_end_list(req);
|
||||
}
|
||||
|
||||
static void decode_list(vici_res_t *res)
|
||||
{
|
||||
char *str;
|
||||
int len;
|
||||
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_BEGIN_LIST);
|
||||
ck_assert_str_eq(vici_parse_name(res), "list1");
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_LIST_ITEM);
|
||||
ck_assert_str_eq(vici_parse_value_str(res), "item1");
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_LIST_ITEM);
|
||||
str = vici_parse_value(res, &len);
|
||||
ck_assert(chunk_equals(chunk_from_str("item2"), chunk_create(str, len)));
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_END_LIST);
|
||||
ck_assert(vici_parse(res) == VICI_PARSE_END);
|
||||
}
|
||||
|
||||
static struct {
|
||||
void (*encode)(vici_req_t* req);
|
||||
void (*decode)(vici_res_t* res);
|
||||
} echo_tests[] = {
|
||||
{ encode_section, decode_section },
|
||||
{ encode_list, decode_list },
|
||||
};
|
||||
|
||||
static vici_message_t* echo_cb(void *user, char *name,
|
||||
u_int id, vici_message_t *request)
|
||||
{
|
||||
ck_assert_str_eq(name, "echo");
|
||||
ck_assert_int_eq((uintptr_t)user, 1);
|
||||
|
||||
return vici_message_create_from_enumerator(request->create_enumerator(request));
|
||||
}
|
||||
|
||||
START_TEST(test_echo)
|
||||
{
|
||||
vici_dispatcher_t *dispatcher;
|
||||
vici_conn_t *conn;
|
||||
vici_req_t *req;
|
||||
vici_res_t *res;
|
||||
|
||||
lib->processor->set_threads(lib->processor, 8);
|
||||
|
||||
dispatcher = vici_dispatcher_create(URI);
|
||||
ck_assert(dispatcher);
|
||||
|
||||
dispatcher->manage_command(dispatcher, "echo", echo_cb, (void*)(uintptr_t)1);
|
||||
|
||||
vici_init();
|
||||
conn = vici_connect(URI);
|
||||
ck_assert(conn);
|
||||
|
||||
req = vici_begin("echo");
|
||||
echo_tests[_i].encode(req);
|
||||
res = vici_submit(req, conn);
|
||||
ck_assert(res);
|
||||
echo_tests[_i].decode(res);
|
||||
vici_free_res(res);
|
||||
|
||||
vici_disconnect(conn);
|
||||
|
||||
dispatcher->manage_command(dispatcher, "echo", NULL, NULL);
|
||||
|
||||
lib->processor->cancel(lib->processor);
|
||||
dispatcher->destroy(dispatcher);
|
||||
|
||||
vici_deinit();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_missing)
|
||||
{
|
||||
vici_dispatcher_t *dispatcher;
|
||||
vici_conn_t *conn;
|
||||
vici_req_t *req;
|
||||
vici_res_t *res;
|
||||
|
||||
lib->processor->set_threads(lib->processor, 8);
|
||||
|
||||
dispatcher = vici_dispatcher_create(URI);
|
||||
ck_assert(dispatcher);
|
||||
|
||||
vici_init();
|
||||
conn = vici_connect(URI);
|
||||
ck_assert(conn);
|
||||
|
||||
req = vici_begin("nonexistent");
|
||||
encode_section(req);
|
||||
res = vici_submit(req, conn);
|
||||
ck_assert(res == NULL);
|
||||
|
||||
vici_disconnect(conn);
|
||||
|
||||
dispatcher->manage_command(dispatcher, "echo", NULL, NULL);
|
||||
|
||||
lib->processor->cancel(lib->processor);
|
||||
dispatcher->destroy(dispatcher);
|
||||
|
||||
vici_deinit();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void event_cb(void *user, char *name, vici_res_t *ev)
|
||||
{
|
||||
int *events = (int*)user;
|
||||
|
||||
(*events)++;
|
||||
}
|
||||
|
||||
START_TEST(test_stress)
|
||||
{
|
||||
vici_dispatcher_t *dispatcher;
|
||||
vici_conn_t *conn;
|
||||
vici_req_t *req;
|
||||
vici_res_t *res;
|
||||
int i, total = 50, events = 0;
|
||||
|
||||
lib->processor->set_threads(lib->processor, 8);
|
||||
|
||||
dispatcher = vici_dispatcher_create(URI);
|
||||
ck_assert(dispatcher);
|
||||
|
||||
dispatcher->manage_command(dispatcher, "echo", echo_cb, (void*)(uintptr_t)1);
|
||||
dispatcher->manage_event(dispatcher, "dummy", TRUE);
|
||||
|
||||
vici_init();
|
||||
conn = vici_connect(URI);
|
||||
ck_assert(conn);
|
||||
|
||||
for (i = 0; i < total; i++)
|
||||
{
|
||||
/* do some event management in between */
|
||||
ck_assert(vici_register(conn, "dummy", event_cb, &events) == 0);
|
||||
dispatcher->raise_event(dispatcher, "dummy",
|
||||
vici_message_create_from_args(
|
||||
VICI_KEY_VALUE, "key1", chunk_from_str("value1"),
|
||||
VICI_END));
|
||||
|
||||
req = vici_begin("echo");
|
||||
encode_section(req);
|
||||
res = vici_submit(req, conn);
|
||||
ck_assert(res);
|
||||
decode_section(res);
|
||||
vici_free_res(res);
|
||||
|
||||
ck_assert(vici_register(conn, "dummy", NULL, NULL) == 0);
|
||||
}
|
||||
|
||||
while (events < total)
|
||||
{
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
vici_disconnect(conn);
|
||||
|
||||
dispatcher->manage_command(dispatcher, "echo", NULL, NULL);
|
||||
dispatcher->manage_event(dispatcher, "dummy", FALSE);
|
||||
|
||||
lib->processor->cancel(lib->processor);
|
||||
dispatcher->destroy(dispatcher);
|
||||
|
||||
vici_deinit();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *request_suite_create()
|
||||
{
|
||||
Suite *s;
|
||||
TCase *tc;
|
||||
|
||||
s = suite_create("vici request");
|
||||
|
||||
tc = tcase_create("echo");
|
||||
tcase_add_loop_test(tc, test_echo, 0, countof(echo_tests));
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("missing");
|
||||
tcase_add_test(tc, test_missing);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("stress");
|
||||
tcase_add_test(tc, test_stress);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
return s;
|
||||
}
|
|
@ -15,3 +15,5 @@
|
|||
|
||||
TEST_SUITE(socket_suite_create)
|
||||
TEST_SUITE(message_suite_create)
|
||||
TEST_SUITE(request_suite_create)
|
||||
TEST_SUITE(event_suite_create)
|
||||
|
|
Loading…
Reference in New Issue