Commit b909a109 authored by Daniel Salzman's avatar Daniel Salzman

knotc: add history and completion support based on libedit

parent 1cca7f9e
...@@ -429,6 +429,8 @@ src/utils/common/exec.c ...@@ -429,6 +429,8 @@ src/utils/common/exec.c
src/utils/common/exec.h src/utils/common/exec.h
src/utils/common/hex.c src/utils/common/hex.c
src/utils/common/hex.h src/utils/common/hex.h
src/utils/common/lookup.c
src/utils/common/lookup.h
src/utils/common/msg.c src/utils/common/msg.c
src/utils/common/msg.h src/utils/common/msg.h
src/utils/common/netio.c src/utils/common/netio.c
...@@ -465,7 +467,11 @@ src/utils/knotc/commands.c ...@@ -465,7 +467,11 @@ src/utils/knotc/commands.c
src/utils/knotc/commands.h src/utils/knotc/commands.h
src/utils/knotc/estimator.c src/utils/knotc/estimator.c
src/utils/knotc/estimator.h src/utils/knotc/estimator.h
src/utils/knotc/interactive.c
src/utils/knotc/interactive.h
src/utils/knotc/main.c src/utils/knotc/main.c
src/utils/knotc/process.c
src/utils/knotc/process.h
src/utils/knotd/main.c src/utils/knotd/main.c
src/utils/knsupdate/knsupdate_exec.c src/utils/knsupdate/knsupdate_exec.c
src/utils/knsupdate/knsupdate_exec.h src/utils/knsupdate/knsupdate_exec.h
...@@ -530,6 +536,7 @@ tests/server.c ...@@ -530,6 +536,7 @@ tests/server.c
tests/sockaddr.c tests/sockaddr.c
tests/test_conf.h tests/test_conf.h
tests/tsig_key.c tests/tsig_key.c
tests/utils/test_lookup.c
tests/wire.c tests/wire.c
tests/wire_ctx.c tests/wire_ctx.c
tests/worker_pool.c tests/worker_pool.c
......
...@@ -7,6 +7,7 @@ Knot DNS has several dependencies: ...@@ -7,6 +7,7 @@ Knot DNS has several dependencies:
* liburcu >= 0.5.4 * liburcu >= 0.5.4
* gnutls >= 3.0 * gnutls >= 3.0
* jansson >= 2.3 * jansson >= 2.3
* libedit
Embedded libraries: Embedded libraries:
* lmdb (system library is preferred) * lmdb (system library is preferred)
......
...@@ -447,6 +447,38 @@ AS_IF([test "$with_libidn" != "no"],[ ...@@ -447,6 +447,38 @@ AS_IF([test "$with_libidn" != "no"],[
]) # Knot DNS utilities dependencies ]) # Knot DNS utilities dependencies
PKG_CHECK_MODULES([libedit], [libedit], [], [
save_CFLAGS="$CFLAGS"
save_LIBS="$LIBS"
with_libedit=no
for try_path in "" "/usr" "/usr/local"; do
AS_IF([test -d "$try_path"], [
libedit_CFLAGS="-I$try_path/include"
libedit_LIBS="-L$try_path/lib"
],[
continue
])
CFLAGS="$CFLAGS $libedit_CFLAGS"
LIBS="$LIBS $libedit_LIBS"
AC_CHECK_HEADERS([histedit.h], [], [continue])
AC_SEARCH_LIBS([el_init], [edit], [], [continue])
with_libedit=yes
libedit_LIBS="$libedit_LIBS -ledit"
break
done
CFLAGS="$save_CFLAGS"
LIBS="$save_LIBS"
AS_IF([test "$with_libedit" = "no"], [
AC_MSG_ERROR([libedit library not found])
])
])
# Bash completions # Bash completions
AC_ARG_WITH([bash-completions], AC_ARG_WITH([bash-completions],
AC_HELP_STRING([--with-bash-completions=[DIR]], [Bash completions directory [default=no]]), AC_HELP_STRING([--with-bash-completions=[DIR]], [Bash completions directory [default=no]]),
......
...@@ -34,6 +34,7 @@ Knot DNS requires few libraries to be compiled: ...@@ -34,6 +34,7 @@ Knot DNS requires few libraries to be compiled:
* GnuTLS, at least 3.0 * GnuTLS, at least 3.0
* Jansson, at least 2.3 * Jansson, at least 2.3
* Userspace RCU, at least 0.5.4 * Userspace RCU, at least 0.5.4
* libedit
* lmdb (included) * lmdb (included)
* libcap-ng, at least 0.6.4 (optional) * libcap-ng, at least 0.6.4 (optional)
* libidn (optional) * libidn (optional)
......
...@@ -34,6 +34,11 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] ...@@ -34,6 +34,11 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.sp .sp
\fBknotc\fP [\fIparameters\fP] \fIaction\fP [\fIaction_args\fP] \fBknotc\fP [\fIparameters\fP] \fIaction\fP [\fIaction_args\fP]
.SH DESCRIPTION .SH DESCRIPTION
.sp
If no \fIaction\fP is specified, the program is executed in interactive mode. Then
various command or parameters completion is available via the TAB key. And
command history is enabled (see \fIeditrc(5)\fP for additional
customization).
.SS Parameters .SS Parameters
.INDENT 0.0 .INDENT 0.0
.TP .TP
...@@ -225,7 +230,7 @@ $ knotc conf\-commit ...@@ -225,7 +230,7 @@ $ knotc conf\-commit
.UNINDENT .UNINDENT
.SH SEE ALSO .SH SEE ALSO
.sp .sp
\fIknotd(8)\fP, \fIknot.conf(5)\fP\&. \fIknotd(8)\fP, \fIknot.conf(5)\fP, \fIeditrc(5)\fP\&.
.SH AUTHOR .SH AUTHOR
CZ.NIC Labs <http://www.knot-dns.cz> CZ.NIC Labs <http://www.knot-dns.cz>
.SH COPYRIGHT .SH COPYRIGHT
......
...@@ -11,6 +11,11 @@ Synopsis ...@@ -11,6 +11,11 @@ Synopsis
Description Description
----------- -----------
If no *action* is specified, the program is executed in interactive mode. Then
various command or parameters completion is available via the TAB key. And
command history is enabled (see :manpage:`editrc(5)` for additional
customization).
Parameters Parameters
.......... ..........
...@@ -185,4 +190,4 @@ Add example.org zone with a zonefile location ...@@ -185,4 +190,4 @@ Add example.org zone with a zonefile location
See Also See Also
-------- --------
:manpage:`knotd(8)`, :manpage:`knot.conf(5)`. :manpage:`knotd(8)`, :manpage:`knot.conf(5)`, :manpage:`editrc(5)`.
...@@ -211,6 +211,10 @@ knotc_SOURCES = \ ...@@ -211,6 +211,10 @@ knotc_SOURCES = \
utils/knotc/commands.h \ utils/knotc/commands.h \
utils/knotc/estimator.c \ utils/knotc/estimator.c \
utils/knotc/estimator.h \ utils/knotc/estimator.h \
utils/knotc/interactive.c \
utils/knotc/interactive.h \
utils/knotc/process.c \
utils/knotc/process.h \
utils/knotc/main.c utils/knotc/main.c
knotd_SOURCES = \ knotd_SOURCES = \
...@@ -370,7 +374,7 @@ libknotd_la_LIBADD = libknot.la libknot-yparser.la zscanner/libzscanner.la $(lib ...@@ -370,7 +374,7 @@ libknotd_la_LIBADD = libknot.la libknot-yparser.la zscanner/libzscanner.la $(lib
knotd_CPPFLAGS = $(AM_CPPFLAGS) $(liburcu_CFLAGS) knotd_CPPFLAGS = $(AM_CPPFLAGS) $(liburcu_CFLAGS)
knotd_LDADD = libknotd.la $(liburcu_LIBS) knotd_LDADD = libknotd.la $(liburcu_LIBS)
knotc_LDADD = libknotd.la knotc_LDADD = libknotd.la libknotus.la $(libedit_LIBS)
knot1to2_LDADD = libcontrib.la knot1to2_LDADD = libcontrib.la
#################################### ####################################
...@@ -437,6 +441,8 @@ libknotus_la_SOURCES = \ ...@@ -437,6 +441,8 @@ libknotus_la_SOURCES = \
utils/common/exec.h \ utils/common/exec.h \
utils/common/hex.c \ utils/common/hex.c \
utils/common/hex.h \ utils/common/hex.h \
utils/common/lookup.c \
utils/common/lookup.h \
utils/common/msg.c \ utils/common/msg.c \
utils/common/msg.h \ utils/common/msg.h \
utils/common/netio.c \ utils/common/netio.c \
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#include "utils/knotc/commands.h" #include "utils/knotc/commands.h"
#include "utils/knotc/estimator.h" #include "utils/knotc/estimator.h"
#define CMD_EXIT "exit"
#define CMD_STATUS "status" #define CMD_STATUS "status"
#define CMD_STOP "stop" #define CMD_STOP "stop"
#define CMD_RELOAD "reload" #define CMD_RELOAD "reload"
...@@ -97,10 +99,10 @@ static int check_conf_args(cmd_args_t *args) ...@@ -97,10 +99,10 @@ static int check_conf_args(cmd_args_t *args)
return KNOT_EINVAL; return KNOT_EINVAL;
} }
static int get_conf_key(char *key, knot_ctl_data_t *data) static int get_conf_key(const char *key, knot_ctl_data_t *data)
{ {
// Get key0. // Get key0.
char *key0 = key; const char *key0 = key;
// Check for id. // Check for id.
char *id = strchr(key, '['); char *id = strchr(key, '[');
...@@ -410,7 +412,8 @@ static int zone_memstats(const knot_dname_t *dname, void *data) ...@@ -410,7 +412,8 @@ static int zone_memstats(const knot_dname_t *dname, void *data)
.node_table = hattrie_create_n(TRIE_BUCKET_SIZE, &mem_ctx), .node_table = hattrie_create_n(TRIE_BUCKET_SIZE, &mem_ctx),
}; };
char *zone_name = knot_dname_to_str_alloc(dname); 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); char *zone_file = conf_zonefile(conf(), dname);
zs_scanner_t *zs = malloc(sizeof(zs_scanner_t)); zs_scanner_t *zs = malloc(sizeof(zs_scanner_t));
...@@ -419,7 +422,6 @@ static int zone_memstats(const knot_dname_t *dname, void *data) ...@@ -419,7 +422,6 @@ static int zone_memstats(const knot_dname_t *dname, void *data)
log_zone_error(dname, "%s", knot_strerror(KNOT_ENOMEM)); log_zone_error(dname, "%s", knot_strerror(KNOT_ENOMEM));
hattrie_free(est.node_table); hattrie_free(est.node_table);
free(zone_file); free(zone_file);
free(zone_name);
free(zs); free(zs);
return KNOT_ENOMEM; return KNOT_ENOMEM;
} }
...@@ -434,13 +436,11 @@ static int zone_memstats(const knot_dname_t *dname, void *data) ...@@ -434,13 +436,11 @@ static int zone_memstats(const knot_dname_t *dname, void *data)
hattrie_apply_rev(est.node_table, estimator_free_trie_node, NULL); hattrie_apply_rev(est.node_table, estimator_free_trie_node, NULL);
hattrie_free(est.node_table); hattrie_free(est.node_table);
free(zone_file); free(zone_file);
free(zone_name);
zs_deinit(zs); zs_deinit(zs);
free(zs); free(zs);
return KNOT_EPARSEFAIL; return KNOT_EPARSEFAIL;
} }
free(zone_file); free(zone_file);
free(zone_name);
zs_deinit(zs); zs_deinit(zs);
free(zs); free(zs);
...@@ -663,48 +663,40 @@ static int cmd_conf_ctl(cmd_args_t *args) ...@@ -663,48 +663,40 @@ static int cmd_conf_ctl(cmd_args_t *args)
} }
const cmd_desc_t cmd_table[] = { const cmd_desc_t cmd_table[] = {
{ CMD_EXIT, NULL, CTL_NONE },
{ CMD_STATUS, cmd_ctl, CTL_STATUS }, { CMD_STATUS, cmd_ctl, CTL_STATUS },
{ CMD_STOP, cmd_ctl, CTL_STOP }, { CMD_STOP, cmd_ctl, CTL_STOP },
{ CMD_RELOAD, cmd_ctl, CTL_RELOAD }, { CMD_RELOAD, cmd_ctl, CTL_RELOAD },
{ CMD_ZONE_CHECK, cmd_zone_check, CTL_NONE, CMD_CONF_FREAD }, { CMD_ZONE_CHECK, cmd_zone_check, CTL_NONE, CMD_CONF_FOPT_ZONE | CMD_CONF_FREAD },
{ CMD_ZONE_MEMSTATS, cmd_zone_memstats, CTL_NONE, CMD_CONF_FREAD }, { CMD_ZONE_MEMSTATS, cmd_zone_memstats, CTL_NONE, CMD_CONF_FOPT_ZONE | CMD_CONF_FREAD },
{ CMD_ZONE_STATUS, cmd_zone_ctl, CTL_ZONE_STATUS }, { CMD_ZONE_STATUS, cmd_zone_ctl, CTL_ZONE_STATUS, CMD_CONF_FOPT_ZONE },
{ CMD_ZONE_RELOAD, cmd_zone_ctl, CTL_ZONE_RELOAD }, { CMD_ZONE_RELOAD, cmd_zone_ctl, CTL_ZONE_RELOAD, CMD_CONF_FOPT_ZONE },
{ CMD_ZONE_REFRESH, cmd_zone_ctl, CTL_ZONE_REFRESH }, { CMD_ZONE_REFRESH, cmd_zone_ctl, CTL_ZONE_REFRESH, CMD_CONF_FOPT_ZONE },
{ CMD_ZONE_RETRANSFER, cmd_zone_ctl, CTL_ZONE_RETRANSFER }, { CMD_ZONE_RETRANSFER, cmd_zone_ctl, CTL_ZONE_RETRANSFER, CMD_CONF_FOPT_ZONE },
{ CMD_ZONE_FLUSH, cmd_zone_ctl, CTL_ZONE_FLUSH }, { CMD_ZONE_FLUSH, cmd_zone_ctl, CTL_ZONE_FLUSH, CMD_CONF_FOPT_ZONE },
{ CMD_ZONE_SIGN, cmd_zone_ctl, CTL_ZONE_SIGN }, { CMD_ZONE_SIGN, cmd_zone_ctl, CTL_ZONE_SIGN, CMD_CONF_FOPT_ZONE },
{ CMD_CONF_INIT, cmd_conf_init, CTL_NONE, CMD_CONF_FWRITE }, { CMD_CONF_INIT, cmd_conf_init, CTL_NONE, CMD_CONF_FWRITE },
{ CMD_CONF_CHECK, cmd_conf_check, CTL_NONE, CMD_CONF_FREAD }, { CMD_CONF_CHECK, cmd_conf_check, CTL_NONE, CMD_CONF_FREAD },
{ CMD_CONF_IMPORT, cmd_conf_import, CTL_NONE, CMD_CONF_FWRITE }, { CMD_CONF_IMPORT, cmd_conf_import, CTL_NONE, CMD_CONF_FWRITE },
{ CMD_CONF_EXPORT, cmd_conf_export, CTL_NONE, CMD_CONF_FREAD }, { CMD_CONF_EXPORT, cmd_conf_export, CTL_NONE, CMD_CONF_FREAD },
{ CMD_CONF_LIST, cmd_conf_ctl, CTL_CONF_LIST, CMD_CONF_FOPT_ITEM }, { CMD_CONF_LIST, cmd_conf_ctl, CTL_CONF_LIST, CMD_CONF_FOPT_ITEM },
{ CMD_CONF_READ, cmd_conf_ctl, CTL_CONF_READ, CMD_CONF_FOPT_ITEM }, { CMD_CONF_READ, cmd_conf_ctl, CTL_CONF_READ, CMD_CONF_FOPT_ITEM },
{ CMD_CONF_BEGIN, cmd_conf_ctl, CTL_CONF_BEGIN }, { CMD_CONF_BEGIN, cmd_conf_ctl, CTL_CONF_BEGIN },
{ CMD_CONF_COMMIT, cmd_conf_ctl, CTL_CONF_COMMIT }, { CMD_CONF_COMMIT, cmd_conf_ctl, CTL_CONF_COMMIT },
{ CMD_CONF_ABORT, cmd_conf_ctl, CTL_CONF_ABORT }, { CMD_CONF_ABORT, cmd_conf_ctl, CTL_CONF_ABORT },
{ CMD_CONF_DIFF, cmd_conf_ctl, CTL_CONF_DIFF, CMD_CONF_FOPT_ITEM }, { CMD_CONF_DIFF, cmd_conf_ctl, CTL_CONF_DIFF, CMD_CONF_FOPT_ITEM | CMD_CONF_FREQ_TXN },
{ CMD_CONF_GET, cmd_conf_ctl, CTL_CONF_GET, CMD_CONF_FOPT_ITEM }, { CMD_CONF_GET, cmd_conf_ctl, CTL_CONF_GET, CMD_CONF_FOPT_ITEM | CMD_CONF_FREQ_TXN },
{ CMD_CONF_SET, cmd_conf_ctl, CTL_CONF_SET, CMD_CONF_FREQ_ITEM | CMD_CONF_FOPT_DATA }, { CMD_CONF_SET, cmd_conf_ctl, CTL_CONF_SET, CMD_CONF_FREQ_ITEM | CMD_CONF_FOPT_DATA | CMD_CONF_FREQ_TXN },
{ CMD_CONF_UNSET, cmd_conf_ctl, CTL_CONF_UNSET, CMD_CONF_FOPT_ITEM | CMD_CONF_FOPT_DATA }, { CMD_CONF_UNSET, cmd_conf_ctl, CTL_CONF_UNSET, CMD_CONF_FOPT_ITEM | CMD_CONF_FOPT_DATA | CMD_CONF_FREQ_TXN },
{ NULL }
};
const cmd_desc_old_t cmd_table_old[] = {
{ "checkzone", CMD_ZONE_CHECK },
{ "memstats", CMD_ZONE_MEMSTATS },
{ "zonestatus", CMD_ZONE_STATUS },
{ "refresh", CMD_ZONE_REFRESH },
{ "flush", CMD_ZONE_FLUSH },
{ "signzone", CMD_ZONE_SIGN },
{ "checkconf", CMD_CONF_CHECK },
{ "conf-desc", CMD_CONF_LIST },
{ NULL } { NULL }
}; };
const cmd_help_t cmd_help_table[] = { static const cmd_help_t cmd_help_table[] = {
{ CMD_EXIT, "", "Exit interactive mode." },
{ "", "", "" },
{ CMD_STATUS, "", "Check if the server is running." }, { CMD_STATUS, "", "Check if the server is running." },
{ CMD_STOP, "", "Stop the server if running." }, { CMD_STOP, "", "Stop the server if running." },
{ CMD_RELOAD, "", "Reload the server configuration and modified zones." }, { CMD_RELOAD, "", "Reload the server configuration and modified zones." },
...@@ -733,3 +725,18 @@ const cmd_help_t cmd_help_table[] = { ...@@ -733,3 +725,18 @@ const cmd_help_t cmd_help_table[] = {
{ CMD_CONF_UNSET, "[<item>] [<data>...]", "Unset the item data in the transaction." }, { CMD_CONF_UNSET, "[<item>] [<data>...]", "Unset the item data in the transaction." },
{ NULL } { NULL }
}; };
void print_commands(void)
{
printf("\nActions:\n");
for (const cmd_help_t *cmd = cmd_help_table; cmd->name != NULL; cmd++) {
printf(" %-15s %-20s %s\n", cmd->name, cmd->params, cmd->desc);
}
printf("\n"
"Note:\n"
" Empty <zone> parameter means all zones.\n"
" Type <item> parameter in the form of <section>[<identifier>].<name>.\n"
" (*) indicates a local operation which requires a configuration.\n");
}
...@@ -41,6 +41,8 @@ typedef enum { ...@@ -41,6 +41,8 @@ typedef enum {
CMD_CONF_FOPT_ITEM = 1 << 2, /*!< Optional item argument. */ CMD_CONF_FOPT_ITEM = 1 << 2, /*!< Optional item argument. */
CMD_CONF_FREQ_ITEM = 1 << 3, /*!< Required item argument. */ CMD_CONF_FREQ_ITEM = 1 << 3, /*!< Required item argument. */
CMD_CONF_FOPT_DATA = 1 << 4, /*!< Optional item data argument. */ CMD_CONF_FOPT_DATA = 1 << 4, /*!< Optional item data argument. */
CMD_CONF_FOPT_ZONE = 1 << 5, /*!< Optional zone name argument. */
CMD_CONF_FREQ_TXN = 1 << 6, /*!< Required opened confdb transaction. */
} cmd_conf_flag_t; } cmd_conf_flag_t;
struct cmd_desc; struct cmd_desc;
...@@ -51,7 +53,7 @@ typedef struct { ...@@ -51,7 +53,7 @@ typedef struct {
const cmd_desc_t *desc; const cmd_desc_t *desc;
knot_ctl_t *ctl; knot_ctl_t *ctl;
int argc; int argc;
char **argv; const char **argv;
cmd_flag_t flags; cmd_flag_t flags;
} cmd_args_t; } cmd_args_t;
...@@ -63,12 +65,6 @@ struct cmd_desc { ...@@ -63,12 +65,6 @@ struct cmd_desc {
cmd_conf_flag_t flags; cmd_conf_flag_t flags;
}; };
/*! \brief Old command name translation. */
typedef struct {
const char *old_name;
const char *new_name;
} cmd_desc_old_t;
/*! \brief Command description. */ /*! \brief Command description. */
typedef struct { typedef struct {
const char *name; const char *name;
...@@ -79,10 +75,7 @@ typedef struct { ...@@ -79,10 +75,7 @@ typedef struct {
/*! \brief Table of commands. */ /*! \brief Table of commands. */
extern const cmd_desc_t cmd_table[]; extern const cmd_desc_t cmd_table[];
/*! \brief Table of command translations. */ /*! \brief Prints commands help. */
extern const cmd_desc_old_t cmd_table_old[]; void print_commands(void);
/*! \brief Table of command descriptions. */
extern const cmd_help_t cmd_help_table[];
/*! @} */ /*! @} */
This diff is collapsed.
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* \file
*
* \brief Knot control interactive mode.
*
* \addtogroup knot_utils
* @{
*/
#pragma once
#include "utils/knotc/process.h"
/*!
* Executes an interactive processing loop.
*
* \param[in] params Utility parameters.
*/
int interactive_loop(params_t *params);
/*! @} */
This diff is collapsed.
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/stat.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;
}
int set_config(const cmd_desc_t *desc, params_t *params)
{
if (params->config != NULL && params->confdb != NULL) {
log_error("ambiguous configuration source");
return KNOT_EINVAL;
}
/* Mask relevant flags. */
cmd_conf_flag_t flags = desc->flags & (CMD_CONF_FREAD | CMD_CONF_FWRITE);
/* Choose the optimal config source. */
struct stat st;
bool import = false;
if (flags == CMD_CONF_FNONE && params->socket != NULL) {
import = false;
params->confdb = NULL;
} else if (params->confdb != NULL) {
import = false;
} else if (flags == CMD_CONF_FWRITE) {
import = false;
params->confdb = CONF_DEFAULT_DBDIR;
} else if (params->config != NULL){
import = true;
} else if (stat(CONF_DEFAULT_DBDIR, &st) == 0) {
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_CONF_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_CONF_FWRITE)) {
conf_flags |= CONF_FREADONLY;
}
/* Open confdb. */
conf_t *new_conf = NULL;
int ret = conf_new(&new_conf, conf_scheme, params->confdb, 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);
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);
return KNOT_EOK;
}
int set_ctl(knot_ctl_t **ctl, const cmd_desc_t *desc, params_t *params)
{
if (desc == NULL) {
*ctl = NULL;
return KNOT_EINVAL;
}
/* Mask relevant flags. */
cmd_conf_flag_t flags = desc->flags & (CMD_CONF_FREAD | CMD_CONF_FWRITE);
/* Check if control socket is required. */
if (flags != CMD_CONF_FNONE) {
*ctl = NULL;
return KNOT_EOK;
}
/* Get control socket path. */
char *path = NULL;
if (params->socket != NULL) {
path = strdup(params->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, params->timeout);
int ret = knot_ctl_connect(*ctl, path);
if (ret != KNOT_EOK) {
knot_ctl_free(*ctl);
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,
.flags = params->flags
};
/* Set control interface if necessary. */
ret = set_ctl(&args.ctl, desc, params);
if (ret != KNOT_EOK) {
conf_update(NULL);
return ret;
}
/* Execute the command. */
ret = desc->fcn(&args);
/* Cleanup */
unset_ctl(args.ctl);
conf_update(NULL);
return KNOT_EOK;
}
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* \file
*
* \brief Knot control processing.
*
* \addtogroup knot_utils
* @{
*/
#pragma once
#include "utils/knotc/commands.h"
/*! Utility command line parameters. */
typedef struct {