]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
keymgr: add colored brief mode for listing keys
authorDaniel Salzman <daniel.salzman@nic.cz>
Tue, 17 Aug 2021 06:14:04 +0000 (08:14 +0200)
committerDaniel Salzman <daniel.salzman@nic.cz>
Wed, 25 Aug 2021 15:20:36 +0000 (17:20 +0200)
doc/man/keymgr.8in
doc/man_keymgr.rst
src/utils/keymgr/functions.c
src/utils/keymgr/functions.h
src/utils/keymgr/main.c

index 3aef98de2b7979e94b4d9931999fa9fd2116abfe..48739f5f1c7fa69882b3b5bc7947ebc5b3202d8b 100644 (file)
@@ -59,6 +59,9 @@ bit length of the key by number (default: optimal length given by algorithm). Th
 TSIG key is only displayed on \fIstdout\fP: the command does not create a file, nor include the
 key in a keystore.
 .TP
+\fB\-b\fP, \fB\-\-brief\fP
+List keys briefly.
+.TP
 \fB\-l\fP, \fB\-\-list\fP
 Print the list of zones that have at least one key stored in the configured KASP
 database.
index e2eb6e277c81d9145c09fae6772a4e962c5bf5bb..b6554df43126ab84425928c7d29b61a2c239bab3 100644 (file)
@@ -36,6 +36,9 @@ Basic options
   TSIG key is only displayed on `stdout`: the command does not create a file, nor include the
   key in a keystore.
 
+**-b**, **--brief**
+   List keys briefly.
+
 **-l**, **--list**
   Print the list of zones that have at least one key stored in the configured KASP
   database.
index 6de7143e962af3bb00ace71a9462f59dc0b2fa99..55e1058df5dfea8caaca3f81f2304ae9dbfa3f15 100644 (file)
@@ -23,6 +23,7 @@
 #include "utils/keymgr/functions.h"
 #include "utils/keymgr/bind_privkey.h"
 #include "contrib/base64.h"
+#include "contrib/color.h"
 #include "contrib/ctype.h"
 #include "contrib/string.h"
 #include "contrib/strtonum.h"
@@ -832,34 +833,128 @@ int keymgr_set_timing(knot_kasp_key_t *key, int argc, char *argv[])
        return KNOT_EINVAL;
 }
 
-static void print_timer(const char *name, knot_time_t t, knot_time_print_t format,
-                        char separator)
+typedef struct {
+       const char *name;
+       size_t offset;
+} timer_ctx_t;
+
+static const timer_ctx_t timers[] = {
+       { "pre-active",    offsetof(knot_kasp_key_timing_t, pre_active) },
+       { "publish",       offsetof(knot_kasp_key_timing_t, publish) },
+       { "ready",         offsetof(knot_kasp_key_timing_t, ready) },
+       { "active",        offsetof(knot_kasp_key_timing_t, active) },
+       { "retire-active", offsetof(knot_kasp_key_timing_t, retire_active) },
+       { "retire",        offsetof(knot_kasp_key_timing_t, retire) },
+       { "post-active",   offsetof(knot_kasp_key_timing_t, post_active) },
+       { "revoke",        offsetof(knot_kasp_key_timing_t, revoke) },
+       { "remove",        offsetof(knot_kasp_key_timing_t, remove) },
+       { NULL }
+};
+
+static void print_key_brief(const knot_kasp_key_t *key, keymgr_list_params_t *params)
 {
-       static char buff[100];
-       if (knot_time_print(format, t, buff, sizeof(buff)) < 0) {
-               printf("%s=(error)%c", name, separator); // shall not happen
+       const bool c = params->color;
+
+       printf("%s %s%5u%s ",
+              key->id, COL_BOLD(c), dnssec_key_get_keytag(key->key), COL_RST(c));
+
+       printf("%s%s%s%s ",
+              COL_BOLD(c),
+              (key->is_ksk ? (key->is_zsk ? COL_YELW(c) : COL_RED(c)) : COL_GRN(c)),
+              (key->is_ksk ? (key->is_zsk ? "CSK" : "KSK") : "ZSK"),
+              COL_RST(c));
+
+       uint8_t alg = dnssec_key_get_algorithm(key->key);
+       const knot_lookup_t *alg_info = knot_lookup_by_id(knot_dnssec_alg_names, alg);
+       if (alg_info != NULL) {
+               printf("%s", alg_info->name);
+               if (alg <= KNOT_DNSSEC_ALG_RSASHA512) {
+                       printf("%s/%u%s", COL_DIM(c), dnssec_key_get_size(key->key), COL_RST(c));
+               }
        } else {
-               printf("%s=%s%c", name, buff, separator);
+               printf("ALGORITHM_%u", alg);
+       }
+
+       if (key->is_pub_only) {
+               printf(" %s%spublic-only%s", COL_BOLD(c), COL_MGNT(c), COL_RST(c));
        }
+
+       static char buf[100];
+       knot_time_t now = knot_time();
+       for (const timer_ctx_t *t = &timers[0]; t->name != NULL; t++) {
+               knot_time_t *val = (void *)(&key->timing) + t->offset;
+               if (*val == 0) {
+                       continue;
+               }
+               bool past = (knot_time_cmp(*val, now) <= 0);
+               const char *UNDR = past ? COL_UNDR(c) : "";
+               const char *BOLD = past ? "" : COL_BOLD(c);
+               for (const timer_ctx_t *t2 = t + 1; past && t2->name != NULL; t2++) {
+                       knot_time_t *val2 = (void *)(&key->timing) + t2->offset;
+                       if (knot_time_cmp(*val2, now) <= 0) {
+                               UNDR = "";
+                               break;
+                       }
+               }
+               (void)knot_time_print(params->format, *val, buf, sizeof(buf));
+               printf(" %s%s%s=%s%s%s", UNDR, t->name, COL_RST(c), BOLD, buf, COL_RST(c));
+       }
+       printf("\n");
 }
 
-int keymgr_list_keys(kdnssec_ctx_t *ctx, knot_time_print_t format)
+static void print_key_full(const knot_kasp_key_t *key, knot_time_print_t format)
 {
-       for (size_t i = 0; i < ctx->zone->num_keys; i++) {
-               knot_kasp_key_t *key = &ctx->zone->keys[i];
-               printf("%s ksk=%s zsk=%s tag=%05d algorithm=%-2d size=%-4u public-only=%s ", key->id,
-                      (key->is_ksk ? "yes" : "no "), (key->is_zsk ? "yes" : "no "),
-                      dnssec_key_get_keytag(key->key), (int)dnssec_key_get_algorithm(key->key),
-                      dnssec_key_get_size(key->key), (key->is_pub_only ? "yes" : "no "));
-               print_timer("pre-active",    key->timing.pre_active,     format, ' ');
-               print_timer("publish",       key->timing.publish,        format, ' ');
-               print_timer("ready",         key->timing.ready,          format, ' ');
-               print_timer("active",        key->timing.active,         format, ' ');
-               print_timer("retire-active", key->timing.retire_active,  format, ' ');
-               print_timer("retire",        key->timing.retire,         format, ' ');
-               print_timer("post-active",   key->timing.post_active,    format, ' ');
-               print_timer("revoke",        key->timing.revoke,         format, ' ');
-               print_timer("remove",        key->timing.remove,         format, '\n');
+       printf("%s ksk=%s zsk=%s tag=%05d algorithm=%-2d size=%-4u public-only=%s", key->id,
+              (key->is_ksk ? "yes" : "no "), (key->is_zsk ? "yes" : "no "),
+              dnssec_key_get_keytag(key->key), (int)dnssec_key_get_algorithm(key->key),
+              dnssec_key_get_size(key->key), (key->is_pub_only ? "yes" : "no "));
+
+       static char buf[100];
+       for (const timer_ctx_t *t = &timers[0]; t->name != NULL; t++) {
+               knot_time_t *val = (void *)(&key->timing) + t->offset;
+               (void)knot_time_print(format, *val, buf, sizeof(buf));
+               printf(" %s=%s", t->name, buf);
+       }
+       printf("\n");
+}
+
+typedef struct {
+       knot_time_t val;
+       const knot_kasp_key_t *key;
+} key_sort_item_t;
+
+static int key_sort(const void *a, const void *b)
+{
+       const key_sort_item_t *key_a = a;
+       const key_sort_item_t *key_b = b;
+       return knot_time_cmp(key_a->val, key_b->val);
+}
+
+int keymgr_list_keys(kdnssec_ctx_t *ctx, keymgr_list_params_t *params)
+{
+       if (ctx->zone->num_keys == 0) {
+               return KNOT_EOK;
+       }
+       if (params->brief) {
+               key_sort_item_t items[ctx->zone->num_keys];
+               for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+                       knot_kasp_key_t *key = &ctx->zone->keys[i];
+                       items[i].key = key;
+                       if (knot_time_cmp(key->timing.pre_active, key->timing.publish) < 0) {
+                               items[i].val = key->timing.pre_active;
+                       } else {
+                               items[i].val = key->timing.publish;
+                       }
+               }
+               qsort(&items, ctx->zone->num_keys, sizeof(items[0]), key_sort);
+               for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+                       print_key_brief(items[i].key, params);
+               }
+       } else {
+               for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+                       knot_kasp_key_t *key = &ctx->zone->keys[i];
+                       print_key_full(key, params->format);
+               }
        }
        return KNOT_EOK;
 }
index 23c9fb63f90c4874479b40f96dfbd23af0c92dd4..398088543fef0fd976153bc5acb17611a3226029 100644 (file)
 
 #define ERROR(msg, ...)        { fprintf(stderr, "Error: " msg, ##__VA_ARGS__); fflush(stderr); }
 
