]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
ctl: added option to purge orphaned zones
authorMark Karpilovskij <mark.karpilovskij@nic.cz>
Thu, 22 Feb 2018 09:36:05 +0000 (10:36 +0100)
committerDaniel Salzman <daniel.salzman@nic.cz>
Mon, 5 Mar 2018 11:46:38 +0000 (12:46 +0100)
doc/man/knotc.8in
doc/man_knotc.rst
src/knot/ctl/commands.c
src/knot/ctl/commands.h
src/knot/journal/journal.c
src/knot/zone/zonedb-load.c
src/utils/kjournalprint/main.c
src/utils/knotc/commands.c

index 9f121e01ed2bdcf0f65acee785b73420859f50b9..55a519e558078e8012b99f38434ede54cf5d69ff 100644 (file)
@@ -148,9 +148,11 @@ requires a ttl value specified.
 Remove zone data within the transaction.
 .TP
 \fBzone\-purge\fP \fIzone\fP\&... [\fIfilter\fP\&...]
-Purge zone data, file, journal, timers, and kaspdb.
-Filters are \fB+expire\fP, \fB+timers\fP, \fB+zonefile\fP, \fB+journal\fP,
-and \fB+kaspdb\fP\&.
+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,
+and \fB+kaspdb\fP\&. If no filter is specified, all filters are enabled.
+It the zone is no longer configured, add \fB+orphan\fP filter (zone file cannot
+be purged in this case).
 .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
index c2ae8c732270678569c0a722c6a6bfa5853239d6..e4195c17f86f9a3ea209f7f62d76d83821106d12 100644 (file)
@@ -125,9 +125,11 @@ Actions
   Remove zone data within the transaction.
 
 **zone-purge** *zone*... [*filter*...]
-  Purge zone data, file, journal, timers, and kaspdb.
-  Filters are **+expire**, **+timers**, **+zonefile**, **+journal**,
-  and **+kaspdb**.
+  Purge zone data, zone file, journal, timers, and/or KASP data of specified zones.
+  Available filters are **+expire**, **+zonefile**, **+journal**, **+timers**,
+  and **+kaspdb**. If no filter is specified, all filters are enabled.
+  It the zone is no longer configured, add **+orphan** filter (zone file cannot
+  be purged in this case).
 
 **zone-stats** *zone* [*module*\ [\ **.**\ *counter*\ ]]
   Show zone statistics counter(s). To print also counters with value 0, use
index a5e157f7c53500f12d51f843344c88d3c0474106..867aa79f88a39297114115e57e917c9b5a492aa4 100644 (file)
@@ -34,6 +34,7 @@
 #include "contrib/macros.h"
 #include "contrib/mempattern.h"
 #include "contrib/string.h"
+#include "contrib/ucw/lists.h"
 #include "zscanner/scanner.h"
 #include "contrib/strtonum.h"
 
@@ -981,7 +982,128 @@ static int zone_txn_unset(zone_t *zone, ctl_args_t *args)
                        return zone_update_remove_node(zone->control_update, owner);
                }
        }
