1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2000, 2001 Michael Smith 5 * Copyright (c) 2000 BSDi 6 * All rights reserved. 7 * Copyright (c) 2024 KT Ullavik 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * Print information about system device configuration. 33 */ 34 35 #include <sys/param.h> 36 37 #include <err.h> 38 #include <errno.h> 39 #include <stdbool.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 #include <libxo/xo.h> 46 #include "devinfo.h" 47 48 static bool rflag; 49 static bool vflag; 50 static int open_tag_count; 51 static char *last_res; 52 53 static void print_indent(int); 54 static void print_kvlist(char *); 55 static char* xml_safe_string(char *); 56 static void print_resource(struct devinfo_res *); 57 static int print_device_matching_resource(struct devinfo_res *, void *); 58 static int print_device_rman_resources(struct devinfo_rman *, void *); 59 static void print_device_props(struct devinfo_dev *); 60 static int print_device(struct devinfo_dev *, void *); 61 static int print_rman_resource(struct devinfo_res *, void *); 62 static int print_rman(struct devinfo_rman *, void *); 63 static int print_device_path(struct devinfo_dev *, void *); 64 static void print_path(struct devinfo_dev *, char *); 65 static void usage(void); 66 67 struct indent_arg 68 { 69 int indent; 70 void *arg; 71 }; 72 73 74 static void 75 print_indent(int n) 76 { 77 static char buffer[1024]; 78 79 if (n < 1) 80 return; 81 n = MIN((size_t)n, sizeof(buffer) - 1); 82 memset(buffer, ' ', n); 83 buffer[n] = '\0'; 84 xo_emit("{Pa:%s}", buffer); 85 } 86 87 /* 88 * Takes a list of key-value pairs in the form 89 * "key1=val1 key2=val2 ..." and prints them according 90 * to xo formatting. 91 */ 92 static void 93 print_kvlist(char *s) 94 { 95 char *kv; 96 char *copy; 97 98 if ((copy = strdup(s)) == NULL) 99 xo_err(1, "No memory!"); 100 101 while ((kv = strsep(©, " ")) != NULL) { 102 char* k = strsep(&kv, "="); 103 xo_emit("{ea:%s/%s} {d:key/%s}={d:value/%s}", k, kv, k, kv); 104 } 105 free(copy); 106 } 107 108 static char 109 *xml_safe_string(char *desc) 110 { 111 int i; 112 char *s; 113 114 if ((s = strdup(desc)) == NULL) { 115 xo_err(1, "No memory!"); 116 } 117 118 for (i=0; s[i] != '\0'; i++) { 119 if (s[i] == ' ' || s[i] == '/') { 120 s[i] = '-'; 121 } 122 } 123 return s; 124 } 125 126 /* 127 * Print a resource. 128 */ 129 void 130 print_resource(struct devinfo_res *res) 131 { 132 struct devinfo_rman *rman; 133 bool hexmode; 134 rman_res_t end; 135 char *safe_desc; 136 137 rman = devinfo_handle_to_rman(res->dr_rman); 138 hexmode = (rman->dm_size > 1000) || (rman->dm_size == 0); 139 end = res->dr_start + res->dr_size - 1; 140 141 safe_desc = xml_safe_string(rman->dm_desc); 142 xo_open_instance(safe_desc); 143 144 if (hexmode) { 145 xo_emit("{:start/0x%jx}", res->dr_start); 146 if (res->dr_size > 1) 147 xo_emit("{D:-}{d:end/0x%jx}", end); 148 xo_emit("{e:end/0x%jx}", end); 149 } else { 150 xo_emit("{:start/%ju}", res->dr_start); 151 if (res->dr_size > 1) 152 xo_emit("{D:-}{d:end/%ju}", end); 153 xo_emit("{e:end/%ju}", end); 154 } 155 xo_close_instance(safe_desc); 156 free(safe_desc); 157 } 158 159 /* 160 * Print resource information if this resource matches the 161 * given device. 162 * 163 * If the given indent is 0, return an indicator that a matching 164 * resource exists. 165 */ 166 int 167 print_device_matching_resource(struct devinfo_res *res, void *arg) 168 { 169 struct indent_arg *ia = (struct indent_arg *)arg; 170 struct devinfo_dev *dev = (struct devinfo_dev *)ia->arg; 171 172 if (devinfo_handle_to_device(res->dr_device) == dev) { 173 /* in 'detect' mode, found a match */ 174 if (ia->indent == 0) 175 return(1); 176 print_indent(ia->indent); 177 print_resource(res); 178 xo_emit("\n"); 179 } 180 return(0); 181 } 182 183 /* 184 * Print resource information for this device and resource manager. 185 */ 186 int 187 print_device_rman_resources(struct devinfo_rman *rman, void *arg) 188 { 189 struct indent_arg *ia = (struct indent_arg *)arg; 190 int indent; 191 char *safe_desc; 192 193 indent = ia->indent; 194 195 /* check whether there are any resources matching this device */ 196 ia->indent = 0; 197 if (devinfo_foreach_rman_resource(rman, 198 print_device_matching_resource, ia) != 0) { 199 200 /* there are, print header */ 201 safe_desc = xml_safe_string(rman->dm_desc); 202 print_indent(indent); 203 xo_emit("<{:description/%s}>\n", rman->dm_desc); 204 xo_open_list(safe_desc); 205 206 /* print resources */ 207 ia->indent = indent + 4; 208 devinfo_foreach_rman_resource(rman, 209 print_device_matching_resource, ia); 210 211 xo_close_list(safe_desc); 212 free(safe_desc); 213 } 214 ia->indent = indent; 215 return(0); 216 } 217 218 static void 219 print_device_props(struct devinfo_dev *dev) 220 { 221 if (vflag) { 222 if (*dev->dd_desc) { 223 xo_emit("<{:description/%s}>", dev->dd_desc); 224 } 225 if (*dev->dd_pnpinfo) { 226 xo_open_container("pnpinfo"); 227 xo_emit("{D: pnpinfo}"); 228 229 if ((strcmp(dev->dd_pnpinfo, "unknown") == 0)) 230 xo_emit("{D: unknown}"); 231 else 232 print_kvlist(dev->dd_pnpinfo); 233 234 xo_close_container("pnpinfo"); 235 } 236 if (*dev->dd_location) { 237 xo_open_container("location"); 238 xo_emit("{D: at}"); 239 print_kvlist(dev->dd_location); 240 xo_close_container("location"); 241 } 242 243 // If verbose, then always print state for json/xml. 244 if (!(dev->dd_flags & DF_ENABLED)) 245 xo_emit("{e:state/disabled}"); 246 else if (dev->dd_flags & DF_SUSPENDED) 247 xo_emit("{e:state/suspended}"); 248 else 249 xo_emit("{e:state/enabled}"); 250 } 251 252 if (!(dev->dd_flags & DF_ENABLED)) 253 xo_emit("{D: (disabled)}"); 254 else if (dev->dd_flags & DF_SUSPENDED) 255 xo_emit("{D: (suspended)}"); 256 } 257 258 /* 259 * Print information about a device. 260 */ 261 static int 262 print_device(struct devinfo_dev *dev, void *arg) 263 { 264 struct indent_arg ia; 265 int indent, ret; 266 const char* devname = dev->dd_name[0] ? dev->dd_name : "unknown"; 267 bool printit = vflag || (dev->dd_name[0] != 0 && 268 dev->dd_state >= DS_ATTACHED); 269 270 if (printit) { 271 indent = (int)(intptr_t)arg; 272 print_indent(indent); 273 274 xo_open_container(devname); 275 xo_emit("{d:devicename/%s}", devname); 276 277 print_device_props(dev); 278 xo_emit("\n"); 279 if (rflag) { 280 ia.indent = indent + 4; 281 ia.arg = dev; 282 devinfo_foreach_rman(print_device_rman_resources, 283 (void *)&ia); 284 } 285 } 286 287 ret = (devinfo_foreach_device_child(dev, print_device, 288 (void *)((char *)arg + 2))); 289 290 if (printit) { 291 xo_close_container(devname); 292 } 293 return(ret); 294 } 295 296 /* 297 * Print information about a resource under a resource manager. 298 */ 299 int 300 print_rman_resource(struct devinfo_res *res, void *arg __unused) 301 { 302 struct devinfo_dev *dev; 303 struct devinfo_rman *rman; 304 rman_res_t end; 305 char *res_str, *entry = NULL; 306 bool hexmode; 307 308 dev = devinfo_handle_to_device(res->dr_device); 309 rman = devinfo_handle_to_rman(res->dr_rman); 310 hexmode = (rman->dm_size > 1000) || (rman->dm_size == 0); 311 end = res->dr_start + res->dr_size - 1; 312 313 if (hexmode) { 314 if (res->dr_size > 1) 315 asprintf(&res_str, "0x%jx-0x%jx", res->dr_start, end); 316 else 317 asprintf(&res_str, "0x%jx", res->dr_start); 318 } else { 319 if (res->dr_size > 1) 320 asprintf(&res_str, "%ju-%ju", res->dr_start, end); 321 else 322 asprintf(&res_str, "%ju", res->dr_start); 323 } 324 325 xo_emit("{P: }"); 326 327 if (last_res == NULL) { 328 // First resource 329 xo_open_list(res_str); 330 } else if (strcmp(res_str, last_res) != 0) { 331 // We can't repeat json keys. So we keep an 332 // open list from the last iteration and only 333 // create a new list when see a new resource. 334 xo_close_list(last_res); 335 xo_open_list(res_str); 336 } 337 338 dev = devinfo_handle_to_device(res->dr_device); 339 if (dev != NULL) { 340 if (dev->dd_name[0] != 0) { 341 printf(" (%s)", dev->dd_name); 342 asprintf(&entry, "{el:%s}{D:%s} {D:(%s)}\n", 343 res_str, res_str, dev->dd_name); 344 xo_emit(entry, dev->dd_name); 345 } else { 346 printf(" (unknown)"); 347 if (vflag && *dev->dd_pnpinfo) 348 printf(" pnpinfo %s", dev->dd_pnpinfo); 349 if (vflag && *dev->dd_location) 350 printf(" at %s", dev->dd_location); 351 } 352 } else { 353 asprintf(&entry, "{el:%s}{D:%s} {D:----}\n", res_str, res_str); 354 xo_emit(entry, "----"); 355 } 356 free(entry); 357 last_res = res_str; 358 return(0); 359 } 360 361 /* 362 * Print information about a resource manager. 363 */ 364 int 365 print_rman(struct devinfo_rman *rman, void *arg __unused) 366 { 367 char* safe_desc = xml_safe_string(rman->dm_desc); 368 369 xo_emit("<{:description/%s}\n>", rman->dm_desc); 370 xo_open_container(safe_desc); 371 372 devinfo_foreach_rman_resource(rman, print_rman_resource, 0); 373 374 xo_close_list(last_res); 375 xo_close_container(safe_desc); 376 free(safe_desc); 377 return(0); 378 } 379 380 static void 381 print_device_path_entry(struct devinfo_dev *dev) 382 { 383 const char *devname = dev->dd_name[0] ? dev->dd_name : "unknown"; 384 385 xo_open_container(devname); 386 open_tag_count++; 387 xo_emit("{:devicename/%s} ", devname); 388 print_device_props(dev); 389 if (vflag) 390 xo_emit("\n"); 391 } 392 393 /* 394 * Recurse until we find the right dev. On the way up we print path. 395 */ 396 static int 397 print_device_path(struct devinfo_dev *dev, void *xname) 398 { 399 const char *name = xname; 400 int rv; 401 402 if (strcmp(dev->dd_name, name) == 0) { 403 print_device_path_entry(dev); 404 return (1); 405 } 406 407 rv = devinfo_foreach_device_child(dev, print_device_path, xname); 408 if (rv == 1) { 409 xo_emit("{P: }"); 410 print_device_path_entry(dev); 411 } 412 return (rv); 413 } 414 415 static void 416 print_path(struct devinfo_dev *root, char *path) 417 { 418 open_tag_count = 0; 419 if (devinfo_foreach_device_child(root, print_device_path, 420 (void *)path) == 0) 421 xo_errx(1, "%s: Not found", path); 422 if (!vflag) 423 xo_emit("\n"); 424 425 while (open_tag_count > 0) { 426 xo_close_container_d(); 427 open_tag_count--; 428 } 429 } 430 431 static void __dead2 432 usage(void) 433 { 434 xo_error( 435 "usage: devinfo [-rv]\n", 436 " devinfo -u [-v]\n", 437 " devinfo -p dev [-v]\n"); 438 exit(1); 439 } 440 441 int 442 main(int argc, char *argv[]) 443 { 444 struct devinfo_dev *root; 445 int c, rv; 446 bool uflag; 447 char *path = NULL; 448 449 argc = xo_parse_args(argc, argv); 450 if (argc < 0) { 451 exit(1); 452 } 453 454 uflag = false; 455 while ((c = getopt(argc, argv, "p:ruv")) != -1) { 456 switch(c) { 457 case 'p': 458 path = optarg; 459 break; 460 case 'r': 461 rflag = true; 462 break; 463 case 'u': 464 uflag = true; 465 break; 466 case 'v': 467 vflag = true; 468 break; 469 default: 470 usage(); 471 } 472 } 473 474 if (path && (rflag || uflag)) 475 usage(); 476 477 if ((rv = devinfo_init()) != 0) { 478 errno = rv; 479 xo_err(1, "devinfo_init"); 480 } 481 482 if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL) 483 xo_errx(1, "can't find root device"); 484 485 if (path) { 486 xo_set_flags(NULL, XOF_DTRT); 487 xo_open_container("device-path"); 488 print_path(root, path); 489 xo_close_container("device-path"); 490 } else if (uflag) { 491 /* print resource usage? */ 492 xo_set_flags(NULL, XOF_DTRT); 493 xo_open_container("device-resources"); 494 devinfo_foreach_rman(print_rman, NULL); 495 xo_close_container("device-resources"); 496 } else { 497 /* print device hierarchy */ 498 xo_open_container("device-information"); 499 devinfo_foreach_device_child(root, print_device, (void *)0); 500 xo_close_container("device-information"); 501 } 502 503 if (xo_finish() < 0) { 504 exit(1); 505 } 506 return(0); 507 } 508