From f449f278dd3c70e479a035f50a9bb817a9b433ba Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 17:24:08 +0200 Subject: Adding upstream version 3.2.6. Signed-off-by: Daniel Baumann --- src/utils/knotc/process.c | 291 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 src/utils/knotc/process.c (limited to 'src/utils/knotc/process.c') diff --git a/src/utils/knotc/process.c b/src/utils/knotc/process.c new file mode 100644 index 0000000..0f06891 --- /dev/null +++ b/src/utils/knotc/process.c @@ -0,0 +1,291 @@ +/* Copyright (C) 2022 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 "contrib/openbsd/strlcat.h" +#include "knot/conf/conf.h" +#include "knot/common/log.h" +#include "utils/knotc/commands.h" +#include "utils/knotc/process.h" + +static const cmd_desc_t *get_cmd_desc(const char *command) +{ + /* Find requested command. */ + const cmd_desc_t *desc = cmd_table; + while (desc->name != NULL) { + if (strcmp(desc->name, command) == 0) { + break; + } + desc++; + } + if (desc->name == NULL) { + log_error("invalid command '%s'", command); + return NULL; + } + + return desc; +} + +static bool get_cmd_force_flag(const char *arg) +{ + if (strcmp(arg, "-f") == 0 || strcmp(arg, "--force") == 0) { + return true; + } + return false; +} + +static bool get_cmd_blocking_flag(const char *arg) +{ + if (strcmp(arg, "-b") == 0 || strcmp(arg, "--blocking") == 0) { + return true; + } + return false; +} + +int set_config(const cmd_desc_t *desc, params_t *params) +{ + /* Reset the configuration paths (needed in the interactive mode). */ + params->config = params->orig_config; + params->confdb = params->orig_confdb; + + if (params->config != NULL && params->confdb != NULL) { + log_error("ambiguous configuration source"); + return KNOT_EINVAL; + } + + /* Mask relevant flags. */ + cmd_flag_t flags = desc->flags & (CMD_FREAD | CMD_FWRITE); + cmd_flag_t mod_flags = desc->flags & (CMD_FOPT_MOD | CMD_FREQ_MOD); + + /* Choose the optimal config source. */ + struct stat st; + bool import = false; + if (flags == CMD_FNONE && params->socket != NULL) { + /* Control operation, known socket, skip configuration. */ + return KNOT_EOK; + } else if (params->confdb != NULL) { + import = false; + } else if (flags == CMD_FWRITE) { + import = false; + params->confdb = CONF_DEFAULT_DBDIR; + } else if (params->config != NULL){ + import = true; + } else if (conf_db_exists(CONF_DEFAULT_DBDIR)) { + import = false; + params->confdb = CONF_DEFAULT_DBDIR; + } else if (stat(CONF_DEFAULT_FILE, &st) == 0) { + import = true; + params->config = CONF_DEFAULT_FILE; + } else if (flags != CMD_FNONE) { + log_error("no configuration source available"); + return KNOT_EINVAL; + } + + const char *src = import ? params->config : params->confdb; + log_debug("%s '%s'", import ? "config" : "confdb", + (src != NULL) ? src : "empty"); + + /* Prepare config flags. */ + conf_flag_t conf_flags = CONF_FNOHOSTNAME; + if (params->confdb != NULL && !(flags & CMD_FWRITE)) { + conf_flags |= CONF_FREADONLY; + } + if (import || mod_flags & CMD_FOPT_MOD) { + conf_flags |= CONF_FOPTMODULES; + } else if (mod_flags & CMD_FREQ_MOD) { + conf_flags |= CONF_FREQMODULES; + } + + /* Open confdb. */ + conf_t *new_conf = NULL; + int ret = conf_new(&new_conf, conf_schema, params->confdb, + params->max_conf_size, conf_flags); + if (ret != KNOT_EOK) { + log_error("failed to open configuration database '%s' (%s)", + (params->confdb != NULL) ? params->confdb : "", + knot_strerror(ret)); + return ret; + } + + /* Import the config file. */ + if (import) { + ret = conf_import(new_conf, params->config, true, false); + if (ret != KNOT_EOK) { + log_error("failed to load configuration file '%s' (%s)", + params->config, knot_strerror(ret)); + conf_free(new_conf); + return ret; + } + } + + /* Update to the new config. */ + conf_update(new_conf, CONF_UPD_FNONE); + + return KNOT_EOK; +} + +int set_ctl(knot_ctl_t **ctl, const char *socket, int timeout, const cmd_desc_t *desc) +{ + if (desc == NULL) { + *ctl = NULL; + return KNOT_EINVAL; + } + + /* Mask relevant flags. */ + cmd_flag_t flags = desc->flags & (CMD_FREAD | CMD_FWRITE); + + /* Check if control socket is required. */ + if (flags != CMD_FNONE) { + *ctl = NULL; + return KNOT_EOK; + } + + /* Get control socket path. */ + char *path = NULL; + if (socket != NULL) { + path = strdup(socket); + } else { + conf_val_t listen_val = conf_get(conf(), C_CTL, C_LISTEN); + conf_val_t rundir_val = conf_get(conf(), C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&rundir_val, NULL); + path = conf_abs_path(&listen_val, rundir); + free(rundir); + } + if (path == NULL) { + log_error("empty control socket path"); + return KNOT_EINVAL; + } + + log_debug("socket '%s'", path); + + *ctl = knot_ctl_alloc(); + if (*ctl == NULL) { + free(path); + return KNOT_ENOMEM; + } + + knot_ctl_set_timeout(*ctl, timeout); + + int ret = knot_ctl_connect(*ctl, path); + if (ret != KNOT_EOK) { + knot_ctl_free(*ctl); + *ctl = NULL; + log_error("failed to connect to socket '%s' (%s)", path, + knot_strerror(ret)); + free(path); + return ret; + } + + free(path); + + return KNOT_EOK; +} + +void unset_ctl(knot_ctl_t *ctl) +{ + if (ctl == NULL) { + return; + } + + int ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL); + if (ret != KNOT_EOK && ret != KNOT_ECONN) { + log_error("failed to finish control (%s)", knot_strerror(ret)); + } + + knot_ctl_close(ctl); + knot_ctl_free(ctl); +} + +int process_cmd(int argc, const char **argv, params_t *params) +{ + if (argc == 0) { + return KNOT_ENOTSUP; + } + + /* Check the command name. */ + const cmd_desc_t *desc = get_cmd_desc(argv[0]); + if (desc == NULL) { + return KNOT_ENOENT; + } + + /* Check for exit. */ + if (desc->fcn == NULL) { + return KNOT_CTL_ESTOP; + } + + /* Set up the configuration. */ + int ret = set_config(desc, params); + if (ret != KNOT_EOK) { + return ret; + } + + /* Prepare command parameters. */ + cmd_args_t args = { + .desc = desc, + .argc = argc - 1, + .argv = argv + 1, + .force = params->force, + .extended = params->extended, + .color = params->color, + .color_force = params->color_force, + .blocking = params->blocking + }; + + /* Check for special flags after command. */ + while (args.argc > 0) { + if (get_cmd_force_flag(args.argv[0])) { + args.force = true; + args.argc--; + args.argv++; + } else if (get_cmd_blocking_flag(args.argv[0])) { + args.blocking = true; + args.argc--; + args.argv++; + } else { + break; + } + } + + /* Prepare flags parameter. */ + if (args.force) { + strlcat(args.flags, CTL_FLAG_FORCE, sizeof(args.flags)); + } + if (args.blocking) { + strlcat(args.flags, CTL_FLAG_BLOCKING, sizeof(args.flags)); + } + + /* Set control interface if necessary. */ + int cmd_timeout = params->timeout != -1 ? params->timeout : DEFAULT_CTL_TIMEOUT_MS; + if (args.blocking && params->timeout == -1) { + cmd_timeout = 0; + } + ret = set_ctl(&args.ctl, params->socket, cmd_timeout, desc); + if (ret != KNOT_EOK) { + conf_update(NULL, CONF_UPD_FNONE); + return ret; + } + + /* Execute the command. */ + ret = desc->fcn(&args); + + /* Cleanup */ + unset_ctl(args.ctl); + conf_update(NULL, CONF_UPD_FNONE); + + return ret; +} -- cgit v1.2.3