diff options
Diffstat (limited to 'usr.sbin/devinfo/devinfo.c')
| -rw-r--r-- | usr.sbin/devinfo/devinfo.c | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/usr.sbin/devinfo/devinfo.c b/usr.sbin/devinfo/devinfo.c new file mode 100644 index 000000000000..4163151ec840 --- /dev/null +++ b/usr.sbin/devinfo/devinfo.c @@ -0,0 +1,507 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Print information about system device configuration. + */ + +#include <sys/param.h> + +#include <err.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libxo/xo.h> +#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 *); +static void print_device_props(struct devinfo_dev *); +static int print_device(struct devinfo_dev *, void *); +static int print_rman_resource(struct devinfo_res *, void *); +static int print_rman(struct devinfo_rman *, void *); +static int print_device_path(struct devinfo_dev *, void *); +static void print_path(struct devinfo_dev *, char *); +static void usage(void); + +struct indent_arg +{ + int indent; + void *arg; +}; + + +static void +print_indent(int n) +{ + static char buffer[1024]; + + if (n < 1) + return; + n = MIN((size_t)n, sizeof(buffer) - 1); + memset(buffer, ' ', n); + buffer[n] = '\0'; + 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:key/%s}={d:value/%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; +} + +/* + * Print a resource. + */ +void +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) { + xo_emit("{:start/0x%jx}", res->dr_start); + if (res->dr_size > 1) + xo_emit("{D:-}{d:end/0x%jx}", end); + xo_emit("{e:end/0x%jx}", end); + } else { + xo_emit("{:start/%ju}", res->dr_start); + if (res->dr_size > 1) + xo_emit("{D:-}{d:end/%ju}", end); + xo_emit("{e:end/%ju}", end); + } + xo_close_instance(safe_desc); + free(safe_desc); +} + +/* + * Print resource information if this resource matches the + * given device. + * + * If the given indent is 0, return an indicator that a matching + * resource exists. + */ +int +print_device_matching_resource(struct devinfo_res *res, void *arg) +{ + struct indent_arg *ia = (struct indent_arg *)arg; + struct devinfo_dev *dev = (struct devinfo_dev *)ia->arg; + + if (devinfo_handle_to_device(res->dr_device) == dev) { + /* in 'detect' mode, found a match */ + if (ia->indent == 0) + return(1); + print_indent(ia->indent); + print_resource(res); + xo_emit("\n"); + } + return(0); +} + +/* + * Print resource information for this device and resource manager. + */ +int +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; + + /* check whether there are any resources matching this device */ + ia->indent = 0; + if (devinfo_foreach_rman_resource(rman, + print_device_matching_resource, ia) != 0) { + + /* there are, print header */ + safe_desc = xml_safe_string(rman->dm_desc); + print_indent(indent); + xo_emit("<{:description/%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); +} + +static void +print_device_props(struct devinfo_dev *dev) +{ + if (vflag) { + if (*dev->dd_desc) { + xo_emit("<{:description/%s}>", dev->dd_desc); + } + if (*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) { + 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)) + xo_emit("{D: (disabled)}"); + else if (dev->dd_flags & DF_SUSPENDED) + xo_emit("{D: (suspended)}"); +} + +/* + * Print information about a device. + */ +static int +print_device(struct devinfo_dev *dev, void *arg) +{ + struct indent_arg ia; + 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); + + xo_open_container(devname); + xo_emit("{d:devicename/%s}", devname); + + print_device_props(dev); + xo_emit("\n"); + if (rflag) { + ia.indent = indent + 4; + ia.arg = dev; + devinfo_foreach_rman(print_device_rman_resources, + (void *)&ia); + } + } + + ret = (devinfo_foreach_device_child(dev, print_device, + (void *)((char *)arg + 2))); + + if (printit) { + xo_close_container(devname); + } + return(ret); +} + +/* + * Print information about a resource under a resource manager. + */ +int +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 = NULL; + bool hexmode; + + dev = devinfo_handle_to_device(res->dr_device); + 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; + + if (hexmode) { + if (res->dr_size > 1) + asprintf(&res_str, "0x%jx-0x%jx", res->dr_start, end); + else + asprintf(&res_str, "0x%jx", res->dr_start); + } else { + if (res->dr_size > 1) + asprintf(&res_str, "%ju-%ju", res->dr_start, end); + else + 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); + } + + dev = devinfo_handle_to_device(res->dr_device); + if (dev != NULL) { + if (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(" (unknown)"); + if (vflag && *dev->dd_pnpinfo) + printf(" pnpinfo %s", dev->dd_pnpinfo); + if (vflag && *dev->dd_location) + printf(" at %s", dev->dd_location); + } + } else { + asprintf(&entry, "{el:%s}{D:%s} {D:----}\n", res_str, res_str); + xo_emit(entry, "----"); + } + free(entry); + last_res = res_str; + return(0); +} + +/* + * Print information about a resource manager. + */ +int +print_rman(struct devinfo_rman *rman, void *arg __unused) +{ + char* safe_desc = xml_safe_string(rman->dm_desc); + + xo_emit("<{:description/%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); +} + +static void +print_device_path_entry(struct devinfo_dev *dev) +{ + const char *devname = dev->dd_name[0] ? dev->dd_name : "unknown"; + + xo_open_container(devname); + open_tag_count++; + xo_emit("{:devicename/%s} ", devname); + print_device_props(dev); + if (vflag) + 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) +{ + const char *name = xname; + int rv; + + if (strcmp(dev->dd_name, name) == 0) { + print_device_path_entry(dev); + return (1); + } + + rv = devinfo_foreach_device_child(dev, print_device_path, xname); + if (rv == 1) { + xo_emit("{P: }"); + print_device_path_entry(dev); + } + return (rv); +} + +static void +print_path(struct devinfo_dev *root, char *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) + xo_emit("\n"); + + while (open_tag_count > 0) { + xo_close_container_d(); + open_tag_count--; + } +} + +static void __dead2 +usage(void) +{ + xo_error( + "usage: devinfo [-rv]\n", + " devinfo -u [-v]\n", + " devinfo -p dev [-v]\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct devinfo_dev *root; + int c, rv; + 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) { + case 'p': + path = optarg; + break; + case 'r': + rflag = true; + break; + case 'u': + uflag = true; + break; + case 'v': + vflag = true; + break; + default: + usage(); + } + } + + if (path && (rflag || uflag)) + usage(); + + if ((rv = devinfo_init()) != 0) { + errno = rv; + xo_err(1, "devinfo_init"); + } + + if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL) + 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); +} |
