Skip to content

Commit

Permalink
cmd/flux-kvs: handle non-JSON values in "dir" cmd
Browse files Browse the repository at this point in the history
Problem: the KVS allows non-JSON values to be stored, but
flux kvs dir -R exits with an error if it encounters one.

For each value, if it decodes as JSON, format it like before.
But if it doesn't, print it directly instead of exiting.
If the output is a terminal, truncate long values so they fit
within the terminal width (reusing logic from the ls subcommand).
Also truncate values where they contain unprintable characters
or newlines.  Indicate truncated values by appending "...".

Fixes flux-framework#1158
  • Loading branch information
garlick committed Oct 26, 2017
1 parent 2892d92 commit cf37567
Showing 1 changed file with 95 additions and 33 deletions.
128 changes: 95 additions & 33 deletions src/cmd/flux-kvs.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ int cmd_move (optparse_t *p, int argc, char **argv);
int cmd_dir (optparse_t *p, int argc, char **argv);
int cmd_ls (optparse_t *p, int argc, char **argv);

static void dump_kvs_dir (const flux_kvsdir_t *dir, bool Ropt, bool dopt);
static int get_window_width (optparse_t *p, int fd);
static void dump_kvs_dir (const flux_kvsdir_t *dir, int maxcol,
bool Ropt, bool dopt);

#define min(a,b) ((a)<(b)?(a):(b))

Expand Down Expand Up @@ -83,6 +85,9 @@ static struct optparse_option dir_opts[] = {
{ .name = "directory", .key = 'd', .has_arg = 0,
.usage = "List directory entries and not values",
},
{ .name = "width", .key = 'w', .has_arg = 1,
.usage = "Set output width to COLS. 0 means no limit",
},
OPTPARSE_TABLE_END
};

Expand Down Expand Up @@ -154,7 +159,7 @@ static struct optparse_subcommand subcommands[] = {
put_opts
},
{ "dir",
"[-R] [-d] [key]",
"[-R] [-d] [-w COLS] [key]",
"Display all keys under directory",
cmd_dir,
0,
Expand Down Expand Up @@ -304,37 +309,85 @@ int main (int argc, char *argv[])
return (exitval);
}

static void output_key_json_object (const char *key, json_t *o)
static void kv_printf (const char *key, int maxcol, const char *fmt, ...)
{
va_list ap;
int rc;
char *val, *kv, *p;
bool overflow = false;

assert (maxcol == 0 || maxcol > 3);

va_start (ap, fmt);
rc = vasprintf (&val, fmt, ap);
va_end (ap);

if (rc < 0)
log_err_exit ("%s", __FUNCTION__);

if (asprintf (&kv, "%s%s%s", key ? key : "",
key ? " = " : "",
val) < 0)
log_err_exit ("%s", __FUNCTION__);

/* There will be no truncation of output if maxcol = 0.
* If truncation is enabled, ensure that at most maxcol columns are used.
* Truncate earlier if value contains a convenient newline break.
* Finally, truncate on first non-printable character.
*/
if (maxcol != 0) {
if (strlen (kv) > maxcol - 3) {
kv[maxcol - 3] = '\0';
overflow = true;
}
if ((p = strchr (kv, '\n'))) {
*p = '\0';
overflow = true;
}
for (p = kv; *p != '\0'; p++) {
if (!isprint (*p)) {
*p = '\0';
overflow = true;
break;
}
}
}
printf ("%s%s\n", kv,
overflow ? "..." : "");

free (val);
free (kv);
}

static void output_key_json_object (const char *key, json_t *o, int maxcol)
{
char *s;
if (key)
printf ("%s = ", key);

switch (json_typeof (o)) {
case JSON_NULL:
printf ("nil\n");
kv_printf (key, maxcol, "nil");
break;
case JSON_TRUE:
printf ("true\n");
kv_printf (key, maxcol, "true");
break;
case JSON_FALSE:
printf ("false\n");
kv_printf (key, maxcol, "false");
break;
case JSON_REAL:
printf ("%f\n", json_real_value (o));
kv_printf (key, maxcol, "%f", json_real_value (o));
break;
case JSON_INTEGER:
printf ("%lld\n", (long long)json_integer_value (o));
kv_printf (key, maxcol, "%lld", (long long)json_integer_value (o));
break;
case JSON_STRING:
printf ("%s\n", json_string_value (o));
kv_printf (key, maxcol, "%s", json_string_value (o));
break;
case JSON_ARRAY:
case JSON_OBJECT:
default:
if (!(s = json_dumps (o, JSON_SORT_KEYS)))
log_msg_exit ("json_dumps failed");
printf ("%s\n", s);
kv_printf (key, maxcol, "%s", s);
free (s);
break;
}
Expand All @@ -352,7 +405,7 @@ static void output_key_json_str (const char *key,
if (!(o = json_loads (json_str, JSON_DECODE_ANY, &error)))
log_msg_exit ("%s: %s (line %d column %d)",
arg, error.text, error.line, error.column);
output_key_json_object (key, o);
output_key_json_object (key, o, 0);
json_decref (o);
}

Expand Down Expand Up @@ -656,7 +709,7 @@ static void watch_dump_kvsdir (flux_kvsdir_t *dir, bool Ropt, bool dopt,
return;
}

dump_kvs_dir (dir, Ropt, dopt);
dump_kvs_dir (dir, 0, Ropt, dopt);
printf ("%s\n", WATCH_DIR_SEPARATOR);
fflush (stdout);
}
Expand Down Expand Up @@ -811,23 +864,24 @@ int cmd_dropcache (optparse_t *p, int argc, char **argv)
return (0);
}

