diff mbox series

[5/7] btrfs-progs: subvol list: implement json format output

Message ID 20230813094555.106052-6-christoph@c8h4.io (mailing list archive)
State New, archived
Headers show
Series btrfs-progs: implement json output for subvolume subcommands | expand

Commit Message

Christoph Heiss Aug. 13, 2023, 9:45 a.m. UTC
Implements JSON-formatted output for the `subvolume list` command using
the `--format json` global option, much like it is implemented for other
commands.

Re-uses the `btrfs_list_layout` infrastructure to nicely fit it into the
existing formatting code.

A notable difference to the normal, text-based output is that in the
JSON output, timestamps include the timezone offset as well.

Signed-off-by: Christoph Heiss <christoph@c8h4.io>
---
 cmds/subvolume-list.c | 91 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 88 insertions(+), 3 deletions(-)

Comments

David Sterba Aug. 17, 2023, 7:59 p.m. UTC | #1
On Sun, Aug 13, 2023 at 11:45:00AM +0200, Christoph Heiss wrote:
> Implements JSON-formatted output for the `subvolume list` command using
> the `--format json` global option, much like it is implemented for other
> commands.
> 
> Re-uses the `btrfs_list_layout` infrastructure to nicely fit it into the
> existing formatting code.
> 
> A notable difference to the normal, text-based output is that in the
> JSON output, timestamps include the timezone offset as well.
> 
> Signed-off-by: Christoph Heiss <christoph@c8h4.io>
> ---
>  cmds/subvolume-list.c | 91 +++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 88 insertions(+), 3 deletions(-)
> 
> diff --git a/cmds/subvolume-list.c b/cmds/subvolume-list.c
> index 382b0676..be7faca6 100644
> --- a/cmds/subvolume-list.c
> +++ b/cmds/subvolume-list.c
> @@ -35,7 +35,9 @@
>  #include "common/open-utils.h"
>  #include "common/string-utils.h"
>  #include "common/utils.h"
> +#include "common/format-output.h"
>  #include "cmds/commands.h"
> +#include "cmds/subvolume.h"
>  
>  /*
>   * Naming of options:
> @@ -75,6 +77,8 @@ static const char * const cmd_subvolume_list_usage[] = {
>  	OPTLINE("--sort=gen,ogen,rootid,path", "list the subvolume in order of gen, ogen, rootid or path "
>  		"you also can add '+' or '-' in front of each items. "
>  		"(+:ascending, -:descending, ascending default)"),
> +	HELPINFO_INSERT_GLOBALS,
> +	HELPINFO_INSERT_FORMAT,
>  	NULL,
>  };
>  
> @@ -84,7 +88,8 @@ static const char * const cmd_subvolume_list_usage[] = {
>  enum btrfs_list_layout {
>  	BTRFS_LIST_LAYOUT_DEFAULT = 0,
>  	BTRFS_LIST_LAYOUT_TABLE,
> -	BTRFS_LIST_LAYOUT_RAW
> +	BTRFS_LIST_LAYOUT_RAW,
> +	BTRFS_LIST_LAYOUT_JSON

Json should not be a layout, that's relevant only for plain text output.
diff mbox series

Patch

diff --git a/cmds/subvolume-list.c b/cmds/subvolume-list.c
index 382b0676..be7faca6 100644
--- a/cmds/subvolume-list.c
+++ b/cmds/subvolume-list.c
@@ -35,7 +35,9 @@ 
 #include "common/open-utils.h"
 #include "common/string-utils.h"
 #include "common/utils.h"
+#include "common/format-output.h"
 #include "cmds/commands.h"
+#include "cmds/subvolume.h"
 
 /*
  * Naming of options:
@@ -75,6 +77,8 @@  static const char * const cmd_subvolume_list_usage[] = {
 	OPTLINE("--sort=gen,ogen,rootid,path", "list the subvolume in order of gen, ogen, rootid or path "
 		"you also can add '+' or '-' in front of each items. "
 		"(+:ascending, -:descending, ascending default)"),
+	HELPINFO_INSERT_GLOBALS,
+	HELPINFO_INSERT_FORMAT,
 	NULL,
 };
 
@@ -84,7 +88,8 @@  static const char * const cmd_subvolume_list_usage[] = {
 enum btrfs_list_layout {
 	BTRFS_LIST_LAYOUT_DEFAULT = 0,
 	BTRFS_LIST_LAYOUT_TABLE,
-	BTRFS_LIST_LAYOUT_RAW
+	BTRFS_LIST_LAYOUT_RAW,
+	BTRFS_LIST_LAYOUT_JSON
 };
 
 /*
@@ -1269,14 +1274,83 @@  static void print_all_subvol_info_tab_head(void)
 	}
 }
 
+static void print_subvol_json_key(struct format_ctx *fctx,
+				  const struct root_info *subv,
+				  const enum btrfs_list_column_enum column)
+{
+	const char *column_name;
+
+	UASSERT(0 <= column && column < BTRFS_LIST_ALL);
+
+	column_name = btrfs_list_columns[column].name;
+	switch (column) {
+	case BTRFS_LIST_OBJECTID:
+		fmt_print(fctx, column_name, subv->root_id);
+		break;
+	case BTRFS_LIST_GENERATION:
+		fmt_print(fctx, column_name, subv->gen);
+		break;
+	case BTRFS_LIST_OGENERATION:
+		fmt_print(fctx, column_name, subv->ogen);
+		break;
+	case BTRFS_LIST_PARENT:
+		fmt_print(fctx, column_name, subv->ref_tree);
+		break;
+	case BTRFS_LIST_TOP_LEVEL:
+		fmt_print(fctx, column_name, subv->top_id);
+		break;
+	case BTRFS_LIST_OTIME:
+		fmt_print(fctx, column_name, subv->otime);
+		break;
+	case BTRFS_LIST_UUID:
+		fmt_print(fctx, column_name, subv->uuid);
+		break;
+	case BTRFS_LIST_PUUID:
+		fmt_print(fctx, column_name, subv->puuid);
+		break;
+	case BTRFS_LIST_RUUID:
+		fmt_print(fctx, column_name, subv->ruuid);
+		break;
+	case BTRFS_LIST_PATH:
+		BUG_ON(!subv->full_path);
+		fmt_print(fctx, column_name, subv->full_path);
+		break;
+	default:
+		break;
+	}
+}
+
+static void print_one_subvol_info_json(struct format_ctx *fctx,
+				struct root_info *subv)
+{
+	int i;
+
+	fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);
+
+	for (i = 0; i < BTRFS_LIST_ALL; i++) {
+		if (!btrfs_list_columns[i].need_print)
+			continue;
+
+		print_subvol_json_key(fctx, subv, i);
+	}
+
+	fmt_print_end_group(fctx, NULL);
+}
+
+
 static void print_all_subvol_info(struct rb_root *sorted_tree,
 		  enum btrfs_list_layout layout, const char *raw_prefix)
 {
 	struct rb_node *n;
 	struct root_info *entry;
+	struct format_ctx fctx;
 
-	if (layout == BTRFS_LIST_LAYOUT_TABLE)
+	if (layout == BTRFS_LIST_LAYOUT_TABLE) {
 		print_all_subvol_info_tab_head();
+	} else if (layout == BTRFS_LIST_LAYOUT_JSON) {
+		fmt_start(&fctx, btrfs_subvolume_rowspec, 1, 0);
+		fmt_print_start_group(&fctx, "subvolume-list", JSON_TYPE_ARRAY);
+	}
 
 	n = rb_first(sorted_tree);
 	while (n) {
@@ -1296,10 +1370,18 @@  static void print_all_subvol_info(struct rb_root *sorted_tree,
 		case BTRFS_LIST_LAYOUT_RAW:
 			print_one_subvol_info_raw(entry, raw_prefix);
 			break;
+		case BTRFS_LIST_LAYOUT_JSON:
+			print_one_subvol_info_json(&fctx, entry);
+			break;
 		}
 next:
 		n = rb_next(n);
 	}
+
+	if (layout == BTRFS_LIST_LAYOUT_JSON) {
+		fmt_print_end_group(&fctx, "subvolume-list");
+		fmt_end(&fctx);
+	}
 }
 
 static int btrfs_list_subvols(int fd, struct rb_root *root_lookup)
@@ -1631,6 +1713,9 @@  static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
 	btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
 	btrfs_list_setup_print_column(BTRFS_LIST_PATH);
 
+	if (bconf.output_format == CMD_FORMAT_JSON)
+		layout = BTRFS_LIST_LAYOUT_JSON;
+
 	ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
 			layout, !is_list_all && !is_only_in_path, NULL);
 
@@ -1644,4 +1729,4 @@  out:
 		usage(cmd, 1);
 	return !!ret;
 }
-DEFINE_SIMPLE_COMMAND(subvolume_list, "list");
+DEFINE_COMMAND_WITH_FLAGS(subvolume_list, "list", CMD_FORMAT_JSON);