]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
ctl/purge: add orphan member zones purging
authorDavid Vašek <david.vasek@nic.cz>
Thu, 12 May 2022 08:22:58 +0000 (10:22 +0200)
committerLibor Peltan <libor.peltan@nic.cz>
Tue, 21 Jun 2022 15:36:10 +0000 (17:36 +0200)
src/knot/ctl/commands.c

index 08c6c047b48d48670eefbccc9521134fa7c65fab..3e0e775da4e6345019a844f13d3a5d3d2a786781 100644 (file)
@@ -1314,6 +1314,95 @@ static int drop_journal_if_orphan(const knot_dname_t *for_zone, void *ctx)
        return KNOT_EOK;
 }
 
+static int purge_orphan_member_cb(const knot_dname_t *member, const knot_dname_t *owner,
+                                  const knot_dname_t *catz, const char *group, void *ctx)
+{
+       server_t *server = ctx;
+       if (zone_exists(member, server->zone_db)) {
+               return KNOT_EOK;
+       }
+
+       const char *err_str = NULL;
+
+       rcu_read_lock();
+       zone_t *cat_z = knot_zonedb_find(server->zone_db, catz);
+       if (cat_z == NULL) {
+               err_str = "existing";
+       } else if (!cat_z->is_catalog_flag) {
+               err_str = "catalog";
+       }
+       rcu_read_unlock();
+
+       if (err_str == NULL) {
+               return KNOT_EOK;
+       }
+
+       knot_dname_txt_storage_t catz_str;
+       (void)knot_dname_to_str(catz_str, catz, sizeof(catz_str));
+       log_zone_info(member, "member of a non-%s zone %s",
+                     err_str, catz_str);
+
+       // Single-purpose fake zone_t containing only minimal data.
+       // malloc() should suffice here, but clean zone_t is more mishandling-proof.
+       zone_t *orphan = calloc(1, sizeof(zone_t));
+       if (orphan == NULL) {
+               return KNOT_ENOMEM;
+       }
+
+       orphan->name = (knot_dname_t *)member;
+       orphan->server = server;
+
+       purge_flag_t params;
+       params = PURGE_ZONE_TIMERS | PURGE_ZONE_JOURNAL | PURGE_ZONE_KASPDB | PURGE_ZONE_LOG;
+
+       int ret = selective_zone_purge(conf(), orphan, params);
+       free(orphan);
+       if (ret != KNOT_EOK) {
+               log_zone_error(member, "purge of an orphaned zone failed (%s)",
+                              knot_strerror(ret));
+               return ret;
+       }
+
+       // this deleting inside catalog DB iteration is OK, since
+       // the deletion happens in RW txn, while the iteration in persistent RO txn
+       ret = catalog_del(&server->catalog, member);
+       if (ret != KNOT_EOK) {
+               log_zone_error(member, "remove of an orphan from catalog failed (%s)",
+                              knot_strerror(ret));
+               return ret;
+       }
+
+       return KNOT_EOK;
+}
+
+static int catalog_orphans_sweep(server_t *server)
+{
+       catalog_t *cat = &server->catalog;
+       int ret2 = KNOT_EOK;
+       int ret = catalog_begin(cat);
+       if (ret == KNOT_EOK) {
+               ret = catalog_apply(cat, NULL,
+                                   purge_orphan_member_cb,
+                                   server, false);
+               if (ret != KNOT_EOK) {
+                       log_error("failed to purge orphan members data (%s)",
+                                 knot_strerror(ret));
+               }
+               ret2 = catalog_commit(cat);
+               synchronize_rcu();
+               catalog_commit_cleanup(cat);
+               if (ret2 != KNOT_EOK) {
+                       log_error("failed to update catalog (%s)",
+                                 knot_strerror(ret));
+               }
+       } else {
+               log_error("can't open catalog for purging (%s)",
+                         knot_strerror(ret));
+       }
+
+       return (ret == KNOT_EOK) ? ret2 : ret;
+}
+
 static void log_if_orphans_error(knot_dname_t *zone_name, int err, char *db_type)
 {
        if (err == KNOT_EOK || err == KNOT_ENOENT || err == KNOT_EFILE) {
@@ -1361,6 +1450,12 @@ static int orphans_purge(ctl_args_t *args)
                                                zone_exists, args->server->zone_db);
                        log_if_orphans_error(NULL, ret, "timer");
                }
+
+               // 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");
+               }
        } else {
                knot_dname_storage_t buff;
                while (true) {