+}
+
+static bool zone_exists(const knot_dname_t *zone, void *data)
+{
+       assert(zone);
+       assert(data);
 
+       knot_zonedb_t *db = data;
+
+       return knot_zonedb_find(db, zone) != NULL;
+}
+
+static bool zone_names_distinct(const knot_dname_t *zone, void *data)
+{
+       assert(zone);
+       assert(data);
+
+       knot_dname_t *zone_to_purge = data;
+
+       return !knot_dname_is_equal(zone, zone_to_purge);
+}
+
+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);
+
+       if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
+               // Purge KASP DB.
+               if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
+                       list_t zones;
+                       init_list(&zones);
+                       if (kasp_db_open(*kaspdb()) == KNOT_EOK &&
+                           kasp_db_list_zones(*kaspdb(), &zones) == KNOT_EOK) {
+                               ptrnode_t *zn;
+                               WALK_LIST(zn, zones) {
+                                       knot_dname_t *zone_name = (knot_dname_t *)zn->d;
+                                       if (!zone_exists(zone_name, args->server->zone_db)) {
+                                               (void)kasp_db_delete_all(*kaspdb(), zone_name);
+                                       }
+                                       knot_dname_free(zone_name, NULL);
+                               }
+                               ptrlist_free(&zones, NULL);
+                       }
+               }
+
+               // Purge zone journals of unconfigured zones.
+               if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
+                       list_t zones;
+                       init_list(&zones);
+                       if (journal_db_list_zones(&args->server->journal_db, &zones) == KNOT_EOK) {
+                               ptrnode_t *zn;
+                               WALK_LIST(zn, zones) {
+                                       journal_t journal = { 0 };
+                                       knot_dname_t *zone_name = (knot_dname_t *)zn->d;
+                                       if (!zone_exists(zone_name, args->server->zone_db) &&
+                                           journal_open(&journal, &args->server->journal_db,
+                                                        zone_name) == KNOT_EOK) {
+                                               journal_scrape(&journal);
+                                               journal_close(&journal);
+                                       }
+                                       knot_dname_free(zone_name, NULL);
+                               }
+                               ptrlist_free(&zones, NULL);
+                       }
+               }
+
+               // Purge timers of unconfigured zones.
+               if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
+                       (void)zone_timers_sweep(args->server->timers_db,
+                                               zone_exists, args->server->zone_db);
+               }
+       } else {
+               uint8_t buff[KNOT_DNAME_MAXLEN];
+               while (true) {
+                       knot_dname_t *zone_name =
+                               knot_dname_from_str(buff, args->data[KNOT_CTL_IDX_ZONE],
+                                                   sizeof(buff));
+                       if (zone_name == NULL) {
+                               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));
+                               return KNOT_EINVAL;
+                       }
+                       knot_dname_to_lower(zone_name);
+
+                       if (!zone_exists(zone_name, args->server->zone_db)) {
+                               // Purge KASP DB.
+                               if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
+                                       if (kasp_db_open(*kaspdb()) == KNOT_EOK) {
+                                               (void) kasp_db_delete_all(*kaspdb(), zone_name);
+                                       }
+                               }
+
+                               // Purge zone journal.
+                               if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
+                                       journal_t journal = { 0 };
+                                       if (journal_open(&journal, &args->server->journal_db,
+                                                        zone_name) == KNOT_EOK) {
+                                               (void)journal_scrape(&journal);
+                                               journal_close(&journal);
+                                       }
+                               }
+
+                               // Purge zone timers.
+                               if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
+                                       (void)zone_timers_sweep(args->server->timers_db,
+                                                               zone_names_distinct, zone_name);
+                               }
+                       }
+
+                       // Get next zone name.
+                       int ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
+                       if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
+                               break;
+                       }
+                       ctl_log_data(&args->data);
+               }
+       }
+
+       return KNOT_EOK;
 }
 
 static int zone_purge(zone_t *zone, ctl_args_t *args)
@@ -1211,7 +1333,11 @@ static int ctl_zone(ctl_args_t *args, ctl_cmd_t cmd)
        case CTL_ZONE_UNSET:
                return zones_apply(args, zone_txn_unset);
        case CTL_ZONE_PURGE:
-               return zones_apply(args, zone_purge);
+               if (MATCH_AND_FILTER(args, CTL_FILTER_PURGE_ORPHAN)) {
+                       return orphans_purge(args);
+               } else {
+                       return zones_apply(args, zone_purge);
+               }
        case CTL_ZONE_STATS:
                return zones_apply(args, zone_stats);
        default:
index b8f3e2e0d7e6ffd5b7d3160a2e398c5f320a0705..b75dfc2543b3a1a83517fe55b329235228da127b 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2018 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
@@ -44,6 +44,7 @@
 #define CTL_FILTER_PURGE_ZONEFILE      'f'
 #define CTL_FILTER_PURGE_JOURNAL       'j'
 #define CTL_FILTER_PURGE_KASPDB                'k'
