Skip to content

Commit 7750b6d

Browse files
committed
btrfs-progs: filesystem show: implement json format output
Implements JSON-formatted output for the `filesystem show` command using the `--format json` global option. Devices are in a `device-list` array, optionally showing if a device is missing. Signed-off-by: Jelle van der Waa <[email protected]>
1 parent ea90ae1 commit 7750b6d

File tree

2 files changed

+130
-49
lines changed

2 files changed

+130
-49
lines changed

Diff for: Documentation/dev/dev-json.rst

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Commands that support json output
1515

1616
* :command:`btrfs device stats`
1717
* :command:`btrfs filesystem df`
18+
* :command:`btrfs filesystem show`
1819
* :command:`btrfs qgroup show`
1920
* :command:`btrfs subvolume get-default`
2021
* :command:`btrfs subvolume list`

Diff for: cmds/filesystem.c

+129-49
Original file line numberDiff line numberDiff line change
@@ -310,36 +310,75 @@ static void splice_device_list(struct list_head *seed_devices,
310310
list_splice(seed_devices, all_devices);
311311
}
312312

313-
static void print_filesystem_info(char *label, char uuidbuf[BTRFS_UUID_UNPARSED_SIZE],
314-
u64 bytes_used, u64 num_devices,
315-
unsigned unit_mode)
313+
static const struct rowspec filesystem_show_data_rowspec[] = {
314+
{ .key = "label", .fmt = "%s", .out_json = "label" },
315+
{ .key = "uuid", .fmt = "%s", .out_json = "uuid" },
316+
{ .key = "num_devices", .fmt = "%llu", .out_json = "total_devices" },
317+
{ .key = "used", .fmt = "%llu", .out_json = "used" },
318+
/* device list */
319+
{ .key = "devid", .fmt = "%llu", .out_json = "devid" },
320+
{ .key = "size", .fmt = "%llu", .out_json = "size" },
321+
{ .key = "used", .fmt = "%llu", .out_json = "used" },
322+
{ .key = "path", .fmt = "%s", .out_json = "path" },
323+
{ .key = "missing", .fmt = "bool", .out_json = "missing" },
324+
ROWSPEC_END
325+
};
326+
327+
static void print_filesystem_info(struct format_ctx *fctx,
328+
char *label, char uuidbuf[BTRFS_UUID_UNPARSED_SIZE],
329+
u64 bytes_used, u64 num_devices,
330+
unsigned unit_mode)
316331
{
317-
if (label)
318-
pr_verbose(LOG_DEFAULT, "Label: '%s' ", label);
319-
else
320-
pr_verbose(LOG_DEFAULT, "Label: none ");
321-
322-
pr_verbose(LOG_DEFAULT, " uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
323-
num_devices,
324-
pretty_size_mode(bytes_used,
325-
unit_mode));
332+
if (bconf.output_format == CMD_FORMAT_JSON) {
333+
if (label)
334+
fmt_print(fctx, "label", label);
335+
else
336+
fmt_print(fctx, "label", "none");
337+
338+
fmt_print(fctx, "uuid", uuidbuf);
339+
fmt_print(fctx, "num_devices", num_devices);
340+
fmt_print(fctx, "used", bytes_used);
341+
} else {
342+
if (label)
343+
pr_verbose(LOG_DEFAULT, "Label: '%s' ", label);
344+
else
345+
pr_verbose(LOG_DEFAULT, "Label: none ");
346+
347+
pr_verbose(LOG_DEFAULT, " uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
348+
num_devices,
349+
pretty_size_mode(bytes_used,
350+
unit_mode));
351+
}
326352
}
327353

328-
static void print_filesystem_device(u64 devid, u64 total_bytes, u64 bytes_used,
354+
static void print_filesystem_device(struct format_ctx *fctx,
355+
u64 devid, u64 total_bytes, u64 bytes_used,
329356
char *path,
330357
bool missing,
331358
unsigned unit_mode)
332359
{
333-
pr_verbose(LOG_DEFAULT, "\tdevid %4llu size %s used %s path %s%s\n",
334-
devid,
335-
pretty_size_mode(total_bytes, unit_mode),
336-
pretty_size_mode(bytes_used, unit_mode),
337-
path,
338-
missing ? " MISSING" : "");
360+
if (bconf.output_format == CMD_FORMAT_JSON) {
361+
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);
362+
fmt_print(fctx, "devid", devid);
363+
fmt_print(fctx, "size", 0);
364+
fmt_print(fctx, "used", 0);
365+
fmt_print(fctx, "path", path);
366+
if (missing)
367+
fmt_print(fctx, "missing", 0);
368+
fmt_print_end_group(fctx, NULL);
369+
} else {
370+
pr_verbose(LOG_DEFAULT, "\tdevid %4llu size %s used %s path %s%s\n",
371+
devid,
372+
pretty_size_mode(total_bytes, unit_mode),
373+
pretty_size_mode(bytes_used, unit_mode),
374+
path,
375+
missing ? " MISSING" : "");
376+
}
339377
}
340378

