Commit e47e9048 authored by Daniel Salzman's avatar Daniel Salzman

libknot: add server control interface

parent 143cf24f
......@@ -347,6 +347,8 @@ src/libknot/binary.h
src/libknot/codes.c
src/libknot/codes.h
src/libknot/consts.h
src/libknot/control/control.c
src/libknot/control/control.h
src/libknot/db/db.h
src/libknot/db/db_lmdb.c
src/libknot/db/db_lmdb.h
......@@ -497,6 +499,7 @@ tests/conf.c
tests/conf_tools.c
tests/confdb.c
tests/confio.c
tests/control.c
tests/db.c
tests/descriptor.c
tests/dname.c
......
......@@ -98,6 +98,7 @@ nobase_libknot_la_HEADERS = \
libknot/binary.h \
libknot/codes.h \
libknot/consts.h \
libknot/control/control.h \
libknot/descriptor.h \
libknot/dname.h \
libknot/dnssec/rrset-sign.h \
......@@ -141,6 +142,7 @@ nobase_libknot_la_HEADERS = \
libknot_la_SOURCES = \
libknot/binary.c \
libknot/codes.c \
libknot/control/control.c \
libknot/descriptor.c \
libknot/dname.c \
libknot/dnssec/rrset-sign.c \
......
This diff is collapsed.
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* \file
*
* \brief A server control interface.
*
* \addtogroup libknot
* @{
*/
#pragma once
/*! Control data item indexes. */
typedef enum {
KNOT_CTL_IDX_CMD = 0, /*!< Control command name. */
KNOT_CTL_IDX_FLAGS, /*!< Control command flags. */
KNOT_CTL_IDX_ERROR, /*!< Error message. */
KNOT_CTL_IDX_SECTION, /*!< Configuration section name. */
KNOT_CTL_IDX_ITEM, /*!< Configuration item name. */
KNOT_CTL_IDX_ID, /*!< Congiguration item identifier. */
KNOT_CTL_IDX_ZONE, /*!< Zone name. */
KNOT_CTL_IDX_OWNER, /*!< Zone record owner */
KNOT_CTL_IDX_TTL, /*!< Zone record TTL. */
KNOT_CTL_IDX_TYPE, /*!< Zone record type name. */
KNOT_CTL_IDX_DATA, /*!< Configuration item/zone record data. */
KNOT_CTL_IDX__COUNT, /*!< The number of data items. */
} knot_ctl_idx_t;
/*! Control unit types. */
typedef enum {
KNOT_CTL_TYPE_END, /*!< End of message, cache flushed. */
KNOT_CTL_TYPE_DATA, /*!< Data unit, cached. */
KNOT_CTL_TYPE_EXTRA, /*!< Extra value data unit, cached. */
KNOT_CTL_TYPE_BLOCK, /*!< End of data block, cache flushed. */
} knot_ctl_type_t;
/*! Control input/output string data. */
typedef const char* knot_ctl_data_t[KNOT_CTL_IDX__COUNT];
/*! A control context. */
struct knot_ctl;
typedef struct knot_ctl knot_ctl_t;
/*!
* Allocates a control context.
*
* \return Control context.
*/
knot_ctl_t* knot_ctl_alloc(void);
/*!
* Deallocates a control context.
*
* \param[in] ctx Control context.
*/
void knot_ctl_free(knot_ctl_t *ctx);
/*!
* Sets the timeout for socket operations.
*
* Default value is 5 seconds.
*
* \param[in] ctx Control context.
* \param[in] timeout Timeout in milliseconds (0 for infinity).
*/
void knot_ctl_set_timeout(knot_ctl_t *ctx, int timeout_ms);
/*!
* Binds a specified UNIX socket path.
*
* \note Server operation.
*
* \param[in] ctx Control context.
* \param[in] path Control UNIX socket path.
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_ctl_bind(knot_ctl_t *ctx, const char *path);
/*!
* Unbinds a control socket.
*
* \note Server operation.
*
* \param[in] ctx Control context.
*/
void knot_ctl_unbind(knot_ctl_t *ctx);
/*!
* Connects to a specified UNIX socket path.
*
* \note Client operation.
*
* \param[in] ctx Control context.
* \param[in] path Control UNIX socket path.
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_ctl_connect(knot_ctl_t *ctx, const char *path);
/*!
* Waits for an incomming connection.
*
* \note Server operation.
*
* \param[in] ctx Control context.
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_ctl_accept(knot_ctl_t *ctx);
/*!
* Closes the remote connections.
*
* \note Applies to both server and client.
*
* \param[in] ctx Control context.
*/
void knot_ctl_close(knot_ctl_t *ctx);
/*!
* Sends one control unit.
*
* \param[in] ctx Control context.
* \param[in] type Unit type to send.
* \param[in] data Data unit to send (optional, ignored if non-data type).
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_ctl_send(knot_ctl_t *ctx, knot_ctl_type_t type, knot_ctl_data_t *data);
/*!
* Receives one control unit.
*
* \param[in] ctx Control context.
* \param[out] type Received unit type.
* \param[out] data Received data unit (optional).
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_ctl_receive(knot_ctl_t *ctx, knot_ctl_type_t *type, knot_ctl_data_t *data);
/*! @} */
/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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
......@@ -40,6 +40,7 @@
#include "libknot/rrset.h"
#include "libknot/tsig-op.h"
#include "libknot/tsig.h"
#include "libknot/control/control.h"
#include "libknot/db/db.h"
#include "libknot/db/db_lmdb.h"
#include "libknot/db/db_trie.h"
......
......@@ -11,6 +11,7 @@ conf
conf_tools
confdb
confio
control
db
descriptor
dname
......
......@@ -19,6 +19,7 @@ check_PROGRAMS = \
conf_tools \
confdb \
confio \
control \
db \
descriptor \
dname \
......
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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 3 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <tap/basic.h>
#include <tap/files.h>
#define CTL_BUFF_SIZE 18
#include "libknot/control/control.c"
#define fake_ok(condition, msg, ...) \
if (!(condition)) { \
if (msg != NULL) { \
printf("error: " msg "\n", ##__VA_ARGS__); \
} \
exit(-1); \
}
static void ctl_client(const char *socket, size_t argc, knot_ctl_data_t *argv)
{
knot_ctl_t *ctl = knot_ctl_alloc();
fake_ok(ctl != NULL, "Allocate control");
int ret;
for (int i = 0; i < 20; i++) {
ret = knot_ctl_connect(ctl, socket);
if (ret == KNOT_EOK) {
break;
}
usleep(100000);
}
fake_ok(ret == KNOT_EOK, "Connect to socket");
diag("BEGIN: Client -> Server");
if (argc > 0) {
for (size_t i = 0; i < argc; i++) {
if (argv[i][KNOT_CTL_IDX_CMD] != NULL &&
argv[i][KNOT_CTL_IDX_CMD][0] == '\0') {
ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
fake_ok(ret == KNOT_EOK, "Client send data block end type");
} else {
ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &argv[i]);
fake_ok(ret == KNOT_EOK, "Client send data %zu", i);
}
}
}
ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
fake_ok(ret == KNOT_EOK, "Client send final data");
diag("END: Client -> Server");
diag("BEGIN: Client <- Server");
size_t count = 0;
knot_ctl_data_t data;
knot_ctl_type_t type;
while ((ret = knot_ctl_receive(ctl, &type, &data)) == KNOT_EOK) {
if (type == KNOT_CTL_TYPE_END) {
break;
}
if (argv[count][KNOT_CTL_IDX_CMD] != NULL &&
argv[count][KNOT_CTL_IDX_CMD][0] == '\0') {
fake_ok(type == KNOT_CTL_TYPE_BLOCK, "Receive block end type");
} else {
fake_ok(type == KNOT_CTL_TYPE_DATA, "Check data type");
for (size_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) {
fake_ok((data[i] == NULL && argv[count][i] == NULL) ||
(data[i] != NULL && argv[count][i] != NULL),
"Client compare input item occupation %zu", i);
if (data[i] == NULL) {
continue;
}
fake_ok(strcmp(data[i], argv[count][i]) == 0,
"Client compare input item '%s", argv[count][i]);
}
}
count++;
}
fake_ok(ret == KNOT_EOK, "Receive OK check");
fake_ok(type == KNOT_CTL_TYPE_END, "Receive EOF type");
fake_ok(count == argc, "Client compare input count '%zu'", argc);
diag("END: Client <- Server");
knot_ctl_close(ctl);
knot_ctl_free(ctl);
}
static void ctl_server(const char *socket, size_t argc, knot_ctl_data_t *argv)
{
knot_ctl_t *ctl = knot_ctl_alloc();
ok(ctl != NULL, "Allocate control");
int ret = knot_ctl_bind(ctl, socket);
ok(ret == KNOT_EOK, "Bind control socket");
ret = knot_ctl_accept(ctl);
ok(ret == KNOT_EOK, "Accept a connection");
diag("BEGIN: Server <- Client");
size_t count = 0;
knot_ctl_data_t data;
knot_ctl_type_t type;
while ((ret = knot_ctl_receive(ctl, &type, &data)) == KNOT_EOK) {
if (type == KNOT_CTL_TYPE_END) {
break;
}
if (argv[count][KNOT_CTL_IDX_CMD] != NULL &&
argv[count][KNOT_CTL_IDX_CMD][0] == '\0') {
ok(type == KNOT_CTL_TYPE_BLOCK, "Receive block end type");
} else {
ok(type == KNOT_CTL_TYPE_DATA, "Check data type");
for (size_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) {
ok((data[i] == NULL && argv[count][i] == NULL) ||
(data[i] != NULL && argv[count][i] != NULL),
"Server compare input item occupation %zu", i);
if (data[i] == NULL) {
continue;
}
ok(strcmp(data[i], argv[count][i]) == 0,
"Server compare input item '%s", argv[count][i]);
}
}
count++;
}
ok(ret == KNOT_EOK, "Receive OK check");
ok(type == KNOT_CTL_TYPE_END, "Receive EOF type");
ok(count == argc, "Server compare input count '%zu'", argc);
diag("END: Server <- Client");
diag("BEGIN: Server -> Client");
if (argc > 0) {
for (size_t i = 0; i < argc; i++) {
if (argv[i][KNOT_CTL_IDX_CMD] != NULL &&
argv[i][KNOT_CTL_IDX_CMD][0] == '\0') {
ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
ok(ret == KNOT_EOK, "Client send data block end type");
} else {
ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &argv[i]);
ok(ret == KNOT_EOK, "Server send data %zu", i);
}
}
}
ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
ok(ret == KNOT_EOK, "Server send final data");
diag("END: Server -> Client");
knot_ctl_close(ctl);
knot_ctl_unbind(ctl);
knot_ctl_free(ctl);
}
static void test_client_server_client(void)
{
char *socket = test_mktemp();
ok(socket != NULL, "Make a temporary socket file '%s'", socket);
size_t data_len = 5;
knot_ctl_data_t data[] = {
{ "command", "error", "section", "item", "identifier",
"zone", "owner", "ttl", "type", "data" },
{ [KNOT_CTL_IDX_DATA] = "\x01\x02" },
{ [KNOT_CTL_IDX_CMD] = "\0" }, // This means block end in this test!
{ NULL },
{ [KNOT_CTL_IDX_ERROR] = "Ultra long message" }
};
// Fork a client process.
pid_t child_pid = fork();
if (child_pid == -1) {
ok(child_pid >= 0, "Process fork");
return;
}
if (child_pid == 0) {
ctl_client(socket, data_len, data);
free(socket);
return;
} else {
ctl_server(socket, data_len, data);
}
int status;
wait(&status);
ok(status >= 0, "Wait for client");
test_rm_rf(socket);
free(socket);
}
int main(int argc, char *argv[])
{
plan_lazy();
diag("Client -> Server -> Client");
test_client_server_client();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment