From b2ff260e15fd621974743c2d40411ef8abe5c21c Mon Sep 17 00:00:00 2001 From: Love Hornquist Astrand Date: Tue, 7 May 2013 16:47:45 -0700 Subject: [PATCH] klist --json support --- kuser/heimtools-commands.in | 10 +++ kuser/klist.c | 177 ++++++++++++++++++++++++++++---------------- lib/roken/rtbl.c | 101 +++++++++++++++++++++---- lib/roken/rtbl.h | 4 + 4 files changed, 213 insertions(+), 79 deletions(-) diff --git a/kuser/heimtools-commands.in b/kuser/heimtools-commands.in index 736669ca1..b22a8c58d 100644 --- a/kuser/heimtools-commands.in +++ b/kuser/heimtools-commands.in @@ -84,12 +84,22 @@ command = { help = "List all caches" } option = { + long = "json" + type = "flag" + help = "JSON formated output" + } + option = { long = "verbose" short = "v" type = "flag" help = "Verbose output" } option = { + long = "version" + type = "flag" + help = "version" + } + option = { name = "a" short = "a" type = "flag" diff --git a/kuser/klist.c b/kuser/klist.c index d36b39b6f..65ddf4ac3 100644 --- a/kuser/klist.c +++ b/kuser/klist.c @@ -34,7 +34,6 @@ */ #include "kuser_locl.h" -#include "rtbl.h" #include "parse_units.h" #include "heimtools-commands.h" @@ -130,13 +129,18 @@ print_cred(krb5_context context, krb5_creds *cred, rtbl_t ct, int do_flags) } static void -print_cred_verbose(krb5_context context, krb5_creds *cred) +print_cred_verbose(krb5_context context, krb5_creds *cred, int do_json) { size_t j; char *str; krb5_error_code ret; krb5_timestamp sec; + if (do_json) { /* XXX support more json formating later */ + printf("{ \"verbose-supported\" : false }"); + return; + } + krb5_timeofday (context, &sec); ret = krb5_unparse_name(context, cred->server, &str); @@ -150,8 +154,8 @@ print_cred_verbose(krb5_context context, krb5_creds *cred) exit(1); printf(N_("Client: %s\n", ""), str); free (str); - - { + + if (!krb5_is_config_principal(context, cred->client)) { Ticket t; size_t len; char *s; @@ -229,10 +233,11 @@ print_tickets (krb5_context context, krb5_principal principal, int do_verbose, int do_flags, - int do_hidden) + int do_hidden, + int do_json) { + char *str, *name, *fullname; krb5_error_code ret; - char *str, *name; krb5_cc_cursor cursor; krb5_creds creds; krb5_deltat sec; @@ -243,49 +248,53 @@ print_tickets (krb5_context context, if (ret) krb5_err (context, 1, ret, "krb5_unparse_name"); - printf ("%17s: %s:%s\n", - N_("Credentials cache", ""), - krb5_cc_get_type(context, ccache), - krb5_cc_get_name(context, ccache)); - printf ("%17s: %s\n", N_("Principal", ""), str); - - ret = krb5_cc_get_friendly_name(context, ccache, &name); - if (ret == 0) { - if (strcmp(name, str) != 0) - printf ("%17s: %s\n", N_("Friendly name", ""), name); - free(name); - } - free (str); - - if(do_verbose) { - printf ("%17s: %d\n", N_("Cache version", ""), - krb5_cc_get_version(context, ccache)); - } else { - krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET); - } - - ret = krb5_cc_get_kdc_offset(context, ccache, &sec); - - if (ret == 0 && do_verbose && sec != 0) { - char buf[BUFSIZ]; - int val; - int sig; + ret = krb5_cc_get_full_name(context, ccache, &fullname); + if (ret) + krb5_err (context, 1, ret, "krb5_cc_get_full_name"); - val = sec; - sig = 1; - if (val < 0) { - sig = -1; - val = -val; + if (!do_json) { + printf ("%17s: %s\n", N_("Credentials cache", ""), fullname); + printf ("%17s: %s\n", N_("Principal", ""), str); + + ret = krb5_cc_get_friendly_name(context, ccache, &name); + if (ret == 0) { + if (strcmp(name, str) != 0) + printf ("%17s: %s\n", N_("Friendly name", ""), name); + free(name); } + free (str); + + if(do_verbose) { + printf ("%17s: %d\n", N_("Cache version", ""), + krb5_cc_get_version(context, ccache)); + } else { + krb5_cc_set_flags(context, ccache, KRB5_TC_NOTICKET); + } + + ret = krb5_cc_get_kdc_offset(context, ccache, &sec); + + if (ret == 0 && do_verbose && sec != 0) { + char buf[BUFSIZ]; + int val; + int sig; + + val = (int)sec; + sig = 1; + if (val < 0) { + sig = -1; + val = -val; + } + + unparse_time (val, buf, sizeof(buf)); - unparse_time (val, buf, sizeof(buf)); - - printf ("%17s: %s%s\n", N_("KDC time offset", ""), - sig == -1 ? "-" : "", buf); + printf ("%17s: %s%s\n", N_("KDC time offset", ""), + sig == -1 ? "-" : "", buf); + } + printf("\n"); + } else { + printf ("{ \"cache\" : \"%s\", \"principal\" : \"%s\", ", fullname, str); } - printf("\n"); - ret = krb5_cc_start_seq_get (context, ccache, &cursor); if (ret) krb5_err(context, 1, ret, "krb5_cc_start_seq_get"); @@ -298,7 +307,13 @@ print_tickets (krb5_context context, rtbl_add_column(ct, COL_FLAGS, 0); rtbl_add_column(ct, COL_PRINCIPAL, 0); rtbl_set_separator(ct, " "); + if (do_json) { + rtbl_set_flags(ct, RTBL_JSON); + printf("\"tickets\" : "); + } } + if (do_verbose && do_json) + printf("\"tickets\" : ["); while ((ret = krb5_cc_next_cred (context, ccache, &cursor, @@ -306,7 +321,7 @@ print_tickets (krb5_context context, if (!do_hidden && krb5_is_config_principal(context, creds.server)) { ; }else if(do_verbose){ - print_cred_verbose(context, &creds); + print_cred_verbose(context, &creds, do_json); }else{ print_cred(context, &creds, ct, do_flags); } @@ -321,6 +336,11 @@ print_tickets (krb5_context context, rtbl_format(ct, stdout); rtbl_destroy(ct); } + if (do_json) { + if (do_verbose) + printf("]"); + printf("}"); + } } /* @@ -353,7 +373,7 @@ check_for_tgt (krb5_context context, ret = krb5_cc_retrieve_cred (context, ccache, 0, &pattern, &creds); krb5_free_principal (context, pattern.server); if (ret) { - if (ret == KRB5_CC_END) + if (ret == KRB5_CC_NOTFOUND) return 1; krb5_err (context, 1, ret, "krb5_cc_retrieve_cred"); } @@ -448,7 +468,8 @@ display_tokens(int do_verbose) static int display_v5_ccache (krb5_context context, krb5_ccache ccache, int do_test, int do_verbose, - int do_flags, int do_hidden) + int do_flags, int do_hidden, + int do_json) { krb5_error_code ret; krb5_principal principal; @@ -457,6 +478,10 @@ display_v5_ccache (krb5_context context, krb5_ccache ccache, ret = krb5_cc_get_principal (context, ccache, &principal); if (ret) { + if (do_json) { + printf("{}"); + return 0; + } if(ret == ENOENT) { if (!do_test) krb5_warnx(context, N_("No ticket file: %s", ""), @@ -469,7 +494,7 @@ display_v5_ccache (krb5_context context, krb5_ccache ccache, exit_status = check_for_tgt (context, ccache, principal, NULL); else print_tickets (context, ccache, principal, do_verbose, - do_flags, do_hidden); + do_flags, do_hidden, do_json); ret = krb5_cc_close (context, ccache); if (ret) @@ -485,9 +510,9 @@ display_v5_ccache (krb5_context context, krb5_ccache ccache, */ static int -list_caches(krb5_context context) +list_caches(krb5_context context, struct klist_options *opt) { - krb5_cc_cache_cursor cursor; + krb5_cccol_cursor cursor; const char *cdef_name; char *def_name; krb5_error_code ret; @@ -499,21 +524,25 @@ list_caches(krb5_context context) krb5_errx(context, 1, "krb5_cc_default_name"); def_name = strdup(cdef_name); - ret = krb5_cc_cache_get_first (context, NULL, &cursor); + ret = krb5_cccol_cursor_new(context, &cursor); if (ret == KRB5_CC_NOSUPP) return 0; else if (ret) krb5_err (context, 1, ret, "krb5_cc_cache_get_first"); ct = rtbl_create(); + rtbl_add_column(ct, COL_DEFCACHE, 0); rtbl_add_column(ct, COL_NAME, 0); rtbl_add_column(ct, COL_CACHENAME, 0); rtbl_add_column(ct, COL_EXPIRES, 0); rtbl_add_column(ct, COL_DEFCACHE, 0); rtbl_set_prefix(ct, " "); - rtbl_set_column_prefix(ct, COL_NAME, ""); + rtbl_set_column_prefix(ct, COL_DEFCACHE, ""); + rtbl_set_column_prefix(ct, COL_NAME, " "); + if (opt->json_flag) + rtbl_set_flags(ct, RTBL_JSON); - while (krb5_cc_cache_next (context, cursor, &id) == 0) { + while (krb5_cccol_cursor_next(context, cursor, &id) == 0) { krb5_principal principal = NULL; int expired = 0; char *name; @@ -529,21 +558,24 @@ list_caches(krb5_context context) if (ret == 0) { const char *str; char *fname; + rtbl_add_column_entry(ct, COL_NAME, name); - rtbl_add_column_entry(ct, COL_CACHENAME, - krb5_cc_get_name(context, id)); + free(name); + if (expired) str = N_(">>> Expired <<<", ""); else str = printable_time(t); rtbl_add_column_entry(ct, COL_EXPIRES, str); - free(name); ret = krb5_cc_get_full_name(context, id, &fname); if (ret) krb5_err (context, 1, ret, "krb5_cc_get_full_name"); - if (strcmp(fname, def_name) == 0) + rtbl_add_column_entry(ct, COL_CACHENAME, fname); + if (opt->json_flag) + ; + else if (strcmp(fname, def_name) == 0) rtbl_add_column_entry(ct, COL_DEFCACHE, "*"); else rtbl_add_column_entry(ct, COL_DEFCACHE, ""); @@ -555,12 +587,15 @@ list_caches(krb5_context context) krb5_free_principal(context, principal); } - krb5_cc_cache_end_seq_get(context, cursor); + krb5_cccol_cursor_free(context, &cursor); free(def_name); rtbl_format(ct, stdout); rtbl_destroy(ct); + if (opt->json_flag) + printf("\n"); + return 0; } @@ -582,8 +617,13 @@ klist(struct klist_options *opt, int argc, char **argv) opt->test_flag || opt->s_flag; + if(opt->version_flag) { + print_version(NULL); + exit(0); + } + if (opt->list_all_flag) { - exit_status = list_caches(heimtools_context); + exit_status = list_caches(heimtools_context, opt); return exit_status; } @@ -592,20 +632,29 @@ klist(struct klist_options *opt, int argc, char **argv) if (opt->all_content_flag) { krb5_cc_cache_cursor cursor; + int first = 1; ret = krb5_cc_cache_get_first(heimtools_context, NULL, &cursor); if (ret) krb5_err(heimtools_context, 1, ret, "krb5_cc_cache_get_first"); - + if (opt->json_flag) + printf("["); while (krb5_cc_cache_next(heimtools_context, cursor, &id) == 0) { + if (opt->json_flag && !first) + printf(","); + exit_status |= display_v5_ccache(heimtools_context, id, do_test, do_verbose, opt->flags_flag, - opt->hidden_flag); - printf("\n\n"); + opt->hidden_flag, opt->json_flag); + if (!opt->json_flag) + printf("\n\n"); + + first = 0; } krb5_cc_cache_end_seq_get(heimtools_context, cursor); - + if (opt->json_flag) + printf("]"); } else { if(opt->cache_string) { ret = krb5_cc_resolve(heimtools_context, opt->cache_string, &id); @@ -618,7 +667,7 @@ klist(struct klist_options *opt, int argc, char **argv) } exit_status = display_v5_ccache(heimtools_context, id, do_test, do_verbose, opt->flags_flag, - opt->hidden_flag); + opt->hidden_flag, opt->json_flag); } } diff --git a/lib/roken/rtbl.c b/lib/roken/rtbl.c index fe0fde662..c1f27baa3 100644 --- a/lib/roken/rtbl.c +++ b/lib/roken/rtbl.c @@ -34,6 +34,7 @@ #include #include "roken.h" +#include #include "rtbl.h" struct column_entry { @@ -188,7 +189,7 @@ column_compute_width (rtbl_t table, struct column_data *column) if(table->flags & RTBL_HEADER_STYLE_NONE) column->width = 0; else - column->width = strlen (column->header); + column->width = (int)strlen (column->header); for (i = 0; i < column->num_rows; i++) column->width = max (column->width, (int) strlen (column->rows[i].data)); } @@ -362,6 +363,18 @@ rtbl_add_column_entryv (rtbl_t table, const char *column, const char *fmt, ...) ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rtbl_format (rtbl_t table, FILE * f) { + char *str = rtbl_format_str(table); + if (str == NULL) + return ENOMEM; + fprintf(f, "%s", str); + free(str); + return 0; +} + +static char * +rtbl_format_pretty(rtbl_t table) +{ + struct rk_strpool *p = NULL; size_t i, j; for (i = 0; i < table->num_columns; i++) @@ -371,16 +384,18 @@ rtbl_format (rtbl_t table, FILE * f) struct column_data *c = table->columns[i]; if(table->column_separator != NULL && i > 0) - fprintf (f, "%s", table->column_separator); - fprintf (f, "%s", get_column_prefix (table, c)); - if(i == table->num_columns - 1 && c->suffix == NULL) + p = rk_strpoolprintf(p, "%s", table->column_separator); + p = rk_strpoolprintf(p, "%s", get_column_prefix (table, c)); + if (c == NULL) { + /* do nothing if no column */ + } else if(i == table->num_columns - 1 && c->suffix == NULL) /* last column, so no need to pad with spaces */ - fprintf (f, "%-*s", 0, c->header); + p = rk_strpoolprintf(p, "%-*s", 0, c->header); else - fprintf (f, "%-*s", (int)c->width, c->header); - fprintf (f, "%s", get_column_suffix (table, c)); + p = rk_strpoolprintf(p, "%-*s", (int)c->width, c->header); + p = rk_strpoolprintf(p, "%s", get_column_suffix (table, c)); } - fprintf (f, "\n"); + p = rk_strpoolprintf(p, "\n"); } for (j = 0;; j++) { @@ -403,7 +418,7 @@ rtbl_format (rtbl_t table, FILE * f) struct column_data *c = table->columns[i]; if(table->column_separator != NULL && i > 0) - fprintf (f, "%s", table->column_separator); + p = rk_strpoolprintf(p, "%s", table->column_separator); w = c->width; @@ -414,16 +429,72 @@ rtbl_format (rtbl_t table, FILE * f) else w = -w; } - fprintf (f, "%s", get_column_prefix (table, c)); + rk_strpoolprintf(p, "%s", get_column_prefix (table, c)); if (c->num_rows <= j) - fprintf (f, "%*s", w, ""); + p = rk_strpoolprintf(p, "%*s", w, ""); else - fprintf (f, "%*s", w, c->rows[j].data); - fprintf (f, "%s", get_column_suffix (table, c)); + p = rk_strpoolprintf(p, "%*s", w, c->rows[j].data); + p = rk_strpoolprintf(p, "%s", get_column_suffix (table, c)); } - fprintf (f, "\n"); + p = rk_strpoolprintf(p, "\n"); } - return 0; + + return rk_strpoolcollect(p); +} + +static char * +rtbl_format_json(rtbl_t table) +{ + struct rk_strpool *p = NULL; + size_t i, j; + int comma; + + p = rk_strpoolprintf(p, "["); + for (j = 0;; j++) { + int flag = 0; + + /* are there any more rows left? */ + for (i = 0; flag == 0 && i < table->num_columns; ++i) { + struct column_data *c = table->columns[i]; + + if (c->num_rows > j) { + ++flag; + break; + } + } + if (flag == 0) + break; + + p = rk_strpoolprintf(p, "%s{", j > 0 ? "," : ""); + + comma = 0; + for (i = 0; i < table->num_columns; i++) { + struct column_data *c = table->columns[i]; + + if (c->num_rows > j) { + char *header = c->header; + while (isspace((int)header[0])) /* trim off prefixed whitespace */ + header++; + p = rk_strpoolprintf(p, "%s\"%s\" : \"%s\"", + comma ? "," : "", header, + c->rows[j].data); + comma = 1; + } + } + p = rk_strpoolprintf(p, "}"); + } + p = rk_strpoolprintf(p, "]"); + + return rk_strpoolcollect(p); +} + +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +rtbl_format_str (rtbl_t table) +{ + if (table->flags & RTBL_JSON) + return rtbl_format_json(table); + + return rtbl_format_pretty(table); } #ifdef TEST diff --git a/lib/roken/rtbl.h b/lib/roken/rtbl.h index 549d3a8aa..c5c9f4f9f 100644 --- a/lib/roken/rtbl.h +++ b/lib/roken/rtbl.h @@ -61,6 +61,7 @@ typedef struct rtbl_data *rtbl_t; /* flags */ #define RTBL_HEADER_STYLE_NONE 1 +#define RTBL_JSON 2 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rtbl_add_column (rtbl_t, const char*, unsigned int); @@ -92,6 +93,9 @@ rtbl_destroy (rtbl_t); ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rtbl_format (rtbl_t, FILE*); +ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL +rtbl_format_str (rtbl_t); + ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL rtbl_get_flags (rtbl_t); -- 2.11.4.GIT