diff --git a/Knot.files b/Knot.files index 39842935ad156f80ee351a0185318c75d6b3abc3..04b32171b993c95250e9802b714d4a187bab0cf6 100644 --- a/Knot.files +++ b/Knot.files @@ -77,7 +77,9 @@ src/contrib/sockaddr.c src/contrib/sockaddr.h src/contrib/string.c src/contrib/string.h +src/contrib/time.h src/contrib/tolower.h +src/contrib/trim.h src/contrib/ucw/array-sort.h src/contrib/ucw/binsearch.h src/contrib/ucw/heap.c @@ -241,8 +243,6 @@ src/knot/common/process.c src/knot/common/process.h src/knot/common/ref.c src/knot/common/ref.h -src/knot/common/time.h -src/knot/common/trim.h src/knot/conf/base.c src/knot/conf/base.h src/knot/conf/conf.c @@ -255,6 +255,8 @@ src/knot/conf/scheme.c src/knot/conf/scheme.h 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/dnssec/context.c diff --git a/src/Makefile.am b/src/Makefile.am index 78875fe869a185aeb83e513f46482fc0d274885e..8865614c97a85f29ce3b2a3365ff1f4e18116492 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -239,6 +239,8 @@ libknotd_la_SOURCES = \ knot/conf/scheme.h \ knot/conf/tools.c \ knot/conf/tools.h \ + knot/ctl/commands.c \ + knot/ctl/commands.h \ knot/ctl/remote.c \ knot/ctl/remote.h \ knot/dnssec/context.c \ diff --git a/src/knot/common/log.c b/src/knot/common/log.c index eb40e72de32d46330872e1b2c8daba51551cf660..0773aa96915e8f13120cc1b355e176652f052dc3 100644 --- a/src/knot/common/log.c +++ b/src/knot/common/log.c @@ -45,8 +45,9 @@ struct log_sink { uint8_t *facility; /* Log sinks. */ size_t facility_count; /* Sink count. */ - FILE** file; /* Open files. */ + FILE **file; /* Open files. */ ssize_t file_count; /* Nr of open files. */ + logflag_t flags; /* Formatting flags. */ }; /*! Log sink singleton. */ @@ -208,6 +209,11 @@ bool log_isopen() return s_log != NULL; } +void log_flag_set(logflag_t flag) +{ + s_log->flags |= flag; +} + /*! \brief Open file as a logging facility. */ static int log_open_file(struct log_sink *log, const char* filename) { @@ -278,13 +284,15 @@ static int emit_log_msg(int level, const char *zone, size_t zone_len, const char level = LOG_MASK(level); /* Prefix date and time. */ - char tstr[LOG_BUFLEN] = {0}; - struct tm lt; - struct timeval tv; - gettimeofday(&tv, NULL); - time_t sec = tv.tv_sec; - if (localtime_r(&sec, <) != NULL) { - strftime(tstr, sizeof(tstr), KNOT_LOG_TIME_FORMAT " ", <); + char tstr[LOG_BUFLEN] = { 0 }; + if (!log_isopen() || !(s_log->flags & LOG_FNO_TIMESTAMP)) { + struct tm lt; + struct timeval tv; + gettimeofday(&tv, NULL); + time_t sec = tv.tv_sec; + if (localtime_r(&sec, <) != NULL) { + strftime(tstr, sizeof(tstr), KNOT_LOG_TIME_FORMAT " ", <); + } } // Log streams @@ -364,10 +372,12 @@ static int log_msg_text(int level, const char *zone, const char *fmt, va_list ar size_t capacity = sizeof(sbuf) - 1; /* Prefix error level. */ - const char *prefix = level_prefix(level); - ret = log_msg_add(&write, &capacity, "%s: ", prefix); - if (ret != KNOT_EOK) { - return ret; + if (level != LOG_INFO || !log_isopen() || !(s_log->flags & LOG_FNO_INFO)) { + const char *prefix = level_prefix(level); + ret = log_msg_add(&write, &capacity, "%s: ", prefix); + if (ret != KNOT_EOK) { + return ret; + } } /* Prefix zone name. */ diff --git a/src/knot/common/log.h b/src/knot/common/log.h index 5cdf3708faacd4ed418d22f83cb4dc3b9790a581..be7e251aaa781813f7ae8bc509fd429c44ce3c9a 100644 --- a/src/knot/common/log.h +++ b/src/knot/common/log.h @@ -60,6 +60,12 @@ typedef enum { LOG_ANY = 7 /*!< Any module. */ } logsrc_t; +/*! \brief Logging format flags. */ +typedef enum { + LOG_FNO_TIMESTAMP = 1 << 0, /*!< Don't print timestamp prefix. */ + LOG_FNO_INFO = 1 << 1 /*!< Don't print info level prefix. */ +} logflag_t; + /*! \brief Format for timestamps in log files. */ #define KNOT_LOG_TIME_FORMAT "%Y-%m-%dT%H:%M:%S" @@ -83,6 +89,11 @@ void log_close(); */ bool log_isopen(); +/*! + * \brief Set logging format flag. + */ +void log_flag_set(logflag_t flag); + /*! * \brief Return log levels for a given facility. * diff --git a/src/knot/ctl/commands.c b/src/knot/ctl/commands.c new file mode 100644 index 0000000000000000000000000000000000000000..7c22edb61faa9468ac560f819c0c0f4658c2743a --- /dev/null +++ b/src/knot/ctl/commands.c @@ -0,0 +1,694 @@ +/* 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 "knot/common/log.h" +#include "knot/conf/confio.h" +#include "knot/ctl/commands.h" +#include "knot/ctl/remote.h" +#include "libknot/libknot.h" +#include "contrib/macros.h" +#include "contrib/string.h" + +/*! \brief Callback prototype for per-zone operations. */ +typedef int (remote_zonef_t)(zone_t *, remote_cmdargs_t *); + +/*! \brief Append data to the output buffer. */ +static int cmdargs_append(remote_cmdargs_t *args, const char *data, size_t size) +{ + assert(args); + assert(size <= CMDARGS_ALLOC_BLOCK); + + if (args->response_size + size >= args->response_max) { + size_t new_max = args->response_max + CMDARGS_ALLOC_BLOCK; + char *new_response = realloc(args->response, new_max); + if (!new_response) { + return KNOT_ENOMEM; + } + + args->response = new_response; + args->response_max = new_max; + } + + memcpy(args->response + args->response_size, data, size); + args->response_size += size; + args->response[args->response_size] = '\0'; + + return KNOT_EOK; +} + +static void zone_apply(server_t *s, remote_cmdargs_t *a, remote_zonef_t *cb, + const knot_dname_t *dname) +{ + int ret = KNOT_ENOZONE; + + zone_t *zone = knot_zonedb_find(s->zone_db, dname); + if (zone != NULL) { + ret = cb(zone, a); + } + + if (ret != KNOT_EOK) { + char name[KNOT_DNAME_TXT_MAXLEN] = ""; + knot_dname_to_str(name, dname, sizeof(name)); + + char *msg = sprintf_alloc("%signoring [%s] %s", + (a->response_size > 0) ? "\n" : "", + name, knot_strerror(ret)); + cmdargs_append(a, msg, strlen(msg)); + free(msg); + } +} + +/*! \brief Apply callback to all zones specified by RDATA. */ +static int zones_apply(server_t *s, remote_cmdargs_t *a, remote_zonef_t *cb) +{ + assert(s); + assert(a); + assert(cb); + + rcu_read_lock(); + + /* Process all configured zones if none is specified. */ + if (a->argc == 0) { + knot_zonedb_foreach(s->zone_db, cb, a); + } else { + /* Process all specified zones. */ + for (unsigned i = 0; i < a->argc; i++) { + const knot_rrset_t *rr = &a->arg[i]; + if (rr->type != KNOT_RRTYPE_NS) { + continue; + } + + for (unsigned j = 0; j < rr->rrs.rr_count; j++) { + zone_apply(s, a, cb, knot_ns_name(&rr->rrs, j)); + } + } + } + + rcu_read_unlock(); + + return KNOT_EOK; +} + +static char *dnssec_info(const zone_t *zone, char *buf, size_t buf_size) +{ + assert(zone); + assert(buf); + + time_t refresh_at = zone_events_get_time(zone, ZONE_EVENT_DNSSEC); + struct tm time_gm = { 0 }; + + gmtime_r(&refresh_at, &time_gm); + size_t written = strftime(buf, buf_size, KNOT_LOG_TIME_FORMAT, &time_gm); + if (written == 0) { + return NULL; + } + + return buf; +} + +static int zone_status(zone_t *zone, remote_cmdargs_t *a) +{ + /* Fetch latest serial. */ + uint32_t serial = 0; + if (zone->contents) { + const knot_rdataset_t *soa_rrs = node_rdataset(zone->contents->apex, + KNOT_RRTYPE_SOA); + assert(soa_rrs != NULL); + serial = knot_soa_serial(soa_rrs); + } + + /* Fetch next zone event. */ + char when[128] = { '\0' }; + zone_event_type_t next_type = ZONE_EVENT_INVALID; + const char *next_name = ""; + if (next_type != ZONE_EVENT_INVALID) { + next_name = zone_events_get_name(next_type); + time_t next_time = zone_events_get_next(zone, &next_type); + next_time = next_time - time(NULL); + if (next_time < 0) { + memcpy(when, "pending", strlen("pending")); + } else if (snprintf(when, sizeof(when), + "in %lldh%lldm%llds", + (long long)(next_time / 3600), + (long long)(next_time % 3600) / 60, + (long long)(next_time % 60)) < 0) { + return KNOT_ESPACE; + } + } else { + memcpy(when, "idle", strlen("idle")); + } + + /* Prepare zone info. */ + char buf[512] = { '\0' }; + char dnssec_buf[128] = { '\0' }; + char *zone_name = knot_dname_to_str_alloc(zone->name); + + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name); + bool dnssec_enable = conf_bool(&val); + bool is_slave = zone_is_slave(zone); + + int n = snprintf(buf, sizeof(buf), + "%s%s\ttype=%s | serial=%u | %s %s | %s %s", + (a->response_size > 0) ? "\n" : "", + zone_name, + is_slave ? "slave" : "master", + serial, + next_name, + when, + dnssec_enable ? "automatic DNSSEC, resigning at:" : "DNSSEC signing disabled", + dnssec_enable ? dnssec_info(zone, dnssec_buf, sizeof(dnssec_buf)) : ""); + free(zone_name); + if (n < 0 || n >= sizeof(buf)) { + return KNOT_ESPACE; + } + + return cmdargs_append(a, buf, n); +} + +static int zone_reload(zone_t *zone, remote_cmdargs_t *a) +{ + UNUSED(a); + + if (zone->flags & ZONE_EXPIRED) { + return KNOT_ENOTSUP; + } + + zone_events_schedule(zone, ZONE_EVENT_LOAD, ZONE_EVENT_NOW); + + return KNOT_EOK; +} + +static int zone_refresh(zone_t *zone, remote_cmdargs_t *a) +{ + UNUSED(a); + + if (!zone_is_slave(zone)) { + return KNOT_ENOTSUP; + } + + zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW); + + return KNOT_EOK; +} + +static int zone_retransfer(zone_t *zone, remote_cmdargs_t *a) +{ + UNUSED(a); + + if (!zone_is_slave(zone)) { + return KNOT_ENOTSUP; + } + + zone->flags |= ZONE_FORCE_AXFR; + zone_events_schedule(zone, ZONE_EVENT_XFER, ZONE_EVENT_NOW); + + return KNOT_EOK; +} + +static int zone_flush(zone_t *zone, remote_cmdargs_t *a) +{ + UNUSED(a); + + zone->flags |= ZONE_FORCE_FLUSH; + zone_events_schedule(zone, ZONE_EVENT_FLUSH, ZONE_EVENT_NOW); + + return KNOT_EOK; +} + +static int zone_sign(zone_t *zone, remote_cmdargs_t *a) +{ + UNUSED(a); + + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name); + if (!conf_bool(&val)) { + return KNOT_ENOTSUP; + } + + zone->flags |= ZONE_FORCE_RESIGN; + zone_events_schedule(zone, ZONE_EVENT_DNSSEC, ZONE_EVENT_NOW); + + return KNOT_EOK; +} + +static int ctl_status(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(s); + + const char *msg = "Running"; + cmdargs_append(a, msg, strlen(msg)); + + return KNOT_EOK; +} + +static int ctl_stop(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(s); + UNUSED(a); + + return KNOT_CTL_ESTOP; +} + +static int ctl_reload(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(s); + UNUSED(a); + + return server_reload(s, conf()->filename); +} + +static int ctl_zone_status(server_t *s, remote_cmdargs_t *a) +{ + return zones_apply(s, a, zone_status); +} + +static int ctl_zone_reload(server_t *s, remote_cmdargs_t *a) +{ + return zones_apply(s, a, zone_reload); +} + +static int ctl_zone_refresh(server_t *s, remote_cmdargs_t *a) +{ + return zones_apply(s, a, zone_refresh); +} + +static int ctl_zone_retransfer(server_t *s, remote_cmdargs_t *a) +{ + return zones_apply(s, a, zone_retransfer); +} + +static int ctl_zone_flush(server_t *s, remote_cmdargs_t *a) +{ + return zones_apply(s, a, zone_flush); +} + +static int ctl_zone_sign(server_t *s, remote_cmdargs_t *a) +{ + return zones_apply(s, a, zone_sign); +} + +static int format_item(conf_io_t *io) +{ + remote_cmdargs_t *a = (remote_cmdargs_t *)io->misc; + + // Get possible error message. + const char *err = io->error.str; + if (err == NULL && io->error.code != KNOT_EOK) { + err = knot_strerror(io->error.code); + } + + // Get the item key and data strings. + char *key = conf_io_txt_key(io); + if (key == NULL) { + return KNOT_ERROR; + } + char *data = conf_io_txt_data(io); + + // Format the item. + char *item = sprintf_alloc( + "%s%s%s%s%s%s%s", + (a->response_size > 0 ? "\n" : ""), + (err != NULL ? "Error (" : ""), + (err != NULL ? err : ""), + (err != NULL ? "): " : ""), + key, + (data != NULL ? " = " : ""), + (data != NULL ? data : "")); + free(key); + free(data); + if (item == NULL) { + return KNOT_ENOMEM; + } + + // Append the item. + int ret = cmdargs_append(a, item, strlen(item)); + free(item); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +static int ctl_conf_begin(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(s); + UNUSED(a); + + return conf_io_begin(false); +} + +static int ctl_conf_commit(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(a); + + conf_io_t io = { + .fcn = format_item, + .misc = a + }; + + // First check the database. + int ret = conf_io_check(&io); + if (ret != KNOT_EOK) { + (void)conf_io_abort(false); + return ret; + } + + ret = conf_io_commit(false); + if (ret != KNOT_EOK) { + (void)conf_io_abort(false); + return ret; + } + + return server_reload(s, NULL); +} + +static int ctl_conf_abort(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(s); + UNUSED(a); + + return conf_io_abort(false); +} + +static int parse_conf_key(char *key, char **key0, char **id, char **key1) +{ + // Check for the empty argument. + if (key == NULL) { + *key0 = NULL; + *key1 = NULL; + *id = NULL; + return KNOT_EOK; + } + + // Get key0. + char *_key0 = key; + + // Check for id. + char *_id = strchr(key, '['); + if (_id != NULL) { + // Separate key0 and id. + *_id++ = '\0'; + + // Check for id end. + char *id_end = _id; + while ((id_end = strchr(id_end, ']')) != NULL) { + // Check for escaped character. + if (*(id_end - 1) != '\\') { + break; + } + id_end++; + } + + // Check for unclosed id. + if (id_end == NULL) { + return KNOT_EINVAL; + } + + // Separate id and key1. + *id_end = '\0'; + + key = id_end + 1; + + // Key1 or nothing must follow. + if (*key != '.' && *key != '\0') { + return KNOT_EINVAL; + } + } + + // Check for key1. + char *_key1 = strchr(key, '.'); + if (_key1 != NULL) { + // Separate key0/id and key1. + *_key1++ = '\0'; + + if (*_key1 == '\0') { + return KNOT_EINVAL; + } + } + + *key0 = _key0; + *key1 = _key1; + *id = _id; + + return KNOT_EOK; +} + +static int ctl_conf_list(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(s); + + if (a->argc > 1) { + return KNOT_EINVAL; + } + + char *key = (a->argc == 1) ? + (char *)remote_get_txt(&a->arg[0], 0, NULL) : NULL; + + // Split key path. + char *key0, *key1, *id; + int ret = parse_conf_key(key, &key0, &id, &key1); + if (ret != KNOT_EOK) { + free(key); + return ret; + } + + if (key1 != NULL || id != NULL) { + free(key); + return KNOT_EINVAL; + } + + conf_io_t io = { + .fcn = format_item, + .misc = a + }; + + // Get items. + ret = conf_io_list(key0, &io); + + free(key); + + return ret; +} + +static int ctl_conf_diff(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(s); + + if (a->argc > 1) { + return KNOT_EINVAL; + } + + char *key = (a->argc == 1) ? + (char *)remote_get_txt(&a->arg[0], 0, NULL) : NULL; + + // Split key path. + char *key0, *key1, *id; + int ret = parse_conf_key(key, &key0, &id, &key1); + if (ret != KNOT_EOK) { + free(key); + return ret; + } + + conf_io_t io = { + .fcn = format_item, + .misc = a + }; + + // Get the difference. + ret = conf_io_diff(key0, key1, id, &io); + + free(key); + + return ret; +} + +static int conf_read(server_t *s, remote_cmdargs_t *a, bool get_current) +{ + UNUSED(s); + + if (a->argc > 1) { + return KNOT_EINVAL; + } + + char *key = (a->argc == 1) ? + (char *)remote_get_txt(&a->arg[0], 0, NULL) : NULL; + + // Split key path. + char *key0, *key1, *id; + int ret = parse_conf_key(key, &key0, &id, &key1); + if (ret != KNOT_EOK) { + free(key); + return ret; + } + + conf_io_t io = { + .fcn = format_item, + .misc = a + }; + + // Get item(s) value. + ret = conf_io_get(key0, key1, id, get_current, &io); + + free(key); + + return ret; +} + +static int ctl_conf_read(server_t *s, remote_cmdargs_t *a) +{ + return conf_read(s, a, true); +} + +static int ctl_conf_get(server_t *s, remote_cmdargs_t *a) +{ + return conf_read(s, a, false); +} + +static int ctl_conf_set(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(s); + + if (a->argc < 1 || a->argc > 255) { + return KNOT_EINVAL; + } + + char *key = (char *)remote_get_txt(&a->arg[0], 0, NULL); + + // Split key path. + char *key0, *key1, *id; + int ret = parse_conf_key(key, &key0, &id, &key1); + if (ret != KNOT_EOK) { + free(key); + return ret; + } + + conf_io_t io = { + .fcn = format_item, + .misc = a + }; + + // Start child transaction. + ret = conf_io_begin(true); + if (ret != KNOT_EOK) { + free(key); + return ret; + } + + // Add item with no data. + if (a->argc == 1) { + ret = conf_io_set(key0, key1, id, NULL, &io); + // Add item with specified data. + } else { + for (int i = 1; i < a->argc; i++) { + char *data = (char *)remote_get_txt(&a->arg[i], 0, NULL); + ret = conf_io_set(key0, key1, id, data, &io); + free(data); + if (ret != KNOT_EOK) { + break; + } + } + } + + free(key); + + // Finish child transaction. + if (ret == KNOT_EOK) { + return conf_io_commit(true); + } else { + (void)conf_io_abort(true); + return ret; + } +} + +static int ctl_conf_unset(server_t *s, remote_cmdargs_t *a) +{ + UNUSED(s); + + if (a->argc > 255) { + return KNOT_EINVAL; + } + + char *key = (a->argc >= 1) ? + (char *)remote_get_txt(&a->arg[0], 0, NULL) : NULL; + + // Split key path. + char *key0, *key1, *id; + int ret = parse_conf_key(key, &key0, &id, &key1); + if (ret != KNOT_EOK) { + free(key); + return ret; + } + + // Start child transaction. + ret = conf_io_begin(true); + if (ret != KNOT_EOK) { + free(key); + return ret; + } + + // Delete item with no data. + if (a->argc <= 1) { + ret = conf_io_unset(key0, key1, id, NULL); + // Delete specified data. + } else { + for (int i = 1; i < a->argc; i++) { + char *data = (char *)remote_get_txt(&a->arg[i], 0, NULL); + ret = conf_io_unset(key0, key1, id, data); + free(data); + if (ret != KNOT_EOK) { + break; + } + } + } + + free(key); + + // Finish child transaction. + if (ret == KNOT_EOK) { + return conf_io_commit(true); + } else { + (void)conf_io_abort(true); + return ret; + } +} + +/*! \brief Table of remote commands. */ +const remote_cmd_t remote_cmd_tbl[] = { + { KNOT_CTL_STATUS, ctl_status }, + { KNOT_CTL_STOP, ctl_stop }, + { KNOT_CTL_RELOAD, ctl_reload }, + + { KNOT_CTL_ZONE_STATUS, ctl_zone_status }, + { KNOT_CTL_ZONE_RELOAD, ctl_zone_reload }, + { KNOT_CTL_ZONE_REFRESH, ctl_zone_refresh }, + { KNOT_CTL_ZONE_RETRANSFER, ctl_zone_retransfer }, + { KNOT_CTL_ZONE_FLUSH, ctl_zone_flush }, + { KNOT_CTL_ZONE_SIGN, ctl_zone_sign }, + + { KNOT_CTL_CONF_LIST, ctl_conf_list }, + { KNOT_CTL_CONF_READ, ctl_conf_read }, + { KNOT_CTL_CONF_BEGIN, ctl_conf_begin }, + { KNOT_CTL_CONF_COMMIT, ctl_conf_commit }, + { KNOT_CTL_CONF_ABORT, ctl_conf_abort }, + { KNOT_CTL_CONF_DIFF, ctl_conf_diff }, + { KNOT_CTL_CONF_GET, ctl_conf_get }, + { KNOT_CTL_CONF_SET, ctl_conf_set }, + { KNOT_CTL_CONF_UNSET, ctl_conf_unset }, + { NULL } +}; diff --git a/src/knot/ctl/commands.h b/src/knot/ctl/commands.h new file mode 100644 index 0000000000000000000000000000000000000000..317e8908e773f283a1222dd13fc2e876e5186769 --- /dev/null +++ b/src/knot/ctl/commands.h @@ -0,0 +1,65 @@ +/* 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/>. + */ + +#pragma once + +#include "libknot/libknot.h" +#include "knot/server/server.h" + +#define CMDARGS_ALLOC_BLOCK KNOT_WIRE_MAX_PKTSIZE + +#define KNOT_CTL_STATUS "status" +#define KNOT_CTL_STOP "stop" +#define KNOT_CTL_RELOAD "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" + +#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" + +/*! \brief Remote command structure. */ +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; + +/*! \brief Callback prototype for remote commands. */ +typedef int (*remote_cmdf_t)(server_t *, remote_cmdargs_t *); + +/*! \brief Remote command table item. */ +typedef struct { + const char *name; + remote_cmdf_t f; +} remote_cmd_t; + +/*! \brief Table of remote commands. */ +extern const remote_cmd_t remote_cmd_tbl[]; diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c index 533aa03904fc70c20dee2ba43d3af1f4937c5099..882554973cfa18f1cbd88fa7d154caa227cbbaec 100644 --- a/src/knot/ctl/remote.c +++ b/src/knot/ctl/remote.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* 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 @@ -18,19 +18,12 @@ #include <urcu.h> #include "dnssec/random.h" -#include "knot/common/fdset.h" #include "knot/common/log.h" #include "knot/conf/conf.h" -#include "knot/conf/confdb.h" -#include "knot/conf/confio.h" +#include "knot/ctl/commands.h" #include "knot/ctl/remote.h" -#include "knot/dnssec/zone-sign.h" -#include "knot/dnssec/zone-nsec.h" #include "knot/server/tcp-handler.h" -#include "knot/zone/timers.h" #include "libknot/libknot.h" -#include "libknot/yparser/yptrafo.h" -#include "contrib/macros.h" #include "contrib/net.h" #include "contrib/sockaddr.h" #include "contrib/string.h" @@ -39,19 +32,8 @@ #define KNOT_CTL_REALM "knot." #define KNOT_CTL_REALM_EXT ("." KNOT_CTL_REALM) -#define CMDARGS_ALLOC_BLOCK KNOT_WIRE_MAX_PKTSIZE #define CMDARGS_BUFLEN_LOG 256 -/*! \brief Remote command structure. */ -typedef struct remote_cmdargs { - const knot_rrset_t *arg; - unsigned argc; - knot_rcode_t rc; - char *response; - size_t response_size; - size_t response_max; -} remote_cmdargs_t; - /*! \brief Initialize cmdargs_t structure. */ static int cmdargs_init(remote_cmdargs_t *args) { @@ -69,30 +51,6 @@ static int cmdargs_init(remote_cmdargs_t *args) return KNOT_EOK; } -/*! \brief Append data to the output buffer. */ -static int cmdargs_append(remote_cmdargs_t *args, const char *data, size_t size) -{ - assert(args); - assert(size <= CMDARGS_ALLOC_BLOCK); - - if (args->response_size + size >= args->response_max) { - size_t new_max = args->response_max + CMDARGS_ALLOC_BLOCK; - char *new_response = realloc(args->response, new_max); - if (!new_response) { - return KNOT_ENOMEM; - } - - args->response = new_response; - args->response_max = new_max; - } - - memcpy(args->response + args->response_size, data, size); - args->response_size += size; - args->response[args->response_size] = '\0'; - - return KNOT_EOK; -} - /*! \brief Deinitialize cmdargs_t structure. */ static void cmdargs_deinit(remote_cmdargs_t *args) { @@ -102,804 +60,6 @@ static void cmdargs_deinit(remote_cmdargs_t *args) memset(args, 0, sizeof(*args)); } -/*! \brief Callback prototype for remote commands. */ -typedef int (*remote_cmdf_t)(server_t *, remote_cmdargs_t *); - -/*! \brief Callback prototype for per-zone operations. */ -typedef int (remote_zonef_t)(zone_t *, remote_cmdargs_t *); - -/*! \brief Remote command table item. */ -typedef struct remote_cmd { - const char *name; - remote_cmdf_t f; -} remote_cmd_t; - -/* Forward decls. */ -static int remote_c_stop(server_t *s, remote_cmdargs_t *a); -static int remote_c_reload(server_t *s, remote_cmdargs_t *a); -static int remote_c_refresh(server_t *s, remote_cmdargs_t *a); -static int remote_c_retransfer(server_t *s, remote_cmdargs_t *a); -static int remote_c_status(server_t *s, remote_cmdargs_t *a); -static int remote_c_zonestatus(server_t *s, remote_cmdargs_t *a); -static int remote_c_flush(server_t *s, remote_cmdargs_t *a); -static int remote_c_signzone(server_t *s, remote_cmdargs_t *a); -static int remote_c_conf_begin(server_t *s, remote_cmdargs_t *a); -static int remote_c_conf_commit(server_t *s, remote_cmdargs_t *a); -static int remote_c_conf_abort(server_t *s, remote_cmdargs_t *a); -static int remote_c_conf_desc(server_t *s, remote_cmdargs_t *a); -static int remote_c_conf_diff(server_t *s, remote_cmdargs_t *a); -static int remote_c_conf_read(server_t *s, remote_cmdargs_t *a); -static int remote_c_conf_get(server_t *s, remote_cmdargs_t *a); -static int remote_c_conf_set(server_t *s, remote_cmdargs_t *a); -static int remote_c_conf_unset(server_t *s, remote_cmdargs_t *a); - -/*! \brief Table of remote commands. */ -struct remote_cmd remote_cmd_tbl[] = { - { "stop", &remote_c_stop }, - { "reload", &remote_c_reload }, - { "refresh", &remote_c_refresh }, - { "retransfer", &remote_c_retransfer }, - { "status", &remote_c_status }, - { "zonestatus", &remote_c_zonestatus }, - { "flush", &remote_c_flush }, - { "signzone", &remote_c_signzone }, - { "conf-begin", &remote_c_conf_begin }, - { "conf-commit", &remote_c_conf_commit }, - { "conf-abort", &remote_c_conf_abort }, - { "conf-desc", &remote_c_conf_desc }, - { "conf-diff", &remote_c_conf_diff }, - { "conf-read", &remote_c_conf_read }, - { "conf-get", &remote_c_conf_get }, - { "conf-set", &remote_c_conf_set }, - { "conf-unset", &remote_c_conf_unset }, - { NULL } -}; - -/* Private APIs. */ - -/*! \brief Apply callback to all zones specified by RDATA of NS RRs. */ -static int remote_rdata_apply(server_t *s, remote_cmdargs_t *a, remote_zonef_t *cb) -{ - if (!s || !a || !cb) { - return KNOT_EINVAL; - } - - zone_t *zone = NULL; - int ret = KNOT_EOK; - - for (unsigned i = 0; i < a->argc; ++i) { - /* Process all zones in data section. */ - const knot_rrset_t *rr = &a->arg[i]; - if (rr->type != KNOT_RRTYPE_NS) { - continue; - } - - uint16_t rr_count = rr->rrs.rr_count; - for (uint16_t i = 0; i < rr_count; i++) { - const knot_dname_t *dn = knot_ns_name(&rr->rrs, i); - zone = knot_zonedb_find(s->zone_db, dn); - if (zone == NULL) { - char zname[KNOT_DNAME_MAXLEN]; - knot_dname_to_str(zname, dn, KNOT_DNAME_MAXLEN); - log_warning("remote control, zone %s not found.", - zname); - } else if (cb(zone, a) != KNOT_EOK) { - a->rc = KNOT_RCODE_SERVFAIL; - } - } - } - - return ret; -} - -/*! \brief Zone refresh callback. */ -static int remote_zone_refresh(zone_t *zone, remote_cmdargs_t *a) -{ - UNUSED(a); - - rcu_read_lock(); - bool is_slave = zone_is_slave(zone); - rcu_read_unlock(); - - if (!is_slave) { - return KNOT_EINVAL; - } - - zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW); - return KNOT_EOK; -} - -/*! \brief Zone reload callback. */ -static int remote_zone_reload(zone_t *zone, remote_cmdargs_t *a) -{ - UNUSED(a); - - if (zone->flags & ZONE_EXPIRED) { - log_zone_warning(zone->name, "cannot reload expired zone"); - return KNOT_EOK; - } - - zone_events_schedule(zone, ZONE_EVENT_LOAD, ZONE_EVENT_NOW); - return KNOT_EOK; -} - -/*! \brief Zone refresh callback. */ -static int remote_zone_retransfer(zone_t *zone, remote_cmdargs_t *a) -{ - UNUSED(a); - - rcu_read_lock(); - bool is_slave = zone_is_slave(zone); - rcu_read_unlock(); - - if (!is_slave) { - return KNOT_EINVAL; - } - - zone->flags |= ZONE_FORCE_AXFR; - zone_events_schedule(zone, ZONE_EVENT_XFER, ZONE_EVENT_NOW); - return KNOT_EOK; -} - -/*! \brief Zone flush callback. */ -static int remote_zone_flush(zone_t *zone, remote_cmdargs_t *a) -{ - UNUSED(a); - - zone->flags |= ZONE_FORCE_FLUSH; - zone_events_schedule(zone, ZONE_EVENT_FLUSH, ZONE_EVENT_NOW); - return KNOT_EOK; -} - -/*! \brief Sign zone callback. */ -static int remote_zone_sign(zone_t *zone, remote_cmdargs_t *a) -{ - UNUSED(a); - - rcu_read_lock(); - conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name); - bool dnssec_enable = conf_bool(&val); - rcu_read_unlock(); - - if (!dnssec_enable) { - return KNOT_EINVAL; - } - - zone->flags |= ZONE_FORCE_RESIGN; - zone_events_schedule(zone, ZONE_EVENT_DNSSEC, ZONE_EVENT_NOW); - return KNOT_EOK; -} - -/*! - * \brief Remote command 'stop' handler. - * - * QNAME: stop - * DATA: NULL - */ -static int remote_c_stop(server_t *s, remote_cmdargs_t *a) -{ - UNUSED(a); - UNUSED(s); - return KNOT_CTL_STOP; -} - -/*! - * \brief Remote command 'reload' handler. - * - * QNAME: reload - * DATA: NONE for all zones - * NS RRs with zones in RDATA - */ -static int remote_c_reload(server_t *s, remote_cmdargs_t *a) -{ - int ret = KNOT_EOK; - - if (a->argc == 0) { - /* Reload all. */ - ret = server_reload(s, conf()->filename); - } else { - rcu_read_lock(); - /* Reload specific zones. */ - ret = remote_rdata_apply(s, a, &remote_zone_reload); - rcu_read_unlock(); - } - - return (ret != KNOT_EOK) ? ret : KNOT_CTL_ACCEPTED; -} - -/*! - * \brief Remote command 'status' handler. - * - * QNAME: status - * DATA: NONE - */ -static int remote_c_status(server_t *s, remote_cmdargs_t *a) -{ - UNUSED(s); - UNUSED(a); - return KNOT_EOK; -} - -static char *dnssec_info(const zone_t *zone, char *buf, size_t buf_size) -{ - assert(zone); - assert(buf); - - time_t refresh_at = zone_events_get_time(zone, ZONE_EVENT_DNSSEC); - struct tm time_gm = { 0 }; - - gmtime_r(&refresh_at, &time_gm); - size_t written = strftime(buf, buf_size, KNOT_LOG_TIME_FORMAT, &time_gm); - if (written == 0) { - return NULL; - } - - return buf; -} - -static int remote_zonestatus(zone_t *zone, remote_cmdargs_t *a) -{ - if (zone == NULL || a == NULL) { - return KNOT_EINVAL; - } - - /* Fetch latest serial. */ - const knot_rdataset_t *soa_rrs = NULL; - uint32_t serial = 0; - if (zone->contents) { - soa_rrs = node_rdataset(zone->contents->apex, - KNOT_RRTYPE_SOA); - assert(soa_rrs != NULL); - serial = knot_soa_serial(soa_rrs); - } - - /* Fetch next zone event. */ - char when[128] = { '\0' }; - zone_event_type_t next_type = ZONE_EVENT_INVALID; - const char *next_name = ""; - time_t next_time = zone_events_get_next(zone, &next_type); - if (next_type != ZONE_EVENT_INVALID) { - next_name = zone_events_get_name(next_type); - next_time = next_time - time(NULL); - if (next_time < 0) { - memcpy(when, "pending", strlen("pending")); - } else if (snprintf(when, sizeof(when), - "in %lldh%lldm%llds", - (long long)(next_time / 3600), - (long long)(next_time % 3600) / 60, - (long long)(next_time % 60)) < 0) { - return KNOT_ESPACE; - } - } else { - memcpy(when, "idle", strlen("idle")); - } - - /* Prepare zone info. */ - char buf[512] = { '\0' }; - char dnssec_buf[128] = { '\0' }; - char *zone_name = knot_dname_to_str_alloc(zone->name); - - conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name); - bool dnssec_enable = conf_bool(&val); - bool is_slave = zone_is_slave(zone); - - int n = snprintf(buf, sizeof(buf), - "%s\ttype=%s | serial=%u | %s %s | %s %s\n", - zone_name, - is_slave ? "slave" : "master", - serial, - next_name, - when, - dnssec_enable ? "automatic DNSSEC, resigning at:" : "DNSSEC signing disabled", - dnssec_enable ? dnssec_info(zone, dnssec_buf, sizeof(dnssec_buf)) : ""); - free(zone_name); - if (n < 0 || n >= sizeof(buf)) { - return KNOT_ESPACE; - } - - return cmdargs_append(a, buf, n); -} - -/*! - * \brief Remote command 'zonestatus' handler. - * - * QNAME: zonestatus - * DATA: NONE for all zones - * NS RRs with zones in RDATA - */ -static int remote_c_zonestatus(server_t *s, remote_cmdargs_t *a) -{ - rcu_read_lock(); - if (a->argc == 0) { - knot_zonedb_foreach(s->zone_db, remote_zonestatus, a); - } else { - remote_rdata_apply(s, a, remote_zonestatus); - } - rcu_read_unlock(); - - return KNOT_EOK; -} - -/*! - * \brief Remote command 'refresh' handler. - * - * QNAME: refresh - * DATA: NONE for all zones - * NS RRs with zones in RDATA - */ -static int remote_c_refresh(server_t *s, remote_cmdargs_t *a) -{ - rcu_read_lock(); - if (a->argc == 0) { - /* Refresh all. */ - knot_zonedb_foreach(s->zone_db, remote_zone_refresh, NULL); - } else { - /* Refresh specific zones. */ - remote_rdata_apply(s, a, remote_zone_refresh); - } - rcu_read_unlock(); - - return KNOT_CTL_ACCEPTED; -} - -/*! - * \brief Remote command 'retransfer' handler. - * - * QNAME: retransfer - * DATA: NS RRs with zones in RDATA - */ -static int remote_c_retransfer(server_t *s, remote_cmdargs_t *a) -{ - if (a->argc == 0) { - /* Retransfer all. */ - return KNOT_CTL_ARG_REQ; - } else { - rcu_read_lock(); - /* Retransfer specific zones. */ - remote_rdata_apply(s, a, remote_zone_retransfer); - rcu_read_unlock(); - } - - return KNOT_CTL_ACCEPTED; - -} - -/*! - * \brief Remote command 'flush' handler. - * - * QNAME: flush - * DATA: NONE for all zones - * NS RRs with zones in RDATA - */ -static int remote_c_flush(server_t *s, remote_cmdargs_t *a) -{ - rcu_read_lock(); - if (a->argc == 0) { - /* Flush all. */ - knot_zonedb_foreach(s->zone_db, remote_zone_flush, NULL); - } else { - /* Flush specific zones. */ - remote_rdata_apply(s, a, remote_zone_flush); - } - rcu_read_unlock(); - - return KNOT_CTL_ACCEPTED; -} - -/*! - * \brief Remote command 'signzone' handler. - */ -static int remote_c_signzone(server_t *s, remote_cmdargs_t *a) -{ - if (a->argc == 0) { - /* Resign all. */ - return KNOT_CTL_ARG_REQ; - } else { - rcu_read_lock(); - /* Resign specific zones. */ - remote_rdata_apply(s, a, remote_zone_sign); - rcu_read_unlock(); - } - - return KNOT_CTL_ACCEPTED; -} - -static int format_item(conf_io_t *io) -{ - remote_cmdargs_t *a = (remote_cmdargs_t *)io->misc; - - // Get possible error message. - const char *err = io->error.str; - if (err == NULL && io->error.code != KNOT_EOK) { - err = knot_strerror(io->error.code); - } - - // Get the item key and data strings. - char *key = conf_io_txt_key(io); - if (key == NULL) { - return KNOT_ERROR; - } - char *data = conf_io_txt_data(io); - - // Format the item. - char *item = sprintf_alloc( - "%s%s%s%s%s%s%s", - (a->response_size > 0 ? "\n" : ""), - (err != NULL ? "Error (" : ""), - (err != NULL ? err : ""), - (err != NULL ? "): " : ""), - key, - (data != NULL ? " = " : ""), - (data != NULL ? data : "")); - free(key); - free(data); - if (item == NULL) { - return KNOT_ENOMEM; - } - - // Append the item. - int ret = cmdargs_append(a, item, strlen(item)); - free(item); - if (ret != KNOT_EOK) { - return ret; - } - - return KNOT_EOK; -} - -/*! - * \brief Remote command 'conf-begin' handler. - */ -static int remote_c_conf_begin(server_t *s, remote_cmdargs_t *a) -{ - UNUSED(s); - UNUSED(a); - - return conf_io_begin(false); -} - -/*! - * \brief Remote command 'conf-commit' handler. - */ -static int remote_c_conf_commit(server_t *s, remote_cmdargs_t *a) -{ - UNUSED(a); - - conf_io_t io = { - .fcn = format_item, - .misc = a - }; - - // First check the database. - int ret = conf_io_check(&io); - if (ret != KNOT_EOK) { - (void)conf_io_abort(false); - return ret; - } - - ret = conf_io_commit(false); - if (ret != KNOT_EOK) { - (void)conf_io_abort(false); - return ret; - } - - return server_reload(s, NULL); -} - -/*! - * \brief Remote command 'conf-abort' handler. - */ -static int remote_c_conf_abort(server_t *s, remote_cmdargs_t *a) -{ - UNUSED(s); - UNUSED(a); - - return conf_io_abort(false); -} - -/*! - * \brief Parse config key path key0[id].key1. - */ -static int parse_conf_key(char *key, char **key0, char **id, char **key1) -{ - // Check for the empty argument. - if (key == NULL) { - *key0 = NULL; - *key1 = NULL; - *id = NULL; - return KNOT_EOK; - } - - // Get key0. - char *_key0 = key; - - // Check for id. - char *_id = strchr(key, '['); - if (_id != NULL) { - // Separate key0 and id. - *_id++ = '\0'; - - // Check for id end. - char *id_end = _id; - while ((id_end = strchr(id_end, ']')) != NULL) { - // Check for escaped character. - if (*(id_end - 1) != '\\') { - break; - } - id_end++; - } - - // Check for unclosed id. - if (id_end == NULL) { - return KNOT_EINVAL; - } - - // Separate id and key1. - *id_end = '\0'; - - key = id_end + 1; - - // Key1 or nothing must follow. - if (*key != '.' && *key != '\0') { - return KNOT_EINVAL; - } - } - - // Check for key1. - char *_key1 = strchr(key, '.'); - if (_key1 != NULL) { - // Separate key0/id and key1. - *_key1++ = '\0'; - - if (*_key1 == '\0') { - return KNOT_EINVAL; - } - } - - *key0 = _key0; - *key1 = _key1; - *id = _id; - - return KNOT_EOK; -} - -/*! - * \brief Remote command 'conf-desc' handler. - */ -static int remote_c_conf_desc(server_t *s, remote_cmdargs_t *a) -{ - UNUSED(s); - - if (a->argc > 1) { - return KNOT_EINVAL; - } - - char *key = (a->argc == 1) ? - (char *)remote_get_txt(&a->arg[0], 0, NULL) : NULL; - - // Split key path. - char *key0, *key1, *id; - int ret = parse_conf_key(key, &key0, &id, &key1); - if (ret != KNOT_EOK) { - free(key); - return ret; - } - - if (key1 != NULL || id != NULL) { - free(key); - return KNOT_EINVAL; - } - - conf_io_t io = { - .fcn = format_item, - .misc = a - }; - - // Get items. - ret = conf_io_desc(key0, &io); - - free(key); - - return ret; -} - -/*! - * \brief Remote command 'conf-diff' handler. - */ -static int remote_c_conf_diff(server_t *s, remote_cmdargs_t *a) -{ - UNUSED(s); - - if (a->argc > 1) { - return KNOT_EINVAL; - } - - char *key = (a->argc == 1) ? - (char *)remote_get_txt(&a->arg[0], 0, NULL) : NULL; - - // Split key path. - char *key0, *key1, *id; - int ret = parse_conf_key(key, &key0, &id, &key1); - if (ret != KNOT_EOK) { - free(key); - return ret; - } - - conf_io_t io = { - .fcn = format_item, - .misc = a - }; - - // Get the difference. - ret = conf_io_diff(key0, key1, id, &io); - - free(key); - - return ret; -} - -static int conf_read(server_t *s, remote_cmdargs_t *a, bool get_current) -{ - UNUSED(s); - - if (a->argc > 1) { - return KNOT_EINVAL; - } - - char *key = (a->argc == 1) ? - (char *)remote_get_txt(&a->arg[0], 0, NULL) : NULL; - - // Split key path. - char *key0, *key1, *id; - int ret = parse_conf_key(key, &key0, &id, &key1); - if (ret != KNOT_EOK) { - free(key); - return ret; - } - - conf_io_t io = { - .fcn = format_item, - .misc = a - }; - - // Get item(s) value. - ret = conf_io_get(key0, key1, id, get_current, &io); - - free(key); - - return ret; -} - -/*! - * \brief Remote command 'conf-read' handler. - */ -static int remote_c_conf_read(server_t *s, remote_cmdargs_t *a) -{ - return conf_read(s, a, true); -} - -/*! - * \brief Remote command 'conf-get' handler. - */ -static int remote_c_conf_get(server_t *s, remote_cmdargs_t *a) -{ - return conf_read(s, a, false); -} - -/*! - * \brief Remote command 'conf-set' handler. - */ -static int remote_c_conf_set(server_t *s, remote_cmdargs_t *a) -{ - UNUSED(s); - - if (a->argc < 1 || a->argc > 255) { - return KNOT_EINVAL; - } - - char *key = (char *)remote_get_txt(&a->arg[0], 0, NULL); - - // Split key path. - char *key0, *key1, *id; - int ret = parse_conf_key(key, &key0, &id, &key1); - if (ret != KNOT_EOK) { - free(key); - return ret; - } - - conf_io_t io = { - .fcn = format_item, - .misc = a - }; - - // Start child transaction. - ret = conf_io_begin(true); - if (ret != KNOT_EOK) { - free(key); - return ret; - } - - // Add item with no data. - if (a->argc == 1) { - ret = conf_io_set(key0, key1, id, NULL, &io); - // Add item with specified data. - } else { - for (int i = 1; i < a->argc; i++) { - char *data = (char *)remote_get_txt(&a->arg[i], 0, NULL); - ret = conf_io_set(key0, key1, id, data, &io); - free(data); - if (ret != KNOT_EOK) { - break; - } - } - } - - free(key); - - // Finish child transaction. - if (ret == KNOT_EOK) { - return conf_io_commit(true); - } else { - (void)conf_io_abort(true); - return ret; - } -} - -/*! - * \brief Remote command 'conf-unset' handler. - */ -static int remote_c_conf_unset(server_t *s, remote_cmdargs_t *a) -{ - UNUSED(s); - - if (a->argc > 255) { - return KNOT_EINVAL; - } - - char *key = (a->argc >= 1) ? - (char *)remote_get_txt(&a->arg[0], 0, NULL) : NULL; - - // Split key path. - char *key0, *key1, *id; - int ret = parse_conf_key(key, &key0, &id, &key1); - if (ret != KNOT_EOK) { - free(key); - return ret; - } - - // Start child transaction. - ret = conf_io_begin(true); - if (ret != KNOT_EOK) { - free(key); - return ret; - } - - // Delete item with no data. - if (a->argc <= 1) { - ret = conf_io_unset(key0, key1, id, NULL); - // Delete specified data. - } else { - for (int i = 1; i < a->argc; i++) { - char *data = (char *)remote_get_txt(&a->arg[i], 0, NULL); - ret = conf_io_unset(key0, key1, id, data); - free(data); - if (ret != KNOT_EOK) { - break; - } - } - } - - free(key); - - // Finish child transaction. - if (ret == KNOT_EOK) { - return conf_io_commit(true); - } else { - (void)conf_io_abort(true); - return ret; - } -} - int remote_bind(const char *path) { if (path == NULL) { @@ -1116,7 +276,7 @@ int remote_answer(int sock, server_t *s, knot_pkt_t *pkt) log_command(cmd, &args); - remote_cmd_t *c = remote_cmd_tbl; + const remote_cmd_t *c = remote_cmd_tbl; while (c->name != NULL) { if (strcmp(cmd, c->name) == 0) { ret = c->f(s, &args); diff --git a/src/knot/main.c b/src/knot/main.c index a1eed00cdde420b6e58cd7a3ef0ddfe7ff0edf65..25630f6d1f5a5ac2c6df60790012fe96557cdf68 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -212,7 +212,7 @@ static void event_loop(server_t *server) /* Events. */ if (ret > 0) { ret = remote_process(server, sock, buf, buflen); - if (ret == KNOT_CTL_STOP) { + if (ret == KNOT_CTL_ESTOP) { break; } } diff --git a/src/libknot/errcode.h b/src/libknot/errcode.h index 84ce514d2dc82bb066c91e35c8b6a4dafa79150c..fe607686f8464683d7a4ac053d9f4a1280e88b26 100644 --- a/src/libknot/errcode.h +++ b/src/libknot/errcode.h @@ -89,9 +89,9 @@ enum knot_error { KNOT_EFILE, /* Control states. */ - KNOT_CTL_STOP, - KNOT_CTL_ACCEPTED, - KNOT_CTL_ARG_REQ, + KNOT_CTL_ESTOP, + KNOT_CTL_EACCEPTED, + KNOT_CTL_EARG_REQ, /* Network errors. */ KNOT_NET_EADDR, diff --git a/src/libknot/error.c b/src/libknot/error.c index 5892a4258e62946e8ac851e0f9cceeec0dbba2cb..e65faeed44c24802aa355e4e23519124df0c683c 100644 --- a/src/libknot/error.c +++ b/src/libknot/error.c @@ -88,9 +88,9 @@ static const struct error errors[] = { { KNOT_EFILE, "file error" }, /* Control states. */ - { KNOT_CTL_STOP, "stopping server" }, - { KNOT_CTL_ACCEPTED, "command accepted" }, - { KNOT_CTL_ARG_REQ, "argument required" }, + { KNOT_CTL_ESTOP, "stopping server" }, + { KNOT_CTL_EACCEPTED, "command accepted" }, + { KNOT_CTL_EARG_REQ, "argument required" }, /* Network errors. */ { KNOT_NET_EADDR, "bad address or host name" },