+typedef struct {
+       knot_time_print_t format;
+       bool brief;
+       bool color;
+} keymgr_list_params_t;
+
 int parse_timestamp(char *arg, knot_time_t *stamp);
 
 int keymgr_generate_key(kdnssec_ctx_t *ctx, int argc, char *argv[]);
@@ -48,7 +54,7 @@ int keymgr_foreign_key_id(char *argv[], knot_lmdb_db_t *kaspdb, knot_dname_t **k
 
 int keymgr_set_timing(knot_kasp_key_t *key, int argc, char *argv[]);
 
-int keymgr_list_keys(kdnssec_ctx_t *ctx, knot_time_print_t format);
+int keymgr_list_keys(kdnssec_ctx_t *ctx, keymgr_list_params_t *params);
 
 int keymgr_generate_ds(const knot_dname_t *dname, const knot_kasp_key_t *key);
 
index 11e246b5c095cc6f4104e206f2c3fb5e6b521ee7..0da4397eb1066a04c4f631b7855ff82c6b7d9c2e 100644 (file)
@@ -42,6 +42,7 @@ static void print_help(void)
               "  %s -l\n"
               "\n"
               "Parameters:\n"
+              "  -b, --brief              List keys briefly.\n"
               "  -c, --config <file>      Use a textual configuration file.\n"
               "                            (default %s)\n"
               "  -C, --confdb <dir>       Use a binary configuration database directory.\n"
@@ -114,7 +115,8 @@ static void print_help(void)
               PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR);
 }
 
-static int key_command(int argc, char *argv[], int opt_ind, knot_lmdb_db_t *kaspdb)
+static int key_command(int argc, char *argv[], int opt_ind, knot_lmdb_db_t *kaspdb,
+                       keymgr_list_params_t *list_params)
 {
        if (argc < opt_ind + 2) {
                ERROR("zone name and/or command not specified\n");
@@ -197,13 +199,13 @@ static int key_command(int argc, char *argv[], int opt_ind, knot_lmdb_db_t *kasp
                        }
                }
        } else if (strcmp(argv[1], "list") == 0) {
-               knot_time_print_t format = TIME_PRINT_UNIX;
+               list_params->format = TIME_PRINT_UNIX;
                if (argc > 2 && strcmp(argv[2], "human") == 0) {
-                       format = TIME_PRINT_HUMAN_MIXED;
+                       list_params->format = TIME_PRINT_HUMAN_MIXED;
                } else if (argc > 2 && strcmp(argv[2], "iso") == 0) {
-                       format = TIME_PRINT_ISO8601;
+                       list_params->format = TIME_PRINT_ISO8601;
                }
-               ret = keymgr_list_keys(&kctx, format);
+               ret = keymgr_list_keys(&kctx, list_params);
                print_ok_on_succes = false;
        } else if (strcmp(argv[1], "ds") == 0 || strcmp(argv[1], "dnskey") == 0) {
                int (*generate_rr)(const knot_dname_t *, const knot_kasp_key_t *) = keymgr_generate_dnskey;
@@ -362,13 +364,12 @@ static bool conf_initialized = false; // This is a singleton as well as conf() i
 
 int main(int argc, char *argv[])
 {
-       int ret, just_list = 0;
-
        struct option opts[] = {
                { "config",  required_argument, NULL, 'c' },
                { "confdb",  required_argument, NULL, 'C' },
                { "dir",     required_argument, NULL, 'd' },
                { "tsig",    required_argument, NULL, 't' },
+               { "brief",   no_argument,       NULL, 'b' },
                { "list",    no_argument,       NULL, 'l' },
                { "help",    no_argument,       NULL, 'h' },
                { "version", no_argument,       NULL, 'V' },
@@ -377,8 +378,12 @@ int main(int argc, char *argv[])
 
        tzset();
 
+       int ret;
+       bool just_list = false;
+       keymgr_list_params_t list_params = { 0 };
+
        int opt = 0, parm = 0;
-       while ((opt = getopt_long(argc, argv, "hVd:c:C:t:l", opts, NULL)) != -1) {
+       while ((opt = getopt_long(argc, argv, "hVd:c:C:t:lb", opts, NULL)) != -1) {
                switch (opt) {
                case 'h':
                        print_help();
@@ -414,7 +419,11 @@ int main(int argc, char *argv[])
                        }
                        return (ret == KNOT_EOK ? EXIT_SUCCESS : EXIT_FAILURE);
                case 'l':
-                       just_list = 1;
+                       just_list = true;
+                       break;
+               case 'b':
+                       list_params.brief = true;
+                       list_params.color = true; // TODO: disable if not stdout
                        break;
                default:
                        print_help();
@@ -446,7 +455,7 @@ int main(int argc, char *argv[])
        if (just_list) {
                ret = keymgr_list_zones(&kaspdb);
        } else {
-               ret = key_command(argc, argv, optind, &kaspdb);
+               ret = key_command(argc, argv, optind, &kaspdb, &list_params);
        }
 
        knot_lmdb_deinit(&kaspdb);