401 lines
12 KiB
Diff
401 lines
12 KiB
Diff
|
From bf4a8911c992291e6943d5e936732c290ea2abb9 Mon Sep 17 00:00:00 2001
|
||
|
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||
|
Date: Tue, 2 Aug 2022 10:38:56 +0100
|
||
|
Subject: [PATCH 2/3] info: Add limited colourized output
|
||
|
|
||
|
For terminal, non-JSON output, colourize some of the output. We can
|
||
|
work on making this better later. Use --no-colour (or output to a
|
||
|
non-terminal) to disable colours.
|
||
|
---
|
||
|
info/main.c | 25 ++++++++++++-
|
||
|
info/map.c | 95 ++++++++++++++++++++++++++++++++++++++----------
|
||
|
info/nbdinfo.pod | 11 ++++++
|
||
|
info/show.c | 38 ++++++++++++++-----
|
||
|
4 files changed, 138 insertions(+), 31 deletions(-)
|
||
|
|
||
|
diff --git a/info/main.c b/info/main.c
|
||
|
index 870abb4623..a4550e2294 100644
|
||
|
--- a/info/main.c
|
||
|
+++ b/info/main.c
|
||
|
@@ -1,5 +1,5 @@
|
||
|
/* NBD client library in userspace
|
||
|
- * Copyright (C) 2020-2021 Red Hat Inc.
|
||
|
+ * Copyright (C) 2020-2022 Red Hat Inc.
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Lesser General Public
|
||
|
@@ -30,6 +30,7 @@
|
||
|
|
||
|
#include <libnbd.h>
|
||
|
|
||
|
+#include "ansi-colours.h"
|
||
|
#include "version.h"
|
||
|
|
||
|
#include "nbdinfo.h"
|
||
|
@@ -37,6 +38,7 @@
|
||
|
const char *progname;
|
||
|
struct nbd_handle *nbd;
|
||
|
FILE *fp; /* output file descriptor */
|
||
|
+bool colour; /* --colour / --no-colour option */
|
||
|
bool list_all = false; /* --list option */
|
||
|
bool probe_content = false; /* --content / --no-content option */
|
||
|
bool json_output = false; /* --json option */
|
||
|
@@ -93,6 +95,8 @@ main (int argc, char *argv[])
|
||
|
HELP_OPTION = CHAR_MAX + 1,
|
||
|
LONG_OPTIONS,
|
||
|
SHORT_OPTIONS,
|
||
|
+ COLOUR_OPTION,
|
||
|
+ NO_COLOUR_OPTION,
|
||
|
CONTENT_OPTION,
|
||
|
NO_CONTENT_OPTION,
|
||
|
JSON_OPTION,
|
||
|
@@ -105,6 +109,14 @@ main (int argc, char *argv[])
|
||
|
const struct option long_options[] = {
|
||
|
{ "help", no_argument, NULL, HELP_OPTION },
|
||
|
{ "can", required_argument, NULL, CAN_OPTION },
|
||
|
+ { "color", no_argument, NULL, COLOUR_OPTION },
|
||
|
+ { "colors", no_argument, NULL, COLOUR_OPTION },
|
||
|
+ { "colour", no_argument, NULL, COLOUR_OPTION },
|
||
|
+ { "colours", no_argument, NULL, COLOUR_OPTION },
|
||
|
+ { "no-color", no_argument, NULL, NO_COLOUR_OPTION },
|
||
|
+ { "no-colors", no_argument, NULL, NO_COLOUR_OPTION },
|
||
|
+ { "no-colour", no_argument, NULL, NO_COLOUR_OPTION },
|
||
|
+ { "no-colours", no_argument, NULL, NO_COLOUR_OPTION },
|
||
|
{ "content", no_argument, NULL, CONTENT_OPTION },
|
||
|
{ "no-content", no_argument, NULL, NO_CONTENT_OPTION },
|
||
|
{ "is", required_argument, NULL, CAN_OPTION },
|
||
|
@@ -127,6 +139,7 @@ main (int argc, char *argv[])
|
||
|
bool list_okay = true;
|
||
|
|
||
|
progname = argv[0];
|
||
|
+ colour = isatty (STDOUT_FILENO);
|
||
|
|
||
|
for (;;) {
|
||
|
c = getopt_long (argc, argv, short_options, long_options, NULL);
|
||
|
@@ -156,6 +169,14 @@ main (int argc, char *argv[])
|
||
|
json_output = true;
|
||
|
break;
|
||
|
|
||
|
+ case COLOUR_OPTION:
|
||
|
+ colour = true;
|
||
|
+ break;
|
||
|
+
|
||
|
+ case NO_COLOUR_OPTION:
|
||
|
+ colour = false;
|
||
|
+ break;
|
||
|
+
|
||
|
case CONTENT_OPTION:
|
||
|
content_flag = true;
|
||
|
break;
|
||
|
@@ -288,10 +309,12 @@ main (int argc, char *argv[])
|
||
|
|
||
|
if (!json_output) {
|
||
|
if (protocol) {
|
||
|
+ ansi_colour (ANSI_FG_MAGENTA, fp);
|
||
|
fprintf (fp, "protocol: %s", protocol);
|
||
|
if (tls_negotiated >= 0)
|
||
|
fprintf (fp, " %s TLS", tls_negotiated ? "with" : "without");
|
||
|
fprintf (fp, "\n");
|
||
|
+ ansi_restore (fp);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
diff --git a/info/map.c b/info/map.c
|
||
|
index 39c5933bf5..a5aad95522 100644
|
||
|
--- a/info/map.c
|
||
|
+++ b/info/map.c
|
||
|
@@ -30,6 +30,7 @@
|
||
|
|
||
|
#include <libnbd.h>
|
||
|
|
||
|
+#include "ansi-colours.h"
|
||
|
#include "minmax.h"
|
||
|
#include "vector.h"
|
||
|
|
||
|
@@ -99,7 +100,9 @@ do_map (void)
|
||
|
|
||
|
/* Callback handling --map. */
|
||
|
static void print_one_extent (uint64_t offset, uint64_t len, uint32_t type);
|
||
|
-static char *extent_description (const char *metacontext, uint32_t type);
|
||
|
+static void extent_description (const char *metacontext, uint32_t type,
|
||
|
+ char **descr, bool *free_descr,
|
||
|
+ const char **fg, const char **bg);
|
||
|
|
||
|
static int
|
||
|
extent_callback (void *user_data, const char *metacontext,
|
||
|
@@ -169,15 +172,25 @@ static void
|
||
|
print_one_extent (uint64_t offset, uint64_t len, uint32_t type)
|
||
|
{
|
||
|
static bool comma = false;
|
||
|
- char *descr = extent_description (map, type);
|
||
|
+ char *descr;
|
||
|
+ bool free_descr;
|
||
|
+ const char *fg, *bg;
|
||
|
+
|
||
|
+ extent_description (map, type, &descr, &free_descr, &fg, &bg);
|
||
|
|
||
|
if (!json_output) {
|
||
|
+ if (fg)
|
||
|
+ ansi_colour (fg, fp);
|
||
|
+ if (bg)
|
||
|
+ ansi_colour (bg, fp);
|
||
|
fprintf (fp, "%10" PRIu64 " "
|
||
|
"%10" PRIu64 " "
|
||
|
"%3" PRIu32,
|
||
|
offset, len, type);
|
||
|
if (descr)
|
||
|
fprintf (fp, " %s", descr);
|
||
|
+ if (fg || bg)
|
||
|
+ ansi_restore (fp);
|
||
|
fprintf (fp, "\n");
|
||
|
}
|
||
|
else {
|
||
|
@@ -196,7 +209,8 @@ print_one_extent (uint64_t offset, uint64_t len, uint32_t type)
|
||
|
comma = true;
|
||
|
}
|
||
|
|
||
|
- free (descr);
|
||
|
+ if (free_descr)
|
||
|
+ free (descr);
|
||
|
}
|
||
|
|
||
|
/* --map --totals suboption */
|
||
|
@@ -237,14 +251,24 @@ print_totals (uint32_vector *entries, int64_t size)
|
||
|
}
|
||
|
|
||
|
if (c > 0) {
|
||
|
- char *descr = extent_description (map, type);
|
||
|
+ char *descr;
|
||
|
+ bool free_descr;
|
||
|
+ const char *fg, *bg;
|
||
|
double percent = 100.0 * c / size;
|
||
|
|
||
|
+ extent_description (map, type, &descr, &free_descr, &fg, &bg);
|
||
|
+
|
||
|
if (!json_output) {
|
||
|
+ if (fg)
|
||
|
+ ansi_colour (fg, fp);
|
||
|
+ if (bg)
|
||
|
+ ansi_colour (bg, fp);
|
||
|
fprintf (fp, "%10" PRIu64 " %5.1f%% %3" PRIu32,
|
||
|
c, percent, type);
|
||
|
if (descr)
|
||
|
fprintf (fp, " %s", descr);
|
||
|
+ if (fg || bg)
|
||
|
+ ansi_restore (fp);
|
||
|
fprintf (fp, "\n");
|
||
|
}
|
||
|
else {
|
||
|
@@ -264,7 +288,8 @@ print_totals (uint32_vector *entries, int64_t size)
|
||
|
comma = true;
|
||
|
}
|
||
|
|
||
|
- free (descr);
|
||
|
+ if (free_descr)
|
||
|
+ free (descr);
|
||
|
}
|
||
|
|
||
|
if (next_type == (uint64_t)UINT32_MAX + 1)
|
||
|
@@ -275,37 +300,67 @@ print_totals (uint32_vector *entries, int64_t size)
|
||
|
if (json_output) fprintf (fp, "\n]\n");
|
||
|
}
|
||
|
|
||
|
-static char *
|
||
|
-extent_description (const char *metacontext, uint32_t type)
|
||
|
+static void
|
||
|
+extent_description (const char *metacontext, uint32_t type,
|
||
|
+ char **descr, bool *free_descr,
|
||
|
+ const char **fg, const char **bg)
|
||
|
{
|
||
|
- char *ret;
|
||
|
-
|
||
|
if (strcmp (metacontext, "base:allocation") == 0) {
|
||
|
switch (type) {
|
||
|
- case 0: return strdup ("data");
|
||
|
- case 1: return strdup ("hole");
|
||
|
- case 2: return strdup ("zero");
|
||
|
- case 3: return strdup ("hole,zero");
|
||
|
+ case 0:
|
||
|
+ *descr = "data"; *free_descr = false;
|
||
|
+ *fg = ANSI_FG_BOLD_BLACK; *bg = NULL;
|
||
|
+ return;
|
||
|
+ case 1:
|
||
|
+ *descr = "hole"; *free_descr = false;
|
||
|
+ *fg = *bg = NULL;
|
||
|
+ return;
|
||
|
+ case 2:
|
||
|
+ *descr = "zero"; *free_descr = false;
|
||
|
+ *fg = *bg = NULL;
|
||
|
+ return;
|
||
|
+ case 3:
|
||
|
+ *descr = "hole,zero"; *free_descr = false;
|
||
|
+ *fg = *bg = NULL;
|
||
|
+ return;
|
||
|
}
|
||
|
}
|
||
|
else if (strncmp (metacontext, "qemu:dirty-bitmap:", 18) == 0) {
|
||
|
switch (type) {
|
||
|
- case 0: return strdup ("clean");
|
||
|
- case 1: return strdup ("dirty");
|
||
|
+ case 0:
|
||
|
+ *descr = "clean"; *free_descr = false;
|
||
|
+ *fg = ANSI_FG_GREEN; *bg = NULL;
|
||
|
+ return;
|
||
|
+ case 1:
|
||
|
+ *descr = "dirty"; *free_descr = false;
|
||
|
+ *fg = ANSI_FG_RED; *bg = NULL;
|
||
|
+ return;
|
||
|
}
|
||
|
}
|
||
|
else if (strcmp (metacontext, "qemu:allocation-depth") == 0) {
|
||
|
switch (type) {
|
||
|
- case 0: return strdup ("absent");
|
||
|
- case 1: return strdup ("local");
|
||
|
+ case 0:
|
||
|
+ *descr = "absent"; *free_descr = false;
|
||
|
+ *fg = *bg = NULL;
|
||
|
+ return;
|
||
|
+ case 1:
|
||
|
+ *descr = "local"; *free_descr = false;
|
||
|
+ *fg = ANSI_FG_BRIGHT_WHITE; *bg = ANSI_BG_BLACK;
|
||
|
+ return;
|
||
|
default:
|
||
|
- if (asprintf (&ret, "backing depth %u", type) == -1) {
|
||
|
+ if (asprintf (descr, "backing depth %u", type) == -1) {
|
||
|
perror ("asprintf");
|
||
|
exit (EXIT_FAILURE);
|
||
|
}
|
||
|
- return ret;
|
||
|
+ *free_descr = true;
|
||
|
+ *fg = NULL; *bg = ANSI_BG_LIGHT_GREY;
|
||
|
+ return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- return NULL; /* Don't know - description field will be omitted. */
|
||
|
+ /* Don't know - description field will be omitted. */
|
||
|
+ *descr = NULL;
|
||
|
+ *free_descr = false;
|
||
|
+ *fg = NULL;
|
||
|
+ *bg = NULL;
|
||
|
}
|
||
|
diff --git a/info/nbdinfo.pod b/info/nbdinfo.pod
|
||
|
index 4733ecd1f7..7dfb9edb60 100644
|
||
|
--- a/info/nbdinfo.pod
|
||
|
+++ b/info/nbdinfo.pod
|
||
|
@@ -330,6 +330,17 @@ L<nbd_can_df(3)>, L<nbd_can_fast_zero(3)>, L<nbd_can_flush(3)>,
|
||
|
L<nbd_can_fua(3)>, L<nbd_can_multi_conn(3)>, L<nbd_can_trim(3)>,
|
||
|
L<nbd_can_zero(3)>, L<nbd_is_read_only(3)>.
|
||
|
|
||
|
+=item B<--color>
|
||
|
+
|
||
|
+=item B<--colour>
|
||
|
+
|
||
|
+=item B<--no-color>
|
||
|
+
|
||
|
+=item B<--no-colour>
|
||
|
+
|
||
|
+Enable or disable ANSI colours in output. By default we use colours
|
||
|
+if the output seems to be a terminal, and disable them if not.
|
||
|
+
|
||
|
=item B<--content>
|
||
|
|
||
|
=item B<--no-content>
|
||
|
diff --git a/info/show.c b/info/show.c
|
||
|
index 3a436665bf..140220d8c6 100644
|
||
|
--- a/info/show.c
|
||
|
+++ b/info/show.c
|
||
|
@@ -28,11 +28,13 @@
|
||
|
|
||
|
#include <libnbd.h>
|
||
|
|
||
|
+#include "ansi-colours.h"
|
||
|
#include "human-size.h"
|
||
|
#include "string-vector.h"
|
||
|
|
||
|
#include "nbdinfo.h"
|
||
|
|
||
|
+static void show_boolean (const char *name, bool cond);
|
||
|
static int collect_context (void *opaque, const char *name);
|
||
|
static char *get_content (struct nbd_handle *, int64_t size);
|
||
|
|
||
|
@@ -119,6 +121,7 @@ show_one_export (struct nbd_handle *nbd, const char *desc,
|
||
|
content = get_content (nbd, size);
|
||
|
|
||
|
if (!json_output) {
|
||
|
+ ansi_colour (ANSI_FG_BRIGHT_BLUE, fp);
|
||
|
fprintf (fp, "export=");
|
||
|
/* Might as well use the JSON function to get an escaped string here ... */
|
||
|
print_json_string (export_name);
|
||
|
@@ -133,35 +136,38 @@ show_one_export (struct nbd_handle *nbd, const char *desc,
|
||
|
fprintf (fp, "\tcontent: %s\n", content);
|
||
|
if (uri)
|
||
|
fprintf (fp, "\turi: %s\n", uri);
|
||
|
+ ansi_restore (fp);
|
||
|
+ ansi_colour (ANSI_FG_BLUE, fp);
|
||
|
if (show_context) {
|
||
|
fprintf (fp, "\tcontexts:\n");
|
||
|
for (i = 0; i < contexts.len; ++i)
|
||
|
fprintf (fp, "\t\t%s\n", contexts.ptr[i]);
|
||
|
}
|
||
|
+ ansi_restore (fp);
|
||
|
+ ansi_colour (ANSI_FG_MAGENTA, fp);
|
||
|
if (is_rotational >= 0)
|
||
|
fprintf (fp, "\t%s: %s\n", "is_rotational",
|
||
|
is_rotational ? "true" : "false");
|
||
|
if (is_read_only >= 0)
|
||
|
fprintf (fp, "\t%s: %s\n", "is_read_only",
|
||
|
is_read_only ? "true" : "false");
|
||
|
+ ansi_restore (fp);
|
||
|
if (can_cache >= 0)
|
||
|
- fprintf (fp, "\t%s: %s\n", "can_cache", can_cache ? "true" : "false");
|
||
|
+ show_boolean ("can_cache", can_cache);
|
||
|
if (can_df >= 0)
|
||
|
- fprintf (fp, "\t%s: %s\n", "can_df", can_df ? "true" : "false");
|
||
|
+ show_boolean ("can_df", can_df);
|
||
|
if (can_fast_zero >= 0)
|
||
|
- fprintf (fp, "\t%s: %s\n", "can_fast_zero",
|
||
|
- can_fast_zero ? "true" : "false");
|
||
|
+ show_boolean ("can_fast_zero", can_fast_zero);
|
||
|
if (can_flush >= 0)
|
||
|
- fprintf (fp, "\t%s: %s\n", "can_flush", can_flush ? "true" : "false");
|
||
|
+ show_boolean ("can_flush", can_flush);
|
||
|
if (can_fua >= 0)
|
||
|
- fprintf (fp, "\t%s: %s\n", "can_fua", can_fua ? "true" : "false");
|
||
|
+ show_boolean ("can_fua", can_fua);
|
||
|
if (can_multi_conn >= 0)
|
||
|
- fprintf (fp, "\t%s: %s\n", "can_multi_conn",
|
||
|
- can_multi_conn ? "true" : "false");
|
||
|
+ show_boolean ("can_multi_conn", can_multi_conn);
|
||
|
if (can_trim >= 0)
|
||
|
- fprintf (fp, "\t%s: %s\n", "can_trim", can_trim ? "true" : "false");
|
||
|
+ show_boolean ("can_trim", can_trim);
|
||
|
if (can_zero >= 0)
|
||
|
- fprintf (fp, "\t%s: %s\n", "can_zero", can_zero ? "true" : "false");
|
||
|
+ show_boolean ("can_zero", can_zero);
|
||
|
if (block_minimum > 0)
|
||
|
fprintf (fp, "\t%s: %" PRId64 "\n", "block_size_minimum", block_minimum);
|
||
|
if (block_preferred > 0)
|
||
|
@@ -269,6 +275,18 @@ show_one_export (struct nbd_handle *nbd, const char *desc,
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
+/* Used for displaying booleans in non-JSON output. */
|
||
|
+static void
|
||
|
+show_boolean (const char *name, bool cond)
|
||
|
+{
|
||
|
+ if (cond)
|
||
|
+ ansi_colour (ANSI_FG_GREEN, fp);
|
||
|
+ else
|
||
|
+ ansi_colour (ANSI_FG_RED, fp);
|
||
|
+ fprintf (fp, "\t%s: %s\n", name, cond ? "true" : "false");
|
||
|
+ ansi_restore (fp);
|
||
|
+}
|
||
|
+
|
||
|
static int
|
||
|
collect_context (void *opaque, const char *name)
|
||
|
{
|
||
|
--
|
||
|
2.37.0.rc2
|
||
|
|