From c76f3bd399c84d93779d3bec50a32512caafff28 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20Va=C5=A1ek?= <david.vasek@nic.cz>
Date: Mon, 16 May 2022 14:58:44 +0200
Subject: [PATCH] ctl/purge: report errors in orphans purge back to knotc

However, when purging orphan catalog members, knotc isn't notified about errors
returned from selective_zone_purge() and catalog_del() as for now.
Try to use similar reporting style as in zones_apply().
---
 doc/man/knotc.8in       |  3 ++-
 doc/man_knotc.rst       |  3 ++-
 src/knot/ctl/commands.c | 36 +++++++++++++++++++++++++-----------
 3 files changed, 29 insertions(+), 13 deletions(-)

diff --git a/doc/man/knotc.8in b/doc/man/knotc.8in
index a7157ea973..73d6b96927 100644
--- a/doc/man/knotc.8in
+++ b/doc/man/knotc.8in
@@ -220,7 +220,8 @@ Purge zone data, zone file, journal, timers, and/or KASP data of specified zones
 Available filters are \fB+expire\fP, \fB+zonefile\fP, \fB+journal\fP, \fB+timers\fP,
 \fB+kaspdb\fP, and \fB+catalog\fP\&. If no filter is specified, all filters are enabled.
 If the zone is no longer configured, add \fB+orphan\fP parameter (zone file cannot
-be purged in this case). This command always requires the force option. (#)
+be purged in this case). When purging orphans, always check the server log for
+possible errors. This command always requires the force option. (#)
 .TP
 \fBzone\-stats\fP \fIzone\fP [\fImodule\fP[\fB\&.\fP\fIcounter\fP]]
 Show zone statistics counter(s). To print also counters with value 0, use
diff --git a/doc/man_knotc.rst b/doc/man_knotc.rst
index 1db03ede48..200aed4ff8 100644
--- a/doc/man_knotc.rst
+++ b/doc/man_knotc.rst
@@ -197,7 +197,8 @@ Actions
   Available filters are **+expire**, **+zonefile**, **+journal**, **+timers**,
   **+kaspdb**, and **+catalog**. If no filter is specified, all filters are enabled.
   If the zone is no longer configured, add **+orphan** parameter (zone file cannot
-  be purged in this case). This command always requires the force option. (#)
+  be purged in this case). When purging orphans, always check the server log for
+  possible errors. This command always requires the force option. (#)
 
 **zone-stats** *zone* [*module*\ [\ **.**\ *counter*\ ]]
   Show zone statistics counter(s). To print also counters with value 0, use
diff --git a/src/knot/ctl/commands.c b/src/knot/ctl/commands.c
index 42c387f010..0eb09ea589 100644
--- a/src/knot/ctl/commands.c
+++ b/src/knot/ctl/commands.c
@@ -1402,14 +1402,18 @@ static int catalog_orphans_sweep(server_t *server)
 	return (ret == KNOT_EOK) ? ret2 : ret;
 }
 
-static void log_if_orphans_error(knot_dname_t *zone_name, int err, char *db_type)
+static void log_if_orphans_error(knot_dname_t *zone_name, int err, char *db_type,
+                                 bool *failed)
 {
 	if (err == KNOT_EOK || err == KNOT_ENOENT || err == KNOT_EFILE) {
 		return;
 	}
 
-	char *msg = sprintf_alloc("failed to purge orphan from %s database (%s)",
-	                          db_type, knot_strerror(err));
+	*failed = true;
+	const char *error = knot_strerror(err);
+
+	char *msg = sprintf_alloc("control, failed to purge orphan from %s database (%s)",
+	                          db_type, error);
 	if (msg == NULL) {
 		return;
 	}
@@ -1427,33 +1431,38 @@ static int orphans_purge(ctl_args_t *args)
 	assert(args->data[KNOT_CTL_IDX_FILTER] != NULL);
 	bool only_orphan = (strlen(args->data[KNOT_CTL_IDX_FILTER]) == 1);
 	int ret;
+	bool failed = false;
 
 	if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
 		// Purge KASP DB.
 		if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
 			ret = kasp_db_sweep(&args->server->kaspdb,
 			                    zone_exists, args->server->zone_db);
-			log_if_orphans_error(NULL, ret, "KASP");
+			log_if_orphans_error(NULL, ret, "KASP", &failed);
 		}
 
 		// Purge zone journals of unconfigured zones.
 		if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
 			ret = journals_walk(&args->server->journaldb,
 			                    drop_journal_if_orphan, args->server);
-			log_if_orphans_error(NULL, ret, "journal");
+			log_if_orphans_error(NULL, ret, "journal", &failed);
 		}
 
 		// Purge timers of unconfigured zones.
 		if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
 			ret = zone_timers_sweep(&args->server->timerdb,
 			                        zone_exists, args->server->zone_db);
-			log_if_orphans_error(NULL, ret, "timer");
+			log_if_orphans_error(NULL, ret, "timer", &failed);
 		}
 
 		// Purge and remove orphan members of non-existing/non-catalog zones.
 		if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_CATALOG)) {
 			ret = catalog_orphans_sweep(args->server);
-			log_if_orphans_error(NULL, ret, "catalog");
+			log_if_orphans_error(NULL, ret, "catalog", &failed);
+		}
+
+		if (failed) {
+			send_error(args, knot_strerror(KNOT_CTL_EZONE));
 		}
 	} else {
 		knot_dname_storage_t buff;
@@ -1475,7 +1484,7 @@ static int orphans_purge(ctl_args_t *args)
 				if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
 					if (knot_lmdb_open(&args->server->kaspdb) == KNOT_EOK) {
 						ret = kasp_db_delete_all(&args->server->kaspdb, zone_name);
-						log_if_orphans_error(zone_name, ret, "KASP");
+						log_if_orphans_error(zone_name, ret, "KASP", &failed);
 					}
 				}
 
@@ -1483,20 +1492,25 @@ static int orphans_purge(ctl_args_t *args)
 				if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
 					zone_journal_t j = { &args->server->journaldb, zone_name };
 					ret = journal_scrape_with_md(j, true);
-					log_if_orphans_error(zone_name, ret, "journal");
+					log_if_orphans_error(zone_name, ret, "journal", &failed);
 				}
 
 				// Purge zone timers.
 				if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
 					ret = zone_timers_sweep(&args->server->timerdb,
 					                        zone_names_distinct, zone_name);
-					log_if_orphans_error(zone_name, ret, "timer");
+					log_if_orphans_error(zone_name, ret, "timer", &failed);
 				}
 
 				// Purge Catalog.
 				if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_CATALOG)) {
 					ret = catalog_zone_purge(args->server, NULL, zone_name);
-					log_if_orphans_error(zone_name, ret, "catalog");
+					log_if_orphans_error(zone_name, ret, "catalog", &failed);
+				}
+
+				if (failed) {
+					send_error(args, knot_strerror(KNOT_ERROR));
+					failed = false;
 				}
 			}
 
-- 
GitLab