diff --git a/usr.sbin/devinfo/Makefile b/usr.sbin/devinfo/Makefile index f6506c176c9c6..55b234f183634 100644 --- a/usr.sbin/devinfo/Makefile +++ b/usr.sbin/devinfo/Makefile @@ -2,6 +2,6 @@ PACKAGE= devmatch PROG= devinfo MAN= devinfo.8 -LIBADD= devinfo +LIBADD= xo devinfo .include diff --git a/usr.sbin/devinfo/devinfo.c b/usr.sbin/devinfo/devinfo.c index ecd1c07dcf159..faf6df7b53e77 100644 --- a/usr.sbin/devinfo/devinfo.c +++ b/usr.sbin/devinfo/devinfo.c @@ -4,6 +4,7 @@ * Copyright (c) 2000, 2001 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. + * Copyright (c) 2024 KT Ullavik * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,12 +41,18 @@ #include #include #include + +#include #include "devinfo.h" static bool rflag; static bool vflag; +static int open_tag_count; +static char *last_res; static void print_indent(int); +static void print_kvlist(char *); +static char* xml_safe_string(char *); static void print_resource(struct devinfo_res *); static int print_device_matching_resource(struct devinfo_res *, void *); static int print_device_rman_resources(struct devinfo_rman *, void *); @@ -74,7 +81,46 @@ print_indent(int n) n = MIN((size_t)n, sizeof(buffer) - 1); memset(buffer, ' ', n); buffer[n] = '\0'; - printf("%s", buffer); + xo_emit("{Pa:%s}", buffer); +} + +/* + * Takes a list of key-value pairs in the form + * "key1=val1 key2=val2 ..." and prints them according + * to xo formatting. + */ +static void +print_kvlist(char *s) +{ + char *kv; + char *copy; + + if ((copy = strdup(s)) == NULL) + xo_err(1, "No memory!"); + + while ((kv = strsep(©, " ")) != NULL) { + char* k = strsep(&kv, "="); + xo_emit("{ea:%s/%s} {d:%s}={d:%s}", k, kv, k, kv); + } + free(copy); +} + +static char +*xml_safe_string(char *desc) +{ + int i; + char *s; + + if ((s = strdup(desc)) == NULL) { + xo_err(1, "No memory!"); + } + + for (i=0; s[i] != '\0'; i++) { + if (s[i] == ' ' || s[i] == '/') { + s[i] = '-'; + } + } + return s; } /* @@ -86,20 +132,28 @@ print_resource(struct devinfo_res *res) struct devinfo_rman *rman; bool hexmode; rman_res_t end; + char *safe_desc; rman = devinfo_handle_to_rman(res->dr_rman); hexmode = (rman->dm_size > 1000) || (rman->dm_size == 0); end = res->dr_start + res->dr_size - 1; + safe_desc = xml_safe_string(rman->dm_desc); + xo_open_instance(safe_desc); + if (hexmode) { - printf("0x%jx", res->dr_start); + xo_emit("{:start/0x%jx}", res->dr_start); if (res->dr_size > 1) - printf("-0x%jx", end); + xo_emit("{D:-}{d:end/0x%jx}", end); + xo_emit("{e:end/0x%jx}", end); } else { - printf("%ju", res->dr_start); + xo_emit("{:start/%ju}", res->dr_start); if (res->dr_size > 1) - printf("-%ju", end); + xo_emit("{D:-}{d:end/%ju}", end); + xo_emit("{e:end/%ju}", end); } + xo_close_instance(safe_desc); + free(safe_desc); } /* @@ -121,7 +175,7 @@ print_device_matching_resource(struct devinfo_res *res, void *arg) return(1); print_indent(ia->indent); print_resource(res); - printf("\n"); + xo_emit("\n"); } return(0); } @@ -134,6 +188,7 @@ print_device_rman_resources(struct devinfo_rman *rman, void *arg) { struct indent_arg *ia = (struct indent_arg *)arg; int indent; + char *safe_desc; indent = ia->indent; @@ -143,13 +198,18 @@ print_device_rman_resources(struct devinfo_rman *rman, void *arg) print_device_matching_resource, ia) != 0) { /* there are, print header */ + safe_desc = xml_safe_string(rman->dm_desc); print_indent(indent); - printf("%s:\n", rman->dm_desc); + xo_emit("{d:%s}:\n", rman->dm_desc); + xo_open_list(safe_desc); /* print resources */ ia->indent = indent + 4; devinfo_foreach_rman_resource(rman, print_device_matching_resource, ia); + + xo_close_list(safe_desc); + free(safe_desc); } ia->indent = indent; return(0); @@ -160,20 +220,40 @@ print_device_props(struct devinfo_dev *dev) { if (vflag) { if (*dev->dd_desc) { - printf(" <%s>", dev->dd_desc); + xo_emit(" <{d:%s}>", dev->dd_desc); + xo_emit("{e:description/%s}", dev->dd_desc); } if (*dev->dd_pnpinfo) { - printf(" pnpinfo %s", dev->dd_pnpinfo); + xo_open_container("pnpinfo"); + xo_emit("{D: pnpinfo}"); + + if ((strcmp(dev->dd_pnpinfo, "unknown") == 0)) + xo_emit("{D: unknown}"); + else + print_kvlist(dev->dd_pnpinfo); + + xo_close_container("pnpinfo"); } if (*dev->dd_location) { - printf(" at %s", dev->dd_location); + xo_open_container("location"); + xo_emit("{D: at}"); + print_kvlist(dev->dd_location); + xo_close_container("location"); } + + // If verbose, then always print state for json/xml. + if (!(dev->dd_flags & DF_ENABLED)) + xo_emit("{e:state/disabled}"); + else if (dev->dd_flags & DF_SUSPENDED) + xo_emit("{e:state/suspended}"); + else + xo_emit("{e:state/enabled}"); } if (!(dev->dd_flags & DF_ENABLED)) - printf(" (disabled)"); + xo_emit("{D: (disabled)}"); else if (dev->dd_flags & DF_SUSPENDED) - printf(" (suspended)"); + xo_emit("{D: (suspended)}"); } /* @@ -183,16 +263,20 @@ static int print_device(struct devinfo_dev *dev, void *arg) { struct indent_arg ia; - int indent; + int indent, ret; + const char* devname = dev->dd_name[0] ? dev->dd_name : "unknown"; bool printit = vflag || (dev->dd_name[0] != 0 && dev->dd_state >= DS_ATTACHED); if (printit) { indent = (int)(intptr_t)arg; print_indent(indent); - printf("%s", dev->dd_name[0] ? dev->dd_name : "unknown"); + + xo_open_container(devname); + xo_emit("{d:%s}", devname); + print_device_props(dev); - printf("\n"); + xo_emit("\n"); if (rflag) { ia.indent = indent + 4; ia.arg = dev; @@ -201,8 +285,13 @@ print_device(struct devinfo_dev *dev, void *arg) } } - return(devinfo_foreach_device_child(dev, print_device, + ret = (devinfo_foreach_device_child(dev, print_device, (void *)((char *)arg + 2))); + + if (printit) { + xo_close_container(devname); + } + return(ret); } /* @@ -214,6 +303,7 @@ print_rman_resource(struct devinfo_res *res, void *arg __unused) struct devinfo_dev *dev; struct devinfo_rman *rman; rman_res_t end; + char *res_str, *entry; bool hexmode; dev = devinfo_handle_to_device(res->dr_device); @@ -221,27 +311,42 @@ print_rman_resource(struct devinfo_res *res, void *arg __unused) hexmode = (rman->dm_size > 1000) || (rman->dm_size == 0); end = res->dr_start + res->dr_size - 1; - printf(" "); - if (hexmode) { if (res->dr_size > 1) - printf("0x%jx-0x%jx", res->dr_start, end); + asprintf(&res_str, "0x%jx-0x%jx", res->dr_start, end); else - printf("0x%jx", res->dr_start); + asprintf(&res_str, "0x%jx", res->dr_start); } else { if (res->dr_size > 1) - printf("%ju-%ju", res->dr_start, end); + asprintf(&res_str, "%ju-%ju", res->dr_start, end); else - printf("%ju", res->dr_start); + asprintf(&res_str, "%ju", res->dr_start); + } + + xo_emit("{P: }"); + + if (last_res == NULL) { + // First resource + xo_open_list(res_str); + } else if (strcmp(res_str, last_res) != 0) { + // We can't repeat json keys. So we keep an + // open list from the last iteration and only + // create a new list when see a new resource. + xo_close_list(last_res); + xo_open_list(res_str); } if ((dev != NULL) && (dev->dd_name[0] != 0)) { - printf(" (%s)", dev->dd_name); + asprintf(&entry, "{el:%s}{D:%s} {D:(%s)}\n", + res_str, res_str, dev->dd_name); + xo_emit(entry, dev->dd_name); } else { - printf(" ----"); + asprintf(&entry, "{el:%s}{D:%s} {D:----}\n", res_str, res_str); + xo_emit(entry, "----"); } - printf("\n"); + free(entry); + last_res = res_str; return(0); } @@ -251,8 +356,16 @@ print_rman_resource(struct devinfo_res *res, void *arg __unused) int print_rman(struct devinfo_rman *rman, void *arg __unused) { - printf("%s:\n", rman->dm_desc); + char* safe_desc = xml_safe_string(rman->dm_desc); + + xo_emit("{d:%s}:\n", rman->dm_desc); + xo_open_container(safe_desc); + devinfo_foreach_rman_resource(rman, print_rman_resource, 0); + + xo_close_list(last_res); + xo_close_container(safe_desc); + free(safe_desc); return(0); } @@ -261,12 +374,17 @@ print_device_path_entry(struct devinfo_dev *dev) { const char *devname = dev->dd_name[0] ? dev->dd_name : "unknown"; - printf("%s", devname); + xo_open_container(devname); + open_tag_count++; + xo_emit("{d:%s }", devname); print_device_props(dev); if (vflag) - printf("\n"); + xo_emit("\n"); } +/* + * Recurse until we find the right dev. On the way up we print path. + */ static int print_device_path(struct devinfo_dev *dev, void *xname) { @@ -280,7 +398,7 @@ print_device_path(struct devinfo_dev *dev, void *xname) rv = devinfo_foreach_device_child(dev, print_device_path, xname); if (rv == 1) { - printf(" "); + xo_emit("{P: }"); print_device_path_entry(dev); } return (rv); @@ -289,19 +407,26 @@ print_device_path(struct devinfo_dev *dev, void *xname) static void print_path(struct devinfo_dev *root, char *path) { - if (devinfo_foreach_device_child(root, print_device_path, (void *)path) == 0) - errx(1, "%s: Not found", path); + open_tag_count = 0; + if (devinfo_foreach_device_child(root, print_device_path, + (void *)path) == 0) + xo_errx(1, "%s: Not found", path); if (!vflag) - printf("\n"); + xo_emit("\n"); + + while (open_tag_count > 0) { + xo_close_container_d(); + open_tag_count--; + } } static void __dead2 usage(void) { - fprintf(stderr, "%s\n%s\n%s\n", - "usage: devinfo [-rv]", - " devinfo -u", - " devinfo -p dev [-v]"); + xo_error( + "usage: devinfo [-rv]\n" + " devinfo -u\n" + " devinfo -p dev [-v]\n"); exit(1); } @@ -313,6 +438,11 @@ main(int argc, char *argv[]) bool uflag; char *path = NULL; + argc = xo_parse_args(argc, argv); + if (argc < 0) { + exit(1); + } + uflag = false; while ((c = getopt(argc, argv, "p:ruv")) != -1) { switch(c) { @@ -338,20 +468,32 @@ main(int argc, char *argv[]) if ((rv = devinfo_init()) != 0) { errno = rv; - err(1, "devinfo_init"); + xo_err(1, "devinfo_init"); } if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL) - errx(1, "can't find root device"); + xo_errx(1, "can't find root device"); if (path) { + xo_set_flags(NULL, XOF_DTRT); + xo_open_container("device-path"); print_path(root, path); + xo_close_container("device-path"); } else if (uflag) { /* print resource usage? */ + xo_set_flags(NULL, XOF_DTRT); + xo_open_container("device-resources"); devinfo_foreach_rman(print_rman, NULL); + xo_close_container("device-resources"); } else { /* print device hierarchy */ + xo_open_container("device-information"); devinfo_foreach_device_child(root, print_device, (void *)0); + xo_close_container("device-information"); + } + + if (xo_finish() < 0) { + exit(1); } return(0); }