+#define CTL_FILTER_PURGE_ORPHAN                'o'
 
 /*! Control commands. */
 typedef enum {
index 522bdf31816e4ebec9e4fcca4bada6564a7c7992..9f851dcd7df7325bdfd1db04472fb808c916aaf3 100644 (file)
@@ -1912,7 +1912,11 @@ int journal_db_list_zones(journal_db_t **db, list_t *zones)
                if (txn->ret == KNOT_EOK && *compare_metaflag == '\0') {
                        compare_metaflag -= metaflag_len;
                        if (strcmp(compare_metaflag, MDKEY_PERZONE_FLAGS) == 0) {
-                               char *found_zone = knot_dname_to_str_alloc((const knot_dname_t *)key.data);
+                               knot_dname_t *found_zone = knot_dname_copy((const knot_dname_t *)key.data, NULL);
+                               if (found_zone == NULL) {
+                                       txn->ret = KNOT_ENOMEM;
+                                       break;
+                               }
                                ptrlist_add(zones, found_zone, NULL);
                        }
                }
index 3a7ebf143c5679dc9a42d9af8c9b538561b370c8..97383dc1709896e18823f07754c6384c05a9348c 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2018 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
@@ -321,16 +321,6 @@ static void remove_old_zonedb(conf_t *conf, knot_zonedb_t *db_old,
        }
 }
 
-static bool zone_exists(const knot_dname_t *zone, void *data)
-{
-       assert(zone);
-       assert(data);
-
-       knot_zonedb_t *db = data;
-
-       return knot_zonedb_find(db, zone) != NULL;
-}
-
 void zonedb_reload(conf_t *conf, server_t *server)
 {
        if (conf == NULL || server == NULL) {
@@ -351,15 +341,6 @@ void zonedb_reload(conf_t *conf, server_t *server)
        /* Wait for readers to finish reading old zone database. */
        synchronize_rcu();
 
-       /* Sweep the timer database. */
-       if (server->timers_db != NULL) {
-               int ret = zone_timers_sweep(server->timers_db, zone_exists, db_new);
-               if (ret != KNOT_EOK) {
-                       log_warning("failed to clear persistent timer DB (%s)",
-                                   knot_strerror(ret));
-               }
-       }
-
        /* Remove old zone DB. */
        remove_old_zonedb(conf, db_old, db_new);
 }
index b74c64715444b51bc80559a55fa7e70c2719364f..2d30a34f3679bf622c47fdf81da750de1982b028 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2018 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
@@ -281,8 +281,9 @@ int list_zones(char *path)
        ret = journal_db_list_zones(&jdb, &zones);
        if (ret == KNOT_EOK) {
                ptrnode_t *zn;
+               char buff[KNOT_DNAME_TXT_MAXLEN + 1];
                WALK_LIST(zn, zones) {
-                       printf("%s\n", (char *)zn->d);
+                       printf("%s\n", knot_dname_to_str(buff, (knot_dname_t *)zn->d, sizeof(buff)));
                        free(zn->d);
                }
                ptrlist_free(&zones, NULL);
index a78134815bc5f7acdacdb98dc536794f57c87ceb..9b379f21b320f350763ce76c8df44417976f9347 100644 (file)
@@ -707,6 +707,7 @@ const filter_desc_t zone_purge_filters[MAX_FILTERS] = {
        { "+zonefile", CTL_FILTER_PURGE_ZONEFILE },
        { "+journal",  CTL_FILTER_PURGE_JOURNAL },
        { "+kaspdb",   CTL_FILTER_PURGE_KASPDB },
+       { "+orphan",   CTL_FILTER_PURGE_ORPHAN },
 };
 
 const filter_desc_t null_filter = { 0 };
@@ -1130,7 +1131,7 @@ static const cmd_help_t cmd_help_table[] = {
        { CMD_ZONE_GET,        "<zone> [<owner> [<type>]]",              "Get zone data within the transaction." },
        { CMD_ZONE_SET,        "<zone>  <owner> [<ttl>] <type> <rdata>", "Add zone record within the transaction." },
        { CMD_ZONE_UNSET,      "<zone>  <owner> [<type> [<rdata>]]",     "Remove zone data within the transaction." },
-       { CMD_ZONE_PURGE,      "<zone>... [<filter>...]",                "Purge zone data, file, journal, and timers." },
+       { CMD_ZONE_PURGE,      "<zone>... [<filter>...]",                "Purge zone data, zone file, journal, timers, and KASP data." },
        { CMD_ZONE_STATS,      "<zone> [<module>[.<counter>]]",          "Show zone statistics counter(s)."},
        { CMD_ZONE_STATUS,     "<zone> [<filter>...]",                   "Show the zone status." },
        { "",                  "",                                       "" },