From: Daniel Salzman Date: Tue, 17 Aug 2021 06:14:04 +0000 (+0200) Subject: keymgr: add colored brief mode for listing keys X-Git-Tag: v3.3.dev~390^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6b9bec4602b09a2d44eaceebaa19342ca780d56c;p=thirdparty%2Fknot-dns.git keymgr: add colored brief mode for listing keys --- diff --git a/doc/man/keymgr.8in b/doc/man/keymgr.8in index 3aef98de2b..48739f5f1c 100644 --- a/doc/man/keymgr.8in +++ b/doc/man/keymgr.8in @@ -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. diff --git a/doc/man_keymgr.rst b/doc/man_keymgr.rst index e2eb6e277c..b6554df431 100644 --- a/doc/man_keymgr.rst +++ b/doc/man_keymgr.rst @@ -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. diff --git a/src/utils/keymgr/functions.c b/src/utils/keymgr/functions.c index 6de7143e96..55e1058df5 100644 --- a/src/utils/keymgr/functions.c +++ b/src/utils/keymgr/functions.c @@ -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; } diff --git a/src/utils/keymgr/functions.h b/src/utils/keymgr/functions.h index 23c9fb63f9..398088543f 100644 --- a/src/utils/keymgr/functions.h +++ b/src/utils/keymgr/functions.h @@ -22,6 +22,12 @@ #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); diff --git a/src/utils/keymgr/main.c b/src/utils/keymgr/main.c index 11e246b5c0..0da4397eb1 100644 --- a/src/utils/keymgr/main.c +++ b/src/utils/keymgr/main.c @@ -42,6 +42,7 @@ static void print_help(void) " %s -l\n" "\n" "Parameters:\n" + " -b, --brief List keys briefly.\n" " -c, --config Use a textual configuration file.\n" " (default %s)\n" " -C, --confdb 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);