341379
static void print_devices(struct btrfs_fs_devices *fs_devices,
342-
u64 *devs_found, unsigned unit_mode)
380+
u64 *devs_found, unsigned unit_mode,
381+
struct format_ctx *fctx)
343382
{
344383
struct btrfs_device *device;
345384
struct btrfs_fs_devices *cur_fs;
@@ -355,16 +394,17 @@ static void print_devices(struct btrfs_fs_devices *fs_devices,
355394

356395
list_sort(NULL, all_devices, cmp_device_id);
357396
list_for_each_entry(device, all_devices, dev_list) {
358-
print_filesystem_device(device->devid,
359-
device->total_bytes, device->bytes_used,
360-
device->name,
361-
false,
362-
unit_mode);
397+
print_filesystem_device(fctx, device->devid,
398+
device->total_bytes, device->bytes_used,
399+
device->name,
400+
false,
401+
unit_mode);
363402
(*devs_found)++;
364403
}
365404
}
366405

367-
static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
406+
static void print_one_uuid(struct format_ctx *fctx,
407+
struct btrfs_fs_devices *fs_devices,
368408
unsigned unit_mode)
369409
{
370410
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
@@ -380,14 +420,27 @@ static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
380420
dev_list);
381421
total = device->total_devs;
382422

383-
print_filesystem_info(device->label && device->label[0] ? device->label : NULL, uuidbuf,
384-
device->super_bytes_used, total,
385-
unit_mode);
423+
if (bconf.output_format == CMD_FORMAT_JSON)
424+
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);
425+
426+
print_filesystem_info(fctx, device->label && device->label[0] ? device->label : NULL, uuidbuf,
427+
device->super_bytes_used, total,
428+
unit_mode);
386429

387-
print_devices(fs_devices, &devs_found, unit_mode);
430+
if (bconf.output_format == CMD_FORMAT_JSON)
431+
fmt_print_start_group(fctx, "device-list", JSON_TYPE_ARRAY);
388432

389-
if (devs_found < total) {
390-
pr_verbose(LOG_DEFAULT, "\t*** Some devices missing\n");
433+
print_devices(fs_devices, &devs_found, unit_mode, fctx);
434+
435+
// TODO: global missing option?
436+
if (bconf.output_format == CMD_FORMAT_JSON) {
437+
fmt_print_end_group(fctx, NULL);
438+
fmt_print_end_group(fctx, "device-list");
439+
} else {
440+
if (devs_found < total) {
441+
pr_verbose(LOG_DEFAULT, "\t*** Some devices missing\n");
442+
}
443+
pr_verbose(LOG_DEFAULT, "\n");
391444
}
392445
}
393446

@@ -402,7 +455,8 @@ static u64 calc_used_bytes(struct btrfs_ioctl_space_args *si)
402455
return ret;
403456
}
404457

405-
static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
458+
static int print_one_fs(struct format_ctx *fctx,
459+
struct btrfs_ioctl_fs_info_args *fs_info,
406460
struct btrfs_ioctl_dev_info_args *dev_info,
407461
struct btrfs_ioctl_space_args *space_info,
408462
char *label, unsigned unit_mode)
@@ -419,10 +473,16 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
419473
else if (ret)
420474
return ret;
421475

476+
if (bconf.output_format == CMD_FORMAT_JSON)
477+
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);
478+
422479
uuid_unparse(fs_info->fsid, uuidbuf);
423-
print_filesystem_info(label && *label ? label : NULL, uuidbuf,
424-
calc_used_bytes(space_info), fs_info->num_devices,
425-
unit_mode);
480+
print_filesystem_info(fctx, label && *label ? label : NULL, uuidbuf,
481+
calc_used_bytes(space_info), fs_info->num_devices,
482+
unit_mode);
483+
484+
if (bconf.output_format == CMD_FORMAT_JSON)
485+
fmt_print_start_group(fctx, "device-list", JSON_TYPE_ARRAY);
426486

