/* Copyright (C) 2018 CZ.NIC, z.s.p.o. 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 . */ #include #include #include #include "libknot/libknot.h" #include "knot/common/log.h" #include "knot/ctl/commands.h" #include "knot/conf/conf.h" #include "knot/conf/confdb.h" #include "knot/zone/zonefile.h" #include "knot/zone/zone-load.h" #include "contrib/macros.h" #include "contrib/string.h" #include "contrib/openbsd/strlcat.h" #include "utils/knotc/commands.h" #include "utils/knotc/estimator.h" #define CMD_EXIT "exit" #define CMD_STATUS "status" #define CMD_STOP "stop" #define CMD_RELOAD "reload" #define CMD_STATS "stats" #define CMD_ZONE_CHECK "zone-check" #define CMD_ZONE_MEMSTATS "zone-memstats" #define CMD_ZONE_STATUS "zone-status" #define CMD_ZONE_RELOAD "zone-reload" #define CMD_ZONE_REFRESH "zone-refresh" #define CMD_ZONE_RETRANSFER "zone-retransfer" #define CMD_ZONE_NOTIFY "zone-notify" #define CMD_ZONE_FLUSH "zone-flush" #define CMD_ZONE_SIGN "zone-sign" #define CMD_ZONE_KSK_SBM "zone-ksk-submitted" #define CMD_ZONE_FREEZE "zone-freeze" #define CMD_ZONE_THAW "zone-thaw" #define CMD_ZONE_READ "zone-read" #define CMD_ZONE_BEGIN "zone-begin" #define CMD_ZONE_COMMIT "zone-commit" #define CMD_ZONE_ABORT "zone-abort" #define CMD_ZONE_DIFF "zone-diff" #define CMD_ZONE_GET "zone-get" #define CMD_ZONE_SET "zone-set" #define CMD_ZONE_UNSET "zone-unset" #define CMD_ZONE_PURGE "zone-purge" #define CMD_ZONE_STATS "zone-stats" #define CMD_CONF_INIT "conf-init" #define CMD_CONF_CHECK "conf-check" #define CMD_CONF_IMPORT "conf-import" #define CMD_CONF_EXPORT "conf-export" #define CMD_CONF_LIST "conf-list" #define CMD_CONF_READ "conf-read" #define CMD_CONF_BEGIN "conf-begin" #define CMD_CONF_COMMIT "conf-commit" #define CMD_CONF_ABORT "conf-abort" #define CMD_CONF_DIFF "conf-diff" #define CMD_CONF_GET "conf-get" #define CMD_CONF_SET "conf-set" #define CMD_CONF_UNSET "conf-unset" #define CTL_LOG_STR "failed to control" #define CTL_SEND(type, data) \ ret = knot_ctl_send(args->ctl, (type), (data)); \ if (ret != KNOT_EOK) { \ log_error(CTL_LOG_STR" (%s)", knot_strerror(ret)); \ return ret; \ } #define CTL_SEND_DATA CTL_SEND(KNOT_CTL_TYPE_DATA, &data) #define CTL_SEND_BLOCK CTL_SEND(KNOT_CTL_TYPE_BLOCK, NULL) static int check_args(cmd_args_t *args, int min, int max) { if (max == 0 && args->argc > 0) { log_error("command doesn't take arguments"); return KNOT_EINVAL; } else if (min == max && args->argc != min) { log_error("command requires %i arguments", min); return KNOT_EINVAL; } else if (args->argc < min) { log_error("command requires at least %i arguments", min); return KNOT_EINVAL; } else if (max > 0 && args->argc > max) { log_error("command takes at most %i arguments", max); return KNOT_EINVAL; } return KNOT_EOK; } static int check_conf_args(cmd_args_t *args) { // Mask relevant flags. cmd_flag_t flags = args->desc->flags; flags &= CMD_FOPT_ITEM | CMD_FREQ_ITEM | CMD_FOPT_DATA; switch (args->argc) { case 0: if (flags == CMD_FNONE || (flags & CMD_FOPT_ITEM)) { return KNOT_EOK; } break; case 1: if (flags & (CMD_FOPT_ITEM | CMD_FREQ_ITEM)) { return KNOT_EOK; } break; default: if (flags != CMD_FNONE) { return KNOT_EOK; } break; } log_error("invalid number of arguments"); return KNOT_EINVAL; } static int get_conf_key(const char *key, knot_ctl_data_t *data) { // Get key0. const 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) { log_error("(missing bracket after identifier) %s", id); return KNOT_EINVAL; } // Separate id and key1. *id_end = '\0'; key = id_end + 1; // Key1 or nothing must follow. if (*key != '.' && *key != '\0') { log_error("(unexpected token) %s", key); return KNOT_EINVAL; } } // Check for key1. char *key1 = strchr(key, '.'); if (key1 != NULL) { // Separate key0/id and key1. *key1++ = '\0'; if (*key1 == '\0') { log_error("(missing item specification)"); return KNOT_EINVAL; } } (*data)[KNOT_CTL_IDX_SECTION] = key0; (*data)[KNOT_CTL_IDX_ITEM] = key1; (*data)[KNOT_CTL_IDX_ID] = id; return KNOT_EOK; } static void format_data(ctl_cmd_t cmd, knot_ctl_type_t data_type, knot_ctl_data_t *data, bool *empty) { const char *error = (*data)[KNOT_CTL_IDX_ERROR]; const char *flags = (*data)[KNOT_CTL_IDX_FLAGS]; const char *key0 = (*data)[KNOT_CTL_IDX_SECTION]; const char *key1 = (*data)[KNOT_CTL_IDX_ITEM]; const char *id = (*data)[KNOT_CTL_IDX_ID]; const char *zone = (*data)[KNOT_CTL_IDX_ZONE]; const char *owner = (*data)[KNOT_CTL_IDX_OWNER]; const char *ttl = (*data)[KNOT_CTL_IDX_TTL]; const char *type = (*data)[KNOT_CTL_IDX_TYPE]; const char *value = (*data)[KNOT_CTL_IDX_DATA]; const char *sign = NULL; if (ctl_has_flag(flags, CTL_FLAG_ADD)) { sign = CTL_FLAG_ADD; } else if (ctl_has_flag(flags, CTL_FLAG_REM)) { sign = CTL_FLAG_REM; } switch (cmd) { case CTL_STATUS: if (error != NULL) { printf("error: (%s)%s%s", error, (type != NULL) ? " " : "", (type != NULL) ? type : ""); } else if (value != NULL) { printf("%s", value); *empty = false; } break; case CTL_STOP: case CTL_RELOAD: case CTL_CONF_BEGIN: case CTL_CONF_ABORT: // Only error message is expected here. if (error != NULL) { printf("error: (%s)", error); } break; case CTL_ZONE_STATUS: case CTL_ZONE_RELOAD: case CTL_ZONE_REFRESH: case CTL_ZONE_RETRANSFER: case CTL_ZONE_NOTIFY: case CTL_ZONE_FLUSH: case CTL_ZONE_SIGN: case CTL_ZONE_KSK_SBM: case CTL_ZONE_BEGIN: case CTL_ZONE_COMMIT: case CTL_ZONE_ABORT: case CTL_ZONE_PURGE: if (data_type == KNOT_CTL_TYPE_DATA) { printf("%s%s%s%s%s%s%s%s", (!(*empty) ? "\n" : ""), (error != NULL ? "error: " : ""), (zone != NULL ? "[" : ""), (zone != NULL ? zone : ""), (zone != NULL ? "]" : ""), (error != NULL ? " (" : ""), (error != NULL ? error : ""), (error != NULL ? ")" : "")); *empty = false; } if (cmd == CTL_ZONE_STATUS && type != NULL) { printf("%s %s: %s", (data_type != KNOT_CTL_TYPE_DATA ? " |" : ""), type, value); } break; case CTL_CONF_COMMIT: // Can return a check error context. case CTL_CONF_LIST: case CTL_CONF_READ: case CTL_CONF_DIFF: case CTL_CONF_GET: case CTL_CONF_SET: case CTL_CONF_UNSET: if (data_type == KNOT_CTL_TYPE_DATA) { printf("%s%s%s%s%s%s%s%s%s%s%s%s", (!(*empty) ? "\n" : ""), (error != NULL ? "error: (" : ""), (error != NULL ? error : ""), (error != NULL ? ") " : ""), (sign != NULL ? sign : ""), (key0 != NULL ? key0 : ""), (id != NULL ? "[" : ""), (id != NULL ? id : ""), (id != NULL ? "]" : ""), (key1 != NULL ? "." : ""), (key1 != NULL ? key1 : ""), (value != NULL ? " =" : "")); *empty = false; } if (value != NULL) { printf(" %s", value); } break; case CTL_ZONE_READ: case CTL_ZONE_DIFF: case CTL_ZONE_GET: case CTL_ZONE_SET: case CTL_ZONE_UNSET: printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (!(*empty) ? "\n" : ""), (error != NULL ? "error: (" : ""), (error != NULL ? error : ""), (error != NULL ? ") " : ""), (zone != NULL ? "[" : ""), (zone != NULL ? zone : ""), (zone != NULL ? "] " : ""), (sign != NULL ? sign : ""), (owner != NULL ? owner : ""), (ttl != NULL ? " " : ""), (ttl != NULL ? ttl : ""), (type != NULL ? " " : ""), (type != NULL ? type : ""), (value != NULL ? " " : ""), (value != NULL ? value : "")); *empty = false; break; case CTL_STATS: case CTL_ZONE_STATS: printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (!(*empty) ? "\n" : ""), (error != NULL ? "error: (" : ""), (error != NULL ? error : ""), (error != NULL ? ") " : ""), (zone != NULL ? "[" : ""), (zone != NULL ? zone : ""), (zone != NULL ? "] " : ""), (key0 != NULL ? key0 : ""), (key1 != NULL ? "." : ""), (key1 != NULL ? key1 : ""), (id != NULL ? "[" : ""), (id != NULL ? id : ""), (id != NULL ? "]" : ""), (value != NULL ? " = " : ""), (value != NULL ? value : "")); *empty = false; break; default: assert(0); } } static void format_block(ctl_cmd_t cmd, bool failed, bool empty) { switch (cmd) { case CTL_STATUS: printf("%s\n", (failed || !empty) ? "" : "Running"); break; case CTL_STOP: printf("%s\n", failed ? "" : "Stopped"); break; case CTL_RELOAD: printf("%s\n", failed ? "" : "Reloaded"); break; case CTL_CONF_BEGIN: case CTL_CONF_COMMIT: case CTL_CONF_ABORT: case CTL_CONF_SET: case CTL_CONF_UNSET: case CTL_ZONE_RELOAD: case CTL_ZONE_REFRESH: case CTL_ZONE_RETRANSFER: case CTL_ZONE_NOTIFY: case CTL_ZONE_FLUSH: case CTL_ZONE_SIGN: case CTL_ZONE_KSK_SBM: case CTL_ZONE_FREEZE: case CTL_ZONE_THAW: case CTL_ZONE_BEGIN: case CTL_ZONE_COMMIT: case CTL_ZONE_ABORT: case CTL_ZONE_SET: case CTL_ZONE_UNSET: case CTL_ZONE_PURGE: printf("%s\n", failed ? "" : "OK"); break; case CTL_ZONE_STATUS: case CTL_ZONE_READ: case CTL_ZONE_DIFF: case CTL_ZONE_GET: case CTL_CONF_LIST: case CTL_CONF_READ: case CTL_CONF_DIFF: case CTL_CONF_GET: case CTL_ZONE_STATS: case CTL_STATS: printf("%s", empty ? "" : "\n"); break; default: assert(0); } } static int ctl_receive(cmd_args_t *args) { bool failed = false; bool empty = true; while (true) { knot_ctl_type_t type; knot_ctl_data_t data; int ret = knot_ctl_receive(args->ctl, &type, &data); if (ret != KNOT_EOK) { log_error(CTL_LOG_STR" (%s)", knot_strerror(ret)); return ret; } switch (type) { case KNOT_CTL_TYPE_END: log_error(CTL_LOG_STR" (%s)", knot_strerror(KNOT_EMALF)); return KNOT_EMALF; case KNOT_CTL_TYPE_BLOCK: format_block(args->desc->cmd, failed, empty); return failed ? KNOT_ERROR : KNOT_EOK; case KNOT_CTL_TYPE_DATA: case KNOT_CTL_TYPE_EXTRA: format_data(args->desc->cmd, type, &data, &empty); break; default: assert(0); return KNOT_EINVAL; } if (data[KNOT_CTL_IDX_ERROR] != NULL) { failed = true; } } return KNOT_EOK; } static int cmd_ctl(cmd_args_t *args) { int ret = check_args(args, 0, (args->desc->cmd == CTL_STATUS ? 1 : 0)); if (ret != KNOT_EOK) { return ret; } knot_ctl_data_t data = { [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL, [KNOT_CTL_IDX_TYPE] = args->argc > 0 ? args->argv[0] : NULL }; CTL_SEND_DATA CTL_SEND_BLOCK return ctl_receive(args); } static int set_stats_items(cmd_args_t *args, knot_ctl_data_t *data) { int min_args, max_args; switch (args->desc->cmd) { case CTL_STATS: min_args = 0; max_args = 1; break; case CTL_ZONE_STATS: min_args = 1; max_args = 2; break; default: assert(0); return KNOT_EINVAL; } // Check the number of arguments. int ret = check_args(args, min_args, max_args); if (ret != KNOT_EOK) { return ret; } int idx = 0; // Set ZONE name. if (args->argc > idx && args->desc->cmd == CTL_ZONE_STATS) { if (strcmp(args->argv[idx], "--") != 0) { (*data)[KNOT_CTL_IDX_ZONE] = args->argv[idx]; } idx++; } if (args->argc > idx) { (*data)[KNOT_CTL_IDX_SECTION] = args->argv[idx]; char *item = strchr(args->argv[idx], '.'); if (item != NULL) { // Separate section and item. *item++ = '\0'; (*data)[KNOT_CTL_IDX_ITEM] = item; } } return KNOT_EOK; } static int cmd_stats_ctl(cmd_args_t *args) { knot_ctl_data_t data = { [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL }; int ret = set_stats_items(args, &data); if (ret != KNOT_EOK) { return ret; } CTL_SEND_DATA CTL_SEND_BLOCK return ctl_receive(args); } static int zone_exec(cmd_args_t *args, int (*fcn)(const knot_dname_t *, void *), void *data) { bool failed = false; // Process specified zones. if (args->argc > 0) { uint8_t id[KNOT_DNAME_MAXLEN]; for (int i = 0; i < args->argc; i++) { if (knot_dname_from_str(id, args->argv[i], sizeof(id)) == NULL) { log_zone_str_error(args->argv[i], "invalid name"); failed = true; continue; } knot_dname_to_lower(id); if (!conf_rawid_exists(conf(), C_ZONE, id, knot_dname_size(id))) { log_zone_error(id, "%s", knot_strerror(KNOT_ENOZONE)); failed = true; continue; } if (fcn(id, data) != KNOT_EOK) { failed = true; } } // Process all configured zones. } else { for (conf_iter_t iter = conf_iter(conf(), C_ZONE); iter.code == KNOT_EOK; conf_iter_next(conf(), &iter)) { conf_val_t val = conf_iter_id(conf(), &iter); const knot_dname_t *id = conf_dname(&val); if (fcn(id, data) != KNOT_EOK) { failed = true; } } } return failed ? KNOT_ERROR : KNOT_EOK; } static int zone_check(const knot_dname_t *dname, void *data) { UNUSED(data); zone_contents_t *contents; int ret = zone_load_contents(conf(), dname, &contents); if (ret == KNOT_EOK) { zone_contents_deep_free(contents); } return ret; } static int cmd_zone_check(cmd_args_t *args) { return zone_exec(args, zone_check, NULL); } static int zone_memstats(const knot_dname_t *dname, void *data) { // Init malloc wrapper for trie size estimation. size_t malloc_size = 0; knot_mm_t mem_ctx = { .ctx = &malloc_size, .alloc = estimator_malloc, .free = estimator_free }; // Init memory estimation context. zone_estim_t est = { .node_table = trie_create(&mem_ctx), }; char buff[KNOT_DNAME_TXT_MAXLEN + 1]; char *zone_name = knot_dname_to_str(buff, dname, sizeof(buff)); char *zone_file = conf_zonefile(conf(), dname); zs_scanner_t *zs = malloc(sizeof(zs_scanner_t)); if (est.node_table == NULL || zone_name == NULL || zone_file == NULL || zs == NULL) { log_zone_error(dname, "%s", knot_strerror(KNOT_ENOMEM)); trie_free(est.node_table); free(zone_file); free(zs); return KNOT_ENOMEM; } // Do a parser run, but do not actually create the zone. if (zs_init(zs, zone_name, KNOT_CLASS_IN, 3600) != 0 || zs_set_processing(zs, estimator_rrset_memsize_wrap, NULL, &est) != 0 || zs_set_input_file(zs, zone_file) != 0 || zs_parse_all(zs) != 0) { log_zone_error(dname, "failed to parse zone file '%s' (%s)", zone_file, zs_errorname(zs->error.code)); trie_apply(est.node_table, estimator_free_trie_node, NULL); trie_free(est.node_table); free(zone_file); zs_deinit(zs); free(zs); return KNOT_EPARSEFAIL; } free(zone_file); zs_deinit(zs); free(zs); // Cleanup. trie_apply(est.node_table, estimator_free_trie_node, NULL); trie_free(est.node_table); double zone_size = (est.rdata_size + est.node_size + est.dname_size + malloc_size) / (1024.0 * 1024.0); log_zone_info(dname, "%zu records, %.1f MiB memory", est.record_count, zone_size); double *total_size = (double *)data; *total_size += zone_size; return KNOT_EOK; } static int cmd_zone_memstats(cmd_args_t *args) { double total_size = 0; int ret = zone_exec(args, zone_memstats, &total_size); if (args->argc != 1) { log_info("Total %.1f MiB memory", total_size); } return ret; } static int cmd_zone_ctl(cmd_args_t *args) { knot_ctl_data_t data = { [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL }; // Check the number of arguments. int ret = check_args(args, (args->desc->flags & CMD_FREQ_ZONE) ? 1 : 0, -1); if (ret != KNOT_EOK) { return ret; } if (args->desc->cmd == CTL_ZONE_PURGE && !args->force) { log_error("force option required!"); return KNOT_EDENIED; } // Ignore all zones argument. if (args->argc == 1 && strcmp(args->argv[0], "--") == 0) { args->argc = 0; } if (args->argc == 0) { CTL_SEND_DATA } for (int i = 0; i < args->argc; i++) { data[KNOT_CTL_IDX_ZONE] = args->argv[i]; CTL_SEND_DATA } CTL_SEND_BLOCK return ctl_receive(args); } #define MAX_FILTERS 7 typedef struct { const char *name; char id; bool with_data; // Only ONE filter of each filter_desc_t may have data! } filter_desc_t; const filter_desc_t zone_flush_filters[MAX_FILTERS] = { { "+outdir", CTL_FILTER_FLUSH_OUTDIR, true }, }; const filter_desc_t zone_status_filters[MAX_FILTERS] = { { "+role", CTL_FILTER_STATUS_ROLE }, { "+serial", CTL_FILTER_STATUS_SERIAL }, { "+transaction", CTL_FILTER_STATUS_TRANSACTION }, { "+freeze", CTL_FILTER_STATUS_FREEZE }, { "+events", CTL_FILTER_STATUS_EVENTS }, }; const filter_desc_t zone_purge_filters[MAX_FILTERS] = { { "+expire", CTL_FILTER_PURGE_EXPIRE }, { "+timers", CTL_FILTER_PURGE_TIMERS }, { "+zonefile", CTL_FILTER_PURGE_ZONEFILE }, { "+journal", CTL_FILTER_PURGE_JOURNAL }, { "+kaspdb", CTL_FILTER_PURGE_KASPDB }, { "+orphan", CTL_FILTER_PURGE_ORPHAN }, }; const filter_desc_t null_filter = { 0 }; static const filter_desc_t *get_filter(ctl_cmd_t cmd, const char *filter_name) { const filter_desc_t *fd = NULL; switch (cmd) { case CTL_ZONE_FLUSH: fd = zone_flush_filters; break; case CTL_ZONE_STATUS: fd = zone_status_filters; break; case CTL_ZONE_PURGE: fd = zone_purge_filters; break; default: return &null_filter; } for (size_t i = 0; i < MAX_FILTERS && fd[i].name != NULL; i++) { if (strcmp(fd[i].name, filter_name) == 0) { return &fd[i]; } } return &null_filter; } static int cmd_zone_filter_ctl(cmd_args_t *args) { knot_ctl_data_t data = { [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL }; if (args->desc->cmd == CTL_ZONE_PURGE && !args->force) { log_error("force option required!"); return KNOT_EDENIED; } char filter_buff[MAX_FILTERS + 1] = { 0 }; // First, process the filters. for (int i = 0; i < args->argc; i++) { if (args->argv[i][0] == '+') { if (data[KNOT_CTL_IDX_FILTER] == NULL) { data[KNOT_CTL_IDX_FILTER] = filter_buff; } char filter_id[2] = { get_filter(args->desc->cmd, args->argv[i])->id, 0 }; if (filter_id[0] == '\0') { log_error("unknown filter: %s", args->argv[i]); return KNOT_EINVAL; } if (strchr(filter_buff, filter_id[0]) == NULL) { assert(strlen(filter_buff) < MAX_FILTERS); strlcat(filter_buff, filter_id, sizeof(filter_buff)); } if (get_filter(args->desc->cmd, args->argv[i])->with_data) { data[KNOT_CTL_IDX_DATA] = args->argv[++i]; } } } // Second, process zones. int ret; int sentzones = 0; bool twodash = false; for (int i = 0; i < args->argc; i++) { // Skip filters. if (args->argv[i][0] == '+') { if (get_filter(args->desc->cmd, args->argv[i])->with_data) { i++; } continue; } if (strcmp(args->argv[i], "--") != 0) { data[KNOT_CTL_IDX_ZONE] = args->argv[i]; CTL_SEND_DATA sentzones++; } else { twodash = true; } } if ((args->desc->flags & CMD_FREQ_ZONE) && sentzones == 0 && !twodash) { log_error("zone must be specified (or -- for all zones)"); return KNOT_EDENIED; } if (sentzones == 0) { CTL_SEND_DATA } CTL_SEND_BLOCK return ctl_receive(args); } static int set_rdata(cmd_args_t *args, int pos, char *rdata, size_t rdata_len) { rdata[0] = '\0'; for (int i = pos; i < args->argc; i++) { if (i > pos && strlcat(rdata, " ", rdata_len) >= rdata_len) { return KNOT_ESPACE; } if (strlcat(rdata, args->argv[i], rdata_len) >= rdata_len) { return KNOT_ESPACE; } } return KNOT_EOK; } static int set_node_items(cmd_args_t *args, knot_ctl_data_t *data, char *rdata, size_t rdata_len) { int min_args, max_args; switch (args->desc->cmd) { case CTL_ZONE_READ: case CTL_ZONE_GET: min_args = 1; max_args = 3; break; case CTL_ZONE_DIFF: min_args = 1; max_args = 1; break; case CTL_ZONE_SET: min_args = 3; max_args = -1; break; case CTL_ZONE_UNSET: min_args = 2; max_args = -1; break; default: assert(0); return KNOT_EINVAL; } // Check the number of arguments. int ret = check_args(args, min_args, max_args); if (ret != KNOT_EOK) { return ret; } int idx = 0; // Set ZONE name. assert(args->argc > idx); if (strcmp(args->argv[idx], "--") != 0) { (*data)[KNOT_CTL_IDX_ZONE] = args->argv[idx]; } idx++; // Set OWNER name if specified. if (args->argc > idx) { (*data)[KNOT_CTL_IDX_OWNER] = args->argv[idx]; idx++; } // Set TTL only with an editing operation. if (args->argc > idx) { uint16_t type; if (knot_rrtype_from_string(args->argv[idx], &type) != 0) { switch (args->desc->cmd) { case CTL_ZONE_SET: case CTL_ZONE_UNSET: (*data)[KNOT_CTL_IDX_TTL] = args->argv[idx]; idx++; break; default: return KNOT_EINVAL; } } } // Set record TYPE if specified. if (args->argc > idx) { (*data)[KNOT_CTL_IDX_TYPE] = args->argv[idx]; idx++; } // Set record DATA if specified. if (args->argc > idx) { ret = set_rdata(args, idx, rdata, rdata_len); if (ret != KNOT_EOK) { return ret; } (*data)[KNOT_CTL_IDX_DATA] = rdata; } return KNOT_EOK; } static int cmd_zone_node_ctl(cmd_args_t *args) { knot_ctl_data_t data = { [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL }; char rdata[65536]; // Maximum item size in libknot control interface. int ret = set_node_items(args, &data, rdata, sizeof(rdata)); if (ret != KNOT_EOK) { return ret; } CTL_SEND_DATA CTL_SEND_BLOCK return ctl_receive(args); } static int cmd_conf_init(cmd_args_t *args) { int ret = check_args(args, 0, 0); if (ret != KNOT_EOK) { return ret; } ret = conf_db_check(conf(), &conf()->read_txn); if ((ret >= KNOT_EOK || ret == KNOT_CONF_EVERSION)) { if (ret != KNOT_EOK && !args->force) { log_error("use force option to overwrite the existing " "destination and ensure the server is not running!"); return KNOT_EDENIED; } ret = conf_import(conf(), "", false); } if (ret == KNOT_EOK) { log_info("OK"); } else { log_error("init (%s)", knot_strerror(ret)); } return ret; } static int cmd_conf_check(cmd_args_t *args) { int ret = check_args(args, 0, 0); if (ret != KNOT_EOK) { return ret; } log_info("Configuration is valid"); return 0; } static int cmd_conf_import(cmd_args_t *args) { int ret = check_args(args, 1, 1); if (ret != KNOT_EOK) { return ret; } ret = conf_db_check(conf(), &conf()->read_txn); if ((ret >= KNOT_EOK || ret == KNOT_CONF_EVERSION)) { if (ret != KNOT_EOK && !args->force) { log_error("use force option to overwrite the existing " "destination and ensure the server is not running!"); return KNOT_EDENIED; } log_debug("importing confdb from file '%s'", args->argv[0]); ret = conf_import(conf(), args->argv[0], true); } if (ret == KNOT_EOK) { log_info("OK"); } else { log_error("import (%s)", knot_strerror(ret)); } return ret; } static int cmd_conf_export(cmd_args_t *args) { int ret = check_args(args, 0, 1); if (ret != KNOT_EOK) { return ret; } // Stdout is the default output file. const char *file_name = NULL; if (args->argc > 0) { file_name = args->argv[0]; log_debug("exporting confdb into file '%s'", file_name); } ret = conf_export(conf(), file_name, YP_SNONE); if (ret == KNOT_EOK) { if (args->argc > 0) { log_info("OK"); } } else { log_error("export (%s)", knot_strerror(ret)); } return ret; } static int cmd_conf_ctl(cmd_args_t *args) { // Check the number of arguments. int ret = check_conf_args(args); if (ret != KNOT_EOK) { return ret; } knot_ctl_data_t data = { [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL }; // Send the command without parameters. if (args->argc == 0) { CTL_SEND_DATA // Set the first item argument. } else { ret = get_conf_key(args->argv[0], &data); if (ret != KNOT_EOK) { return ret; } // Send if only one argument or item without values. if (args->argc == 1 || !(args->desc->flags & CMD_FOPT_DATA)) { CTL_SEND_DATA } } // Send the item values or the other items. for (int i = 1; i < args->argc; i++) { if (args->desc->flags & CMD_FOPT_DATA) { data[KNOT_CTL_IDX_DATA] = args->argv[i]; } else { ret = get_conf_key(args->argv[i], &data); if (ret != KNOT_EOK) { return ret; } } CTL_SEND_DATA } CTL_SEND_BLOCK return ctl_receive(args); } const cmd_desc_t cmd_table[] = { { CMD_EXIT, NULL, CTL_NONE }, { CMD_STATUS, cmd_ctl, CTL_STATUS, CMD_FOPT_DATA}, { CMD_STOP, cmd_ctl, CTL_STOP }, { CMD_RELOAD, cmd_ctl, CTL_RELOAD }, { CMD_STATS, cmd_stats_ctl, CTL_STATS }, { CMD_ZONE_CHECK, cmd_zone_check, CTL_NONE, CMD_FOPT_ZONE | CMD_FREAD }, { CMD_ZONE_MEMSTATS, cmd_zone_memstats, CTL_NONE, CMD_FOPT_ZONE | CMD_FREAD }, { CMD_ZONE_STATUS, cmd_zone_filter_ctl, CTL_ZONE_STATUS, CMD_FOPT_ZONE }, { CMD_ZONE_RELOAD, cmd_zone_ctl, CTL_ZONE_RELOAD, CMD_FOPT_ZONE }, { CMD_ZONE_REFRESH, cmd_zone_ctl, CTL_ZONE_REFRESH, CMD_FOPT_ZONE }, { CMD_ZONE_RETRANSFER, cmd_zone_ctl, CTL_ZONE_RETRANSFER, CMD_FOPT_ZONE }, { CMD_ZONE_NOTIFY, cmd_zone_ctl, CTL_ZONE_NOTIFY, CMD_FOPT_ZONE }, { CMD_ZONE_FLUSH, cmd_zone_filter_ctl, CTL_ZONE_FLUSH, CMD_FOPT_ZONE }, { CMD_ZONE_SIGN, cmd_zone_ctl, CTL_ZONE_SIGN, CMD_FOPT_ZONE }, { CMD_ZONE_KSK_SBM, cmd_zone_ctl, CTL_ZONE_KSK_SBM, CMD_FREQ_ZONE | CMD_FOPT_ZONE }, { CMD_ZONE_FREEZE, cmd_zone_ctl, CTL_ZONE_FREEZE, CMD_FOPT_ZONE }, { CMD_ZONE_THAW, cmd_zone_ctl, CTL_ZONE_THAW, CMD_FOPT_ZONE }, { CMD_ZONE_READ, cmd_zone_node_ctl, CTL_ZONE_READ, CMD_FREQ_ZONE }, { CMD_ZONE_BEGIN, cmd_zone_ctl, CTL_ZONE_BEGIN, CMD_FREQ_ZONE | CMD_FOPT_ZONE }, { CMD_ZONE_COMMIT, cmd_zone_ctl, CTL_ZONE_COMMIT, CMD_FREQ_ZONE | CMD_FOPT_ZONE }, { CMD_ZONE_ABORT, cmd_zone_ctl, CTL_ZONE_ABORT, CMD_FREQ_ZONE | CMD_FOPT_ZONE }, { CMD_ZONE_DIFF, cmd_zone_node_ctl, CTL_ZONE_DIFF, CMD_FREQ_ZONE }, { CMD_ZONE_GET, cmd_zone_node_ctl, CTL_ZONE_GET, CMD_FREQ_ZONE }, { CMD_ZONE_SET, cmd_zone_node_ctl, CTL_ZONE_SET, CMD_FREQ_ZONE }, { CMD_ZONE_UNSET, cmd_zone_node_ctl, CTL_ZONE_UNSET, CMD_FREQ_ZONE }, { CMD_ZONE_PURGE, cmd_zone_filter_ctl, CTL_ZONE_PURGE, CMD_FREQ_ZONE }, { CMD_ZONE_STATS, cmd_stats_ctl, CTL_ZONE_STATS, CMD_FREQ_ZONE }, { CMD_CONF_INIT, cmd_conf_init, CTL_NONE, CMD_FWRITE }, { CMD_CONF_CHECK, cmd_conf_check, CTL_NONE, CMD_FREAD | CMD_FREQ_MOD }, { CMD_CONF_IMPORT, cmd_conf_import, CTL_NONE, CMD_FWRITE | CMD_FOPT_MOD }, { CMD_CONF_EXPORT, cmd_conf_export, CTL_NONE, CMD_FREAD | CMD_FOPT_MOD }, { CMD_CONF_LIST, cmd_conf_ctl, CTL_CONF_LIST, CMD_FOPT_ITEM }, { CMD_CONF_READ, cmd_conf_ctl, CTL_CONF_READ, CMD_FOPT_ITEM }, { CMD_CONF_BEGIN, cmd_conf_ctl, CTL_CONF_BEGIN }, { CMD_CONF_COMMIT, cmd_conf_ctl, CTL_CONF_COMMIT }, { CMD_CONF_ABORT, cmd_conf_ctl, CTL_CONF_ABORT }, { CMD_CONF_DIFF, cmd_conf_ctl, CTL_CONF_DIFF, CMD_FOPT_ITEM | CMD_FREQ_TXN }, { CMD_CONF_GET, cmd_conf_ctl, CTL_CONF_GET, CMD_FOPT_ITEM | CMD_FREQ_TXN }, { CMD_CONF_SET, cmd_conf_ctl, CTL_CONF_SET, CMD_FREQ_ITEM | CMD_FOPT_DATA | CMD_FREQ_TXN }, { CMD_CONF_UNSET, cmd_conf_ctl, CTL_CONF_UNSET, CMD_FOPT_ITEM | CMD_FOPT_DATA | CMD_FREQ_TXN }, { NULL } }; static const cmd_help_t cmd_help_table[] = { { CMD_EXIT, "", "Exit interactive mode." }, { "", "", "" }, { CMD_STATUS, "[]", "Check if the server is running." }, { CMD_STOP, "", "Stop the server if running." }, { CMD_RELOAD, "", "Reload the server configuration and modified zones." }, { CMD_STATS, "[[.]]", "Show global statistics counter(s)." }, { "", "", "" }, { CMD_ZONE_CHECK, "[...]", "Check if the zone can be loaded. (*)" }, { CMD_ZONE_MEMSTATS, "[...]", "Estimate memory use for the zone. (*)" }, { CMD_ZONE_RELOAD, "[...]", "Reload a zone from a disk." }, { CMD_ZONE_REFRESH, "[...]", "Force slave zone refresh." }, { CMD_ZONE_NOTIFY, "[...]", "Send a NOTIFY message to all configured remotes." }, { CMD_ZONE_RETRANSFER, "[...]", "Force slave zone retransfer (no serial check)." }, { CMD_ZONE_FLUSH, "[...] [...]", "Flush zone journal into the zone file." }, { CMD_ZONE_SIGN, "[...]", "Re-sign the automatically signed zone." }, { CMD_ZONE_KSK_SBM, "...", "When KSK submission, confirm parent's DS presence." }, { CMD_ZONE_FREEZE, "[...]", "Temporarily postpone automatic zone-changing events." }, { CMD_ZONE_THAW, "[...]", "Dismiss zone freeze." }, { "", "", "" }, { CMD_ZONE_READ, " [ []]", "Get zone data that are currently being presented." }, { CMD_ZONE_BEGIN, "...", "Begin a zone transaction." }, { CMD_ZONE_COMMIT, "...", "Commit the zone transaction." }, { CMD_ZONE_ABORT, "...", "Abort the zone transaction." }, { CMD_ZONE_DIFF, "", "Get zone changes within the transaction." }, { CMD_ZONE_GET, " [ []]", "Get zone data within the transaction." }, { CMD_ZONE_SET, " [] ", "Add zone record within the transaction." }, { CMD_ZONE_UNSET, " [ []]", "Remove zone data within the transaction." }, { CMD_ZONE_PURGE, "... [...]", "Purge zone data, zone file, journal, timers, and KASP data." }, { CMD_ZONE_STATS, " [[.]]", "Show zone statistics counter(s)."}, { CMD_ZONE_STATUS, " [...]", "Show the zone status." }, { "", "", "" }, { CMD_CONF_INIT, "", "Initialize the confdb. (*)" }, { CMD_CONF_CHECK, "", "Check the server configuration. (*)" }, { CMD_CONF_IMPORT, "", "Import a config file into the confdb. (*)" }, { CMD_CONF_EXPORT, "[]", "Export the confdb into a config file or stdout. (*)" }, { CMD_CONF_LIST, "[...]", "List the confdb sections or section items." }, { CMD_CONF_READ, "[...]", "Get the item from the active confdb." }, { CMD_CONF_BEGIN, "", "Begin a writing confdb transaction." }, { CMD_CONF_COMMIT, "", "Commit the confdb transaction." }, { CMD_CONF_ABORT, "", "Rollback the confdb transaction." }, { CMD_CONF_DIFF, "[...]", "Get the item difference within the transaction." }, { CMD_CONF_GET, "[...]", "Get the item data within the transaction." }, { CMD_CONF_SET, " [...]", "Set the item data within the transaction." }, { CMD_CONF_UNSET, "[] [...]", "Unset the item data within the transaction." }, { NULL } }; void print_commands(void) { printf("\nActions:\n"); for (const cmd_help_t *cmd = cmd_help_table; cmd->name != NULL; cmd++) { printf(" %-18s %-38s %s\n", cmd->name, cmd->params, cmd->desc); } printf("\n" "Note:\n" " Use @ owner to denote the zone name.\n" " Empty or '--' parameter means all zones or all zones with a transaction.\n" " Type parameter in the form of
[]..\n" " (*) indicates a local operation which requires a configuration.\n"); }