summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Salzman <daniel.salzman@nic.cz>2025-01-08 12:36:54 +0100
committerDaniel Salzman <daniel.salzman@nic.cz>2025-01-08 14:14:31 +0100
commit020c84e622e29ad7273d8d116e57e722079c8fa4 (patch)
treec188553bbb7197ac8598ab1f4a16bf227724e38d
parentctl: fix ctl stuck when abort sent to nonexisting conf txn... (diff)
downloadknot-020c84e622e29ad7273d8d116e57e722079c8fa4.tar.xz
knot-020c84e622e29ad7273d8d116e57e722079c8fa4.zip
ctl: improve error detection and send an error message to the client
-rw-r--r--src/knot/ctl/commands.c38
-rw-r--r--src/knot/ctl/commands.h10
-rw-r--r--src/knot/ctl/process.c9
-rw-r--r--tests-extra/tests/ctl/basic/test.py9
4 files changed, 43 insertions, 23 deletions
diff --git a/src/knot/ctl/commands.c b/src/knot/ctl/commands.c
index b168f3063..1f53014f7 100644
--- a/src/knot/ctl/commands.c
+++ b/src/knot/ctl/commands.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2025 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
@@ -161,7 +161,7 @@ static void ctl_log_conf_data(knot_ctl_data_t *data)
}
}
-static void send_error(ctl_args_t *args, const char *msg)
+void ctl_send_error(ctl_args_t *args, const char *msg)
{
knot_ctl_data_t data;
memcpy(&data, args->data, sizeof(data));
@@ -215,7 +215,7 @@ static int zones_apply(ctl_args_t *args, int (*fcn)(zone_t *, ctl_args_t *))
if (failed) {
ret = KNOT_CTL_EZONE;
log_ctl_error("control, error (%s)", knot_strerror(ret));
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
}
return KNOT_EOK;
@@ -230,7 +230,7 @@ static int zones_apply(ctl_args_t *args, int (*fcn)(zone_t *, ctl_args_t *))
if (ret != KNOT_EOK) {
log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE],
"control, error (%s)", knot_strerror(ret));
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
}
// Get next zone name.
@@ -730,7 +730,7 @@ static int zones_apply_backup(ctl_args_t *args, bool restore_mode)
/* Warning: zone name in the control command params discarded here. */
args->data[KNOT_CTL_IDX_ZONE] = NULL;
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
return KNOT_CTL_EZONE;
}
@@ -742,7 +742,7 @@ static int zones_apply_backup(ctl_args_t *args, bool restore_mode)
log_ctl_error("control, QUIC %s error (%s)",
restore_mode ? "restore" : "backup",
knot_strerror(ret));
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
ret = KNOT_EOK;
goto done;
}
@@ -753,7 +753,7 @@ static int zones_apply_backup(ctl_args_t *args, bool restore_mode)
ret = global_backup(ctx, &args->server->catalog, NULL);
if (ret != KNOT_EOK) {
log_ctl_error("control, error (%s)", knot_strerror(ret));
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
ret = KNOT_EOK;
goto done;
}
@@ -1672,7 +1672,7 @@ static int orphans_purge(ctl_args_t *args)
}
if (failed) {
- send_error(args, knot_strerror(KNOT_CTL_EZONE));
+ ctl_send_error(args, knot_strerror(KNOT_CTL_EZONE));
}
} else {
knot_dname_storage_t buff;
@@ -1684,7 +1684,7 @@ static int orphans_purge(ctl_args_t *args)
log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE],
"control, error (%s)",
knot_strerror(KNOT_EINVAL));
- send_error(args, knot_strerror(KNOT_EINVAL));
+ ctl_send_error(args, knot_strerror(KNOT_EINVAL));
return KNOT_EINVAL;
}
knot_dname_to_lower(zone_name);
@@ -1719,7 +1719,7 @@ static int orphans_purge(ctl_args_t *args)
}
if (failed) {
- send_error(args, knot_strerror(KNOT_ERROR));
+ ctl_send_error(args, knot_strerror(KNOT_ERROR));
failed = false;
}
}
@@ -1817,7 +1817,7 @@ static int common_stats(ctl_args_t *args, zone_t *zone)
#define STATS_CHECK(ret, send) { \
if (ret != KNOT_EOK) { \
if ((send)) { /* Prevents duplicit zone error logs. */ \
- send_error(args, knot_strerror(ret)); \
+ ctl_send_error(args, knot_strerror(ret)); \
} \
return ret; \
} \
@@ -1994,7 +1994,7 @@ static int ctl_server(ctl_args_t *args, ctl_cmd_t cmd)
case CTL_STATUS:
ret = server_status(args);
if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
}
break;
case CTL_STOP:
@@ -2006,7 +2006,7 @@ static int ctl_server(ctl_args_t *args, ctl_cmd_t cmd)
ret = server_reload(args->server, RELOAD_FULL);
}
if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
}
break;
default:
@@ -2176,7 +2176,7 @@ static int ctl_conf_txn(ctl_args_t *args, ctl_cmd_t cmd)
}
if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
}
return ret;
@@ -2229,7 +2229,7 @@ static int ctl_conf_list(ctl_args_t *args, ctl_cmd_t cmd)
ret = conf_io_list(key0, key1, id, schema, current, &io);
}
if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
break;
}
@@ -2274,7 +2274,7 @@ static int ctl_conf_read(ctl_args_t *args, ctl_cmd_t cmd)
ret = KNOT_EINVAL;
}
if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
break;
}
@@ -2293,7 +2293,7 @@ static int ctl_conf_modify(ctl_args_t *args, ctl_cmd_t cmd)
// Start child transaction.
int ret = conf_io_begin(true);
if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
return ret;
}
@@ -2317,7 +2317,7 @@ static int ctl_conf_modify(ctl_args_t *args, ctl_cmd_t cmd)
ret = KNOT_EINVAL;
}
if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
break;
}
@@ -2332,7 +2332,7 @@ static int ctl_conf_modify(ctl_args_t *args, ctl_cmd_t cmd)
if (ret == KNOT_EOK) {
ret = conf_io_commit(true);
if (ret != KNOT_EOK) {
- send_error(args, knot_strerror(ret));
+ ctl_send_error(args, knot_strerror(ret));
}
} else {
conf_io_abort(true);
diff --git a/src/knot/ctl/commands.h b/src/knot/ctl/commands.h
index 2ea958747..6a7aad54a 100644
--- a/src/knot/ctl/commands.h
+++ b/src/knot/ctl/commands.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2025 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
@@ -166,3 +166,11 @@ int ctl_exec(ctl_cmd_t cmd, ctl_args_t *args);
* \return True if presented.
*/
bool ctl_has_flag(const char *flags, const char *flag);
+
+/*!
+ * Send control error message.
+ *
+ * \param[in] args Command arguments.
+ * \param[in] msg Error message.
+ */
+void ctl_send_error(ctl_args_t *args, const char *msg);
diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c
index 96bc398e1..cf6d7ab5e 100644
--- a/src/knot/ctl/process.c
+++ b/src/knot/ctl/process.c
@@ -107,7 +107,14 @@ int ctl_process(knot_ctl_t *ctl, server_t *server, int thread_idx, bool *exclusi
}
if ((cmd == CTL_CONF_COMMIT || cmd == CTL_CONF_ABORT) && !*exclusive) {
- log_ctl_warning("control, invalid reception of '%s'", cmd_name);
+ if (conf()->io.txn != NULL) {
+ cmd_ret = KNOT_EBUSY;
+ } else if (cmd == CTL_CONF_COMMIT) {
+ cmd_ret = KNOT_TXN_ENOTEXISTS;
+ }
+ if (cmd_ret != KNOT_EOK) {
+ ctl_send_error(&args, knot_strerror(cmd_ret));
+ }
cmd_exec = false;
}
diff --git a/tests-extra/tests/ctl/basic/test.py b/tests-extra/tests/ctl/basic/test.py
index a5defec8d..7f5b3b6e4 100644
--- a/tests-extra/tests/ctl/basic/test.py
+++ b/tests-extra/tests/ctl/basic/test.py
@@ -29,8 +29,13 @@ ctl.connect(os.path.join(knot.dir, "knot.sock"))
ctl.send_block(cmd="conf-abort")
resp = ctl.receive_block()
-ctl.send_block(cmd="conf-commit")
-resp = ctl.receive_block()
+try:
+ ctl.send_block(cmd="conf-commit")
+ resp = ctl.receive_block()
+except libknot.control.KnotCtlError as exc:
+ isset(exc.message == "no active transaction", "abort error code")
+else:
+ set_err("UNEXPECTED RETURN")
# Add new zone.
ctl.send_block(cmd="conf-begin")