427487
for (i = 0; i < fs_info->num_devices; i++) {
428488
char *canonical_path;
@@ -432,7 +492,8 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
432492
/* Add check for missing devices even mounted */
433493
fd = open((char *)tmp_dev_info->path, O_RDONLY);
434494
if (fd < 0) {
435-
print_filesystem_device(tmp_dev_info->devid,
495+
print_filesystem_device(fctx,
496+
tmp_dev_info->devid,
436497
0, 0,
437498
(char *)tmp_dev_info->path,
438499
true,
@@ -441,19 +502,24 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
441502
}
442503
close(fd);
443504
canonical_path = path_canonicalize((char *)tmp_dev_info->path);
444-
print_filesystem_device(tmp_dev_info->devid,
445-
tmp_dev_info->total_bytes, tmp_dev_info->bytes_used,
446-
canonical_path,
447-
false,
448-
unit_mode);
505+
print_filesystem_device(fctx, tmp_dev_info->devid,
506+
tmp_dev_info->total_bytes, tmp_dev_info->bytes_used,
507+
canonical_path,
508+
false,
509+
unit_mode);
449510

450511
free(canonical_path);
451512
}
452513

514+
if (bconf.output_format == CMD_FORMAT_JSON) {
515+
fmt_print_end_group(fctx, "device-list");
516+
fmt_print_end_group(fctx, NULL);
517+
}
518+
453519
return 0;
454520
}
455521

456-
static int btrfs_scan_kernel(void *search, unsigned unit_mode)
522+
static int btrfs_scan_kernel(struct format_ctx *fctx, void *search, unsigned unit_mode)
457523
{
458524
int ret = 0, fd;
459525
int found = 0;
@@ -500,10 +566,10 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
500566
fd = open(mnt->mnt_dir, O_RDONLY);
501567
if ((fd != -1) && !get_df(fd, &space_info_arg)) {
502568
/* Put space between filesystem entries for readability. */
503-
if (found != 0)
569+
if (found != 0 && bconf.output_format != CMD_FORMAT_JSON)
504570
pr_verbose(LOG_DEFAULT, "\n");
505571

506-
print_one_fs(&fs_info_arg, dev_info_arg,
572+
print_one_fs(fctx, &fs_info_arg, dev_info_arg,
507573
space_info_arg, label, unit_mode);
508574
free(space_info_arg);
509575
memset(label, 0, sizeof(label));
@@ -767,6 +833,7 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
767833
LIST_HEAD(all_uuids);
768834
struct btrfs_fs_devices *fs_devices;
769835
struct btrfs_root *root = NULL;
836+
struct format_ctx fctx;
770837
char *search = NULL;
771838
char *canon_path = NULL;
772839
int ret;
@@ -810,6 +877,11 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
810877
if (check_argc_max(argc, optind + 1))
811878
return 1;
812879

880+
if (bconf.output_format == CMD_FORMAT_JSON) {
881+
fmt_start(&fctx, filesystem_show_data_rowspec, 1, 0);
882+
fmt_print_start_group(&fctx, "filesystem-list", JSON_TYPE_ARRAY);
883+
}
884+
813885
if (argc > optind) {
814886
search = argv[optind];
815887
if (*search == 0)
@@ -862,7 +934,7 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
862934
}
863935

864936
/* show mounted btrfs */
865-
ret = btrfs_scan_kernel(search, unit_mode);
937+
ret = btrfs_scan_kernel(&fctx, search, unit_mode);
866938
if (search && !ret) {
867939
/* since search is found we are done */
868940
goto out;
@@ -913,10 +985,10 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
913985

914986
list_for_each_entry(fs_devices, &all_uuids, fs_list) {
915987
/* Put space between filesystem entries for readability. */
916-
if (needs_newline)
988+
if (needs_newline && bconf.output_format != CMD_FORMAT_JSON)
917989
pr_verbose(LOG_DEFAULT, "\n");
918990

919-
print_one_uuid(fs_devices, unit_mode);
991+
print_one_uuid(&fctx, fs_devices, unit_mode);
920992
needs_newline = true;
921993
}
922994

@@ -930,13 +1002,21 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
9301002
free_fs_devices(fs_devices);
9311003
}
9321004
out:
1005+
if (bconf.output_format == CMD_FORMAT_JSON) {
1006+
fmt_print_end_group(&fctx, "filesystem-list");
1007+
fmt_end(&fctx);
1008+
}
9331009
free(canon_path);
9341010
if (root)
9351011
close_ctree(root);
9361012
free_seen_fsid(seen_fsid_hash);
9371013
return !!ret;
9381014
}
939-
static DEFINE_SIMPLE_COMMAND(filesystem_show, "show");
1015+
#if EXPERIMENTAL
1016+
static DEFINE_COMMAND_WITH_FLAGS(filesystem_show, "show", CMD_FORMAT_JSON);
1017+
#else
1018+
DEFINE_SIMPLE_COMMAND(filesystem_show, "show");
1019+
#endif
9401020

9411021
static const char * const cmd_filesystem_sync_usage[] = {
9421022
"btrfs filesystem sync <path>",

0 commit comments

Comments
 (0)