Skip to content

Commit a42f5cd

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 6ee776b commit a42f5cd

File tree

2 files changed

+129
-49
lines changed

2 files changed

+129
-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 list`
1819
* :command:`btrfs qgroup show`
1920
* :command:`btrfs subvolume get-default`
2021
* :command:`btrfs subvolume list`

Diff for: cmds/filesystem.c

+128-49
Original file line numberDiff line numberDiff line change
@@ -273,36 +273,75 @@ static void splice_device_list(struct list_head *seed_devices,
273273
list_splice(seed_devices, all_devices);
274274
}
275275

276-
static void print_filesystem_info(char *label, char uuidbuf[BTRFS_UUID_UNPARSED_SIZE],
277-
u64 bytes_used, u64 num_devices,
278-
unsigned unit_mode)
276+
static const struct rowspec filesystem_show_data_rowspec[] = {
277+
{ .key = "label", .fmt = "%s", .out_json = "label" },
278+
{ .key = "uuid", .fmt = "%s", .out_json = "uuid" },
279+
{ .key = "num_devices", .fmt = "%llu", .out_json = "total_devices" },
280+
{ .key = "used", .fmt = "%llu", .out_json = "used" },
281+
/* device list */
282+
{ .key = "devid", .fmt = "%llu", .out_json = "devid" },
283+
{ .key = "size", .fmt = "%llu", .out_json = "size" },
284+
{ .key = "used", .fmt = "%llu", .out_json = "used" },
285+
{ .key = "path", .fmt = "%s", .out_json = "path" },
286+
{ .key = "missing", .fmt = "bool", .out_json = "missing" },
287+
ROWSPEC_END
288+
};
289+
290+
static void print_filesystem_info(struct format_ctx *fctx,
291+
char *label, char uuidbuf[BTRFS_UUID_UNPARSED_SIZE],
292+
u64 bytes_used, u64 num_devices,
293+
unsigned unit_mode)
279294
{
280-
if (label)
281-
pr_verbose(LOG_DEFAULT, "Label: '%s' ", label);
282-
else
283-
pr_verbose(LOG_DEFAULT, "Label: none ");
284-
285-
pr_verbose(LOG_DEFAULT, " uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
286-
num_devices,
287-
pretty_size_mode(bytes_used,
288-
unit_mode));
295+
if (bconf.output_format == CMD_FORMAT_JSON) {
296+
if (label)
297+
fmt_print(fctx, "label", label);
298+
else
299+
fmt_print(fctx, "label", "none");
300+
301+
fmt_print(fctx, "uuid", uuidbuf);
302+
fmt_print(fctx, "num_devices", num_devices);
303+
fmt_print(fctx, "used", bytes_used);
304+
} else {
305+
if (label)
306+
pr_verbose(LOG_DEFAULT, "Label: '%s' ", label);
307+
else
308+
pr_verbose(LOG_DEFAULT, "Label: none ");
309+
310+
pr_verbose(LOG_DEFAULT, " uuid: %s\n\tTotal devices %llu FS bytes used %s\n", uuidbuf,
311+
num_devices,
312+
pretty_size_mode(bytes_used,
313+
unit_mode));
314+
}
289315
}
290316

291-
static void print_filesystem_device(u64 devid, u64 total_bytes, u64 bytes_used,
317+
static void print_filesystem_device(struct format_ctx *fctx,
318+
u64 devid, u64 total_bytes, u64 bytes_used,
292319
char *path,
293320
bool missing,
294321
unsigned unit_mode)
295322
{
296-
pr_verbose(LOG_DEFAULT, "\tdevid %4llu size %s used %s path %s%s\n",
297-
devid,
298-
pretty_size_mode(total_bytes, unit_mode),
299-
pretty_size_mode(bytes_used, unit_mode),
300-
path,
301-
missing ? " MISSING" : "");
323+
if (bconf.output_format == CMD_FORMAT_JSON) {
324+
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);
325+
fmt_print(fctx, "devid", devid);
326+
fmt_print(fctx, "size", 0);
327+
fmt_print(fctx, "used", 0);
328+
fmt_print(fctx, "path", path);
329+
if (missing)
330+
fmt_print(fctx, "missing", 0);
331+
fmt_print_end_group(fctx, NULL);
332+
} else {
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" : "");
339+
}
302340
}
303341

304342
static void print_devices(struct btrfs_fs_devices *fs_devices,
305-
u64 *devs_found, unsigned unit_mode)
343+
u64 *devs_found, unsigned unit_mode,
344+
struct format_ctx *fctx)
306345
{
307346
struct btrfs_device *device;
308347
struct btrfs_fs_devices *cur_fs;
@@ -318,16 +357,17 @@ static void print_devices(struct btrfs_fs_devices *fs_devices,
318357

319358
list_sort(NULL, all_devices, cmp_device_id);
320359
list_for_each_entry(device, all_devices, dev_list) {
321-
print_filesystem_device(device->devid,
322-
device->total_bytes, device->bytes_used,
323-
device->name,
324-
false,
325-
unit_mode);
360+
print_filesystem_device(fctx, device->devid,
361+
device->total_bytes, device->bytes_used,
362+
device->name,
363+
false,
364+
unit_mode);
326365
(*devs_found)++;
327366
}
328367
}
329368

330-
static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
369+
static void print_one_uuid(struct format_ctx *fctx,
370+
struct btrfs_fs_devices *fs_devices,
331371
unsigned unit_mode)
332372
{
333373
char uuidbuf[BTRFS_UUID_UNPARSED_SIZE];
@@ -343,16 +383,28 @@ static void print_one_uuid(struct btrfs_fs_devices *fs_devices,
343383
dev_list);
344384
total = device->total_devs;
345385

346-
print_filesystem_info(device->label && device->label[0] ? device->label : NULL, uuidbuf,
347-
device->super_bytes_used, total,
348-
unit_mode);
386+
if (bconf.output_format == CMD_FORMAT_JSON)
387+
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);
388+
389+
print_filesystem_info(fctx, device->label && device->label[0] ? device->label : NULL, uuidbuf,
390+
device->super_bytes_used, total,
391+
unit_mode);
349392

350-
print_devices(fs_devices, &devs_found, unit_mode);
393+
if (bconf.output_format == CMD_FORMAT_JSON)
394+
fmt_print_start_group(fctx, "device-list", JSON_TYPE_ARRAY);
351395

352-
if (devs_found < total) {
353-
pr_verbose(LOG_DEFAULT, "\t*** Some devices missing\n");
396+
print_devices(fs_devices, &devs_found, unit_mode, fctx);
397+
398+
// TODO: global missing option?
399+
if (bconf.output_format == CMD_FORMAT_JSON) {
400+
fmt_print_end_group(fctx, NULL);
401+
fmt_print_end_group(fctx, "device-list");
402+
} else {
403+
if (devs_found < total) {
404+
pr_verbose(LOG_DEFAULT, "\t*** Some devices missing\n");
405+
}
406+
pr_verbose(LOG_DEFAULT, "\n");
354407
}
355-
pr_verbose(LOG_DEFAULT, "\n");
356408
}
357409

358410
/* adds up all the used spaces as reported by the space info ioctl
@@ -366,7 +418,8 @@ static u64 calc_used_bytes(struct btrfs_ioctl_space_args *si)
366418
return ret;
367419
}
368420

369-
static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
421+
static int print_one_fs(struct format_ctx *fctx,
422+
struct btrfs_ioctl_fs_info_args *fs_info,
370423
struct btrfs_ioctl_dev_info_args *dev_info,
371424
struct btrfs_ioctl_space_args *space_info,
372425
char *label, unsigned unit_mode)
@@ -383,10 +436,16 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
383436
else if (ret)
384437
return ret;
385438

439+
if (bconf.output_format == CMD_FORMAT_JSON)
440+
fmt_print_start_group(fctx, NULL, JSON_TYPE_MAP);
441+
386442
uuid_unparse(fs_info->fsid, uuidbuf);
387-
print_filesystem_info(label && *label ? label : NULL, uuidbuf,
388-
calc_used_bytes(space_info), fs_info->num_devices,
389-
unit_mode);
443+
print_filesystem_info(fctx, label && *label ? label : NULL, uuidbuf,
444+
calc_used_bytes(space_info), fs_info->num_devices,
445+
unit_mode);
446+
447+
if (bconf.output_format == CMD_FORMAT_JSON)
448+
fmt_print_start_group(fctx, "device-list", JSON_TYPE_ARRAY);
390449

391450
for (i = 0; i < fs_info->num_devices; i++) {
392451
char *canonical_path;
@@ -396,7 +455,8 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
396455
/* Add check for missing devices even mounted */
397456
fd = open((char *)tmp_dev_info->path, O_RDONLY);
398457
if (fd < 0) {
399-
print_filesystem_device(tmp_dev_info->devid,
458+
print_filesystem_device(fctx,
459+
tmp_dev_info->devid,
400460
0, 0,
401461
(char *)tmp_dev_info->path,
402462
true,
@@ -405,20 +465,25 @@ static int print_one_fs(struct btrfs_ioctl_fs_info_args *fs_info,
405465
}
406466
close(fd);
407467
canonical_path = path_canonicalize((char *)tmp_dev_info->path);
408-
print_filesystem_device(tmp_dev_info->devid,
409-
tmp_dev_info->total_bytes, tmp_dev_info->bytes_used,
410-
canonical_path,
411-
false,
412-
unit_mode);
468+
print_filesystem_device(fctx, tmp_dev_info->devid,
469+
tmp_dev_info->total_bytes, tmp_dev_info->bytes_used,
470+
canonical_path,
471+
false,
472+
unit_mode);
413473

414474
free(canonical_path);
415475
}
416476

417-
pr_verbose(LOG_DEFAULT, "\n");
477+
if (bconf.output_format == CMD_FORMAT_JSON) {
478+
fmt_print_end_group(fctx, "device-list");
479+
fmt_print_end_group(fctx, NULL);
480+
} else {
481+
pr_verbose(LOG_DEFAULT, "\n");
482+
}
418483
return 0;
419484
}
420485

421-
static int btrfs_scan_kernel(void *search, unsigned unit_mode)
486+
static int btrfs_scan_kernel(struct format_ctx *fctx, void *search, unsigned unit_mode)
422487
{
423488
int ret = 0, fd;
424489
int found = 0;
@@ -464,7 +529,7 @@ static int btrfs_scan_kernel(void *search, unsigned unit_mode)
464529

465530
fd = open(mnt->mnt_dir, O_RDONLY);
466531
if ((fd != -1) && !get_df(fd, &space_info_arg)) {
467-
print_one_fs(&fs_info_arg, dev_info_arg,
532+
print_one_fs(fctx, &fs_info_arg, dev_info_arg,
468533
space_info_arg, label, unit_mode);
469534
free(space_info_arg);
470535
memset(label, 0, sizeof(label));
@@ -728,6 +793,7 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
728793
LIST_HEAD(all_uuids);
729794
struct btrfs_fs_devices *fs_devices;
730795
struct btrfs_root *root = NULL;
796+
struct format_ctx fctx;
731797
char *search = NULL;
732798
char *canon_path = NULL;
733799
int ret;
@@ -770,6 +836,11 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
770836
if (check_argc_max(argc, optind + 1))
771837
return 1;
772838

839+
if (bconf.output_format == CMD_FORMAT_JSON) {
840+
fmt_start(&fctx, filesystem_show_data_rowspec, 1, 0);
841+
fmt_print_start_group(&fctx, "filesystem-list", JSON_TYPE_ARRAY);
842+
}
843+
773844
if (argc > optind) {
774845
search = argv[optind];
775846
if (*search == 0)
@@ -822,7 +893,7 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
822893
}
823894

824895
/* show mounted btrfs */
825-
ret = btrfs_scan_kernel(search, unit_mode);
896+
ret = btrfs_scan_kernel(&fctx, search, unit_mode);
826897
if (search && !ret) {
827898
/* since search is found we are done */
828899
goto out;
@@ -866,7 +937,7 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
866937
}
867938

868939
list_for_each_entry(fs_devices, &all_uuids, fs_list)
869-
print_one_uuid(fs_devices, unit_mode);
940+
print_one_uuid(&fctx, fs_devices, unit_mode);
870941

871942
if (search && !found) {
872943
error("not a valid btrfs filesystem: %s", search);
@@ -878,13 +949,21 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
878949
free_fs_devices(fs_devices);
879950
}
880951
out:
952+
if (bconf.output_format == CMD_FORMAT_JSON) {
953+
fmt_print_end_group(&fctx, "filesystem-list");
954+
fmt_end(&fctx);
955+
}
881956
free(canon_path);
882957
if (root)
883958
close_ctree(root);
884959
free_seen_fsid(seen_fsid_hash);
885960
return !!ret;
886961
}
887-
static DEFINE_SIMPLE_COMMAND(filesystem_show, "show");
962+
#if EXPERIMENTAL
963+
static DEFINE_COMMAND_WITH_FLAGS(filesystem_show, "show", CMD_FORMAT_JSON);
964+
#else
965+
DEFINE_SIMPLE_COMMAND(filesystem_show, "show");
966+
#endif
888967

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

0 commit comments

Comments
 (0)