static void dump_kvs_val (const char *key, const char *json_str)
static void dump_kvs_val (const char *key, int maxcol, const char *value)
{
json_t *o;
json_error_t error;

if (!json_str)
json_str = "null";
if (!(o = json_loads (json_str, JSON_DECODE_ANY, &error))) {
printf ("%s: %s (line %d column %d)\n",
key, error.text, error.line, error.column);
return;
if (!value) {
kv_printf (key, maxcol, "");
}
else if ((o = json_loads (value, JSON_DECODE_ANY, NULL))) {
output_key_json_object (key, o, maxcol);
json_decref (o);
}
else {
kv_printf (key, maxcol, value);
}
output_key_json_object (key, o);
json_decref (o);
}

static void dump_kvs_dir (const flux_kvsdir_t *dir, bool Ropt, bool dopt)
static void dump_kvs_dir (const flux_kvsdir_t *dir, int maxcol,
bool Ropt, bool dopt)
{
const char *rootref = flux_kvsdir_rootref (dir);
flux_t *h = flux_kvsdir_handle (dir);
Expand All @@ -853,17 +907,23 @@ static void dump_kvs_dir (const flux_kvsdir_t *dir, bool Ropt, bool dopt)
if (!(f = flux_kvs_lookupat (h, FLUX_KVS_READDIR, key, rootref))
|| flux_kvs_lookup_get_dir (f, &ndir) < 0)
log_err_exit ("%s", key);
dump_kvs_dir (ndir, Ropt, dopt);
dump_kvs_dir (ndir, maxcol, Ropt, dopt);
flux_future_destroy (f);
} else
printf ("%s.\n", key);
} else {
if (!dopt) {
const char *json_str;
if (!(f = flux_kvs_lookupat (h, 0, key, rootref))
|| flux_kvs_lookup_get (f, &json_str) < 0)
const char *value;
const void *buf;
int len;
if (!(f = flux_kvs_lookupat (h, 0, key, rootref)))
log_err_exit ("%s", key);
if (flux_kvs_lookup_get (f, &value) == 0) // null terminated
dump_kvs_val (key, maxcol, value);
else if (flux_kvs_lookup_get_raw (f, &buf, &len) == 0)
kv_printf (key, maxcol, "%.*s", len, buf);
else
log_err_exit ("%s", key);
dump_kvs_val (key, json_str);
flux_future_destroy (f);
}
else
Expand All @@ -877,6 +937,7 @@ static void dump_kvs_dir (const flux_kvsdir_t *dir, bool Ropt, bool dopt)
int cmd_dir (optparse_t *p, int argc, char **argv)
{
flux_t *h = (flux_t *)optparse_get_data (p, "flux_handle");
int maxcol = get_window_width (p, STDOUT_FILENO);
bool Ropt;
bool dopt;
const char *key;
Expand All @@ -897,7 +958,7 @@ int cmd_dir (optparse_t *p, int argc, char **argv)
if (!(f = flux_kvs_lookup (h, FLUX_KVS_READDIR, key))
|| flux_kvs_lookup_get_dir (f, &dir) < 0)
log_err_exit ("%s", key);
dump_kvs_dir (dir, Ropt, dopt);
dump_kvs_dir (dir, maxcol, Ropt, dopt);
flux_future_destroy (f);
return (0);
}
Expand Down Expand Up @@ -948,8 +1009,6 @@ static int get_window_width (optparse_t *p, int fd)
int width;
const char *s;

if (optparse_hasopt (p, "1"))
return 1;
if ((width = optparse_get_int (p, "width", -1)) >= 0)
return width;
if (ioctl (fd, TIOCGWINSZ, &w) == 0)
Expand Down Expand Up @@ -1181,6 +1240,9 @@ int cmd_ls (optparse_t *p, int argc, char **argv)
bool print_vspace = false; // print vertical space before label
int rc = 0;

if (optparse_hasopt (p, "1"))
win_width = 1;

if (!(dirs = zlist_new ()) || !(singles = zlist_new ()))
log_err_exit ("zlist_new");

Expand Down

0 comments on commit cf37567

Please sign in to comment.