Commit f5a0f6cf authored by Daniel Salzman's avatar Daniel Salzman

utils: use new control interface

parent 6133d468
......@@ -226,8 +226,8 @@ src/knot/conf/tools.c
src/knot/conf/tools.h
src/knot/ctl/commands.c
src/knot/ctl/commands.h
src/knot/ctl/remote.c
src/knot/ctl/remote.h
src/knot/ctl/process.c
src/knot/ctl/process.h
src/knot/dnssec/context.c
src/knot/dnssec/context.h
src/knot/dnssec/nsec-chain.c
......@@ -466,8 +466,6 @@ src/utils/knotc/commands.h
src/utils/knotc/estimator.c
src/utils/knotc/estimator.h
src/utils/knotc/main.c
src/utils/knotc/remote.c
src/utils/knotc/remote.h
src/utils/knotd/main.c
src/utils/knsupdate/knsupdate_exec.c
src/utils/knsupdate/knsupdate_exec.h
......
......@@ -404,15 +404,21 @@ Configuration of the server control interface.
.ft C
control:
listen: STR
timeout: TIME
.ft P
.fi
.UNINDENT
.UNINDENT
.SS listen
.sp
A UNIX socket path where the server listens for remote control commands.
A UNIX socket path where the server listens for control commands.
.sp
\fIDefault:\fP \fI\%rundir\fP/knot.sock
.SS timeout
.sp
Maximum time the control socket operations can take. Set 0 for infinity.
.sp
\fIDefault:\fP 5
.SH REMOTE SECTION
.sp
Definitions of remote servers for outgoing connections (source of a zone
......
......@@ -46,7 +46,10 @@ The default configuration database, if exists, has a preference to the default
configuration file.
.TP
\fB\-s\fP, \fB\-\-socket\fP \fIpath\fP
Use a remote control UNIX socket path (default is \fB@run_dir@/knot.sock\fP).
Use a control UNIX socket path (default is \fB@run_dir@/knot.sock\fP).
.TP
\fB\-t\fP, \fB\-\-timeout\fP \fIseconds\fP
Use a control timeout in seconds. Set 0 for infinity (default is 5).
.TP
\fB\-f\fP, \fB\-\-force\fP
Forced operation. Overrides some checks.
......
......@@ -23,7 +23,10 @@ Parameters
configuration file.
**-s**, **--socket** *path*
Use a remote control UNIX socket path (default is :file:`@run_dir@/knot.sock`).
Use a control UNIX socket path (default is :file:`@run_dir@/knot.sock`).
**-t**, **--timeout** *seconds*
Use a control timeout in seconds. Set 0 for infinity (default is 5).
**-f**, **--force**
Forced operation. Overrides some checks.
......
......@@ -453,16 +453,26 @@ Configuration of the server control interface.
control:
listen: STR
timeout: TIME
.. _control_listen:
listen
------
A UNIX socket path where the server listens for remote control commands.
A UNIX socket path where the server listens for control commands.
*Default:* :ref:`rundir<server_rundir>`/knot.sock
.. _control_timeout:
timeout
-------
Maximum time the control socket operations can take. Set 0 for infinity.
*Default:* 5
.. _Remote section:
Remote section
......
......@@ -211,8 +211,6 @@ knotc_SOURCES = \
utils/knotc/commands.h \
utils/knotc/estimator.c \
utils/knotc/estimator.h \
utils/knotc/remote.c \
utils/knotc/remote.h \
utils/knotc/main.c
knotd_SOURCES = \
......@@ -245,8 +243,8 @@ libknotd_la_SOURCES = \
knot/conf/tools.h \
knot/ctl/commands.c \
knot/ctl/commands.h \
knot/ctl/remote.c \
knot/ctl/remote.h \
knot/ctl/process.c \
knot/ctl/process.h \
knot/dnssec/context.c \
knot/dnssec/context.h \
knot/dnssec/nsec-chain.c \
......
......@@ -98,6 +98,7 @@ static const yp_item_t desc_server[] = {
static const yp_item_t desc_control[] = {
{ C_LISTEN, YP_TSTR, YP_VSTR = { "knot.sock" } },
{ C_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 5, YP_STIME } },
{ C_COMMENT, YP_TSTR, YP_VNONE },
{ NULL }
};
......
......@@ -78,6 +78,7 @@
#define C_TCP_REPLY_TIMEOUT "\x11""tcp-reply-timeout"
#define C_TCP_WORKERS "\x0B""tcp-workers"
#define C_TIMER_DB "\x08""timer-db"
#define C_TIMEOUT "\x07""timeout"
#define C_TPL "\x08""template"
#define C_UDP_WORKERS "\x0B""udp-workers"
#define C_USER "\x04""user"
......
This diff is collapsed.
/* Copyright (C) 2015 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
......@@ -13,53 +13,80 @@
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 Server control commands.
*
* \addtogroup ctl
* @{
*/
#pragma once
#include "libknot/libknot.h"
#include "knot/server/server.h"
#define CMDARGS_ALLOC_BLOCK KNOT_WIRE_MAX_PKTSIZE
/*! Control commands. */
typedef enum {
CTL_NONE,
#define KNOT_CTL_STATUS "status"
#define KNOT_CTL_STOP "stop"
#define KNOT_CTL_RELOAD "reload"
CTL_STATUS,
CTL_STOP,
CTL_RELOAD,
#define KNOT_CTL_ZONE_STATUS "zone-status"
#define KNOT_CTL_ZONE_RELOAD "zone-reload"
#define KNOT_CTL_ZONE_REFRESH "zone-refresh"
#define KNOT_CTL_ZONE_RETRANSFER "zone-retransfer"
#define KNOT_CTL_ZONE_FLUSH "zone-flush"
#define KNOT_CTL_ZONE_SIGN "zone-sign"
CTL_ZONE_STATUS,
CTL_ZONE_RELOAD,
CTL_ZONE_REFRESH,
CTL_ZONE_RETRANSFER,
CTL_ZONE_FLUSH,
CTL_ZONE_SIGN,
#define KNOT_CTL_CONF_LIST "conf-list"
#define KNOT_CTL_CONF_READ "conf-read"
#define KNOT_CTL_CONF_BEGIN "conf-begin"
#define KNOT_CTL_CONF_COMMIT "conf-commit"
#define KNOT_CTL_CONF_ABORT "conf-abort"
#define KNOT_CTL_CONF_DIFF "conf-diff"
#define KNOT_CTL_CONF_GET "conf-get"
#define KNOT_CTL_CONF_SET "conf-set"
#define KNOT_CTL_CONF_UNSET "conf-unset"
CTL_CONF_LIST,
CTL_CONF_READ,
CTL_CONF_BEGIN,
CTL_CONF_COMMIT,
CTL_CONF_ABORT,
CTL_CONF_DIFF,
CTL_CONF_GET,
CTL_CONF_SET,
CTL_CONF_UNSET,
} ctl_cmd_t;
/*! \brief Remote command structure. */
/*! Control command parameters. */
typedef struct {
const knot_rrset_t *arg;
unsigned argc;
knot_rcode_t rc;
char *response;
size_t response_size;
size_t response_max;
} remote_cmdargs_t;
knot_ctl_t *ctl;
knot_ctl_type_t type;
knot_ctl_data_t data;
server_t *server;
} ctl_args_t;
/*! \brief Callback prototype for remote commands. */
typedef int (*remote_cmdf_t)(server_t *, remote_cmdargs_t *);
/*!
* Returns a string equivalent of the command.
*
* \param[in] cmd Command.
*
* \return Command string or NULL.
*/
const char* ctl_cmd_to_str(ctl_cmd_t cmd);
/*! \brief Remote command table item. */
typedef struct {
const char *name;
remote_cmdf_t f;
} remote_cmd_t;
/*!
* Returns a command corresponding to the string.
*
* \param[in] cmd_str Command string.
*
* \return Command.
*/
ctl_cmd_t ctl_str_to_cmd(const char *cmd_str);
/*!
* Executes a control command.
*
* \param[in] cmd Control command.
* \param[in] args Command arguments.
*
* \return Error code, KNOT_EOK if successful.
*/
int ctl_exec(ctl_cmd_t cmd, ctl_args_t *args);
/*! \brief Table of remote commands. */
extern const remote_cmd_t remote_cmd_tbl[];
/*! @} */
/* 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 "knot/common/log.h"
#include "knot/ctl/commands.h"
#include "knot/ctl/process.h"
#include "libknot/error.h"
int ctl_process(knot_ctl_t *ctl, server_t *server)
{
if (ctl == NULL || server == NULL) {
return KNOT_EINVAL;
}
ctl_args_t args = {
.ctl = ctl,
.type = KNOT_CTL_TYPE_END,
.server = server
};
// Strip redundant/unprocessed data units in the current block.
bool strip = false;
while (true) {
// Receive data unit.
int ret = knot_ctl_receive(args.ctl, &args.type, &args.data);
if (ret != KNOT_EOK) {
log_debug("control, failed to receive (%s)",
knot_strerror(ret));
return ret;
}
// Decide what to do.
switch (args.type) {
case KNOT_CTL_TYPE_DATA:
// Leading data unit with a command name.
if (!strip) {
// Set to strip unprocessed data unit.
strip = true;
break;
}
// FALLTHROUGH
case KNOT_CTL_TYPE_EXTRA:
// All non-first data units should be parsed in a callback.
// Ignore if probable previous error.
continue;
case KNOT_CTL_TYPE_BLOCK:
strip = false;
continue;
case KNOT_CTL_TYPE_END:
return KNOT_EOF;
default:
assert(0);
}
const char *cmd_name = args.data[KNOT_CTL_IDX_CMD];
ctl_cmd_t cmd = ctl_str_to_cmd(cmd_name);
if (cmd != CTL_NONE) {
log_info("control, received command '%s'", cmd_name);
} else if (cmd_name != NULL){
log_debug("control, invalid command '%s'", cmd_name);
continue;
} else {
log_debug("control, empty command");
continue;
}
// Execute the command.
int cmd_ret = ctl_exec(cmd, &args);
switch (cmd_ret) {
case KNOT_EOK:
strip = false;
case KNOT_CTL_ESTOP:
break;
default:
log_debug("control, command '%s' (%s)", cmd_name,
knot_strerror(cmd_ret));
break;
}
// Finalize the answer block.
ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
if (ret != KNOT_EOK) {
log_debug("control, failed to reply (%s)",
knot_strerror(ret));
}
// Stop if required.
if (cmd_ret == KNOT_CTL_ESTOP) {
// Finalize the answer message.
ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
if (ret != KNOT_EOK) {
log_debug("control, failed to reply (%s)",
knot_strerror(ret));
}
return cmd_ret;
}
}
}
/* Copyright (C) 2015 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
......@@ -12,9 +12,29 @@
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 Server control processing.
*
* \addtogroup ctl
* @{
*/
#pragma once
int cmd_remote(const char *socket, const char *cmd, uint16_t rrt,
int argc, char *argv[]);
#include "libknot/libknot.h"
#include "knot/server/server.h"
/*!
* Processes incoming control commands.
*
* \param[in] ctl Control context.
* \param[in] server Server instance.
*
* \return Error code, KNOT_EOK if successful.
*/
int ctl_process(knot_ctl_t *ctl, server_t *server);
/*! @} */
/* Copyright (C) 2015 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 <assert.h>
#include <urcu.h>
#include "dnssec/random.h"
#include "knot/common/log.h"
#include "knot/conf/conf.h"
#include "knot/ctl/commands.h"
#include "knot/ctl/remote.h"
#include "knot/server/tcp-handler.h"
#include "libknot/libknot.h"
#include "contrib/net.h"
#include "contrib/sockaddr.h"
#include "contrib/string.h"
#include "contrib/openbsd/strlcpy.h"
#include "contrib/wire.h"
#define KNOT_CTL_REALM "knot."
#define KNOT_CTL_REALM_EXT ("." KNOT_CTL_REALM)
#define CMDARGS_BUFLEN_LOG 256
/*! \brief Initialize cmdargs_t structure. */
static int cmdargs_init(remote_cmdargs_t *args)
{
assert(args);
char *response = malloc(CMDARGS_ALLOC_BLOCK);
if (!response) {
return KNOT_ENOMEM;
}
memset(args, 0, sizeof(*args));
args->response = response;
args->response_max = CMDARGS_ALLOC_BLOCK;
return KNOT_EOK;
}
/*! \brief Deinitialize cmdargs_t structure. */
static void cmdargs_deinit(remote_cmdargs_t *args)
{
assert(args);
free(args->response);
memset(args, 0, sizeof(*args));
}
int remote_bind(const char *path)
{
if (path == NULL) {
return KNOT_EINVAL;
}
log_info("remote control, binding to '%s'", path);
/* Prepare socket address. */
struct sockaddr_storage addr;
int ret = sockaddr_set(&addr, AF_UNIX, path, 0);
if (ret != KNOT_EOK) {
return ret;
}
/* Create new socket. */
int sock = net_bound_socket(SOCK_STREAM, &addr, 0);
if (sock < 0) {
log_error("remote control, failed to bind to '%s' (%s)",
path, knot_strerror(sock));
return sock;
}
/* Start listening. */
if (listen(sock, TCP_BACKLOG_SIZE) != 0) {
log_error("remote control, failed to listen on '%s'", path);
close(sock);
return knot_map_errno();
}
return sock;
}
void remote_unbind(int sock)
{
if (sock < 0) {
return;
}
/* Remove the control socket file. */
struct sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
if (getsockname(sock, (struct sockaddr *)&addr, &addr_len) == 0) {
char addr_str[SOCKADDR_STRLEN] = { 0 };
if (sockaddr_tostr(addr_str, sizeof(addr_str), &addr) > 0) {
(void)unlink(addr_str);
}
}
/* Close the socket. */
(void)close(sock);
}
int remote_recv(int sock, uint8_t *buf, size_t *buflen)
{
int c = tcp_accept(sock);
if (c < 0) {
return c;
}
/* Receive data. */
int n = net_dns_tcp_recv(c, buf, *buflen, -1);
*buflen = n;
if (n <= 0) {
close(c);
return KNOT_ECONNREFUSED;
}
return c;
}
int remote_parse(knot_pkt_t *pkt)
{
return knot_pkt_parse(pkt, 0);
}
static int remote_send_chunk(int c, knot_pkt_t *query, const char *d, uint16_t len,
int index)
{
knot_pkt_t *resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, &query->mm);
if (!resp) {
return KNOT_ENOMEM;
}
/* Initialize response. */
int ret = knot_pkt_init_response(resp, query);
if (ret != KNOT_EOK) {
goto failed;
}
/* Write to NS section. */
ret = knot_pkt_begin(resp, KNOT_AUTHORITY);
assert(ret == KNOT_EOK);
/* Create TXT RR with result. */
knot_rrset_t rr;
ret = remote_build_rr(&rr, "result.", KNOT_RRTYPE_TXT);
if (ret != KNOT_EOK) {
goto failed;
}
ret = remote_create_txt(&rr, d, len, index);
assert(ret == KNOT_EOK);
ret = knot_pkt_put(resp, 0, &rr, KNOT_PF_FREE);
if (ret != KNOT_EOK) {
knot_rrset_clear(&rr, NULL);
goto failed;
}
rcu_read_lock();
conf_val_t *val = &conf()->cache.srv_tcp_reply_timeout;
int timeout = conf_int(val) * 1000;
rcu_read_unlock();
ret = net_dns_tcp_send(c, resp->wire, resp->size, timeout);
failed:
/* Free packet. */
knot_pkt_free(&resp);
return ret;
}
static void log_command(const char *cmd, const remote_cmdargs_t *args)
{
char params[CMDARGS_BUFLEN_LOG] = { 0 };
size_t rest = CMDARGS_BUFLEN_LOG;
size_t pos = 0;
for (unsigned i = 0; i < args->argc; i++) {
const knot_rrset_t *rr = &args->arg[i];
if (rr->type != KNOT_RRTYPE_NS) {
continue;
}
uint16_t rr_count = rr->rrs.rr_count;
for (uint16_t j = 0; j < rr_count; j++) {
const knot_dname_t *dn = knot_ns_name(&rr->rrs, j);
char *name = knot_dname_to_str_alloc(dn);
int ret = snprintf(params + pos, rest, " %s", name);
free(name);
if (ret <= 0 || ret >= rest) {
break;
}
pos += ret;
rest -= ret;
}
}
log_info("remote control, received command '%s%s'", cmd, params);
}
int remote_answer(int sock, server_t *s, knot_pkt_t *pkt)
{
if (sock < 0 || s == NULL || pkt == NULL) {
return KNOT_EINVAL;
}
/* Prerequisites:
* QCLASS: CH
* QNAME: <CMD>.KNOT_CTL_REALM.
*/
const knot_dname_t *qname = knot_pkt_qname(pkt);
if (knot_pkt_qclass(pkt) != KNOT_CLASS_CH) {
return KNOT_EMALF;
}
knot_dname_t *realm = knot_dname_from_str_alloc(KNOT_CTL_REALM);
if (!knot_dname_is_sub(qname, realm) != 0) {
knot_dname_free(&realm, NULL);
return KNOT_EMALF;
}
knot_dname_free(&realm, NULL);
/* Command:
* QNAME: leftmost label of QNAME
</