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:%s}={d:%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("{d:%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(" <{d:%s}>", dev->dd_desc); 224 xo_emit("{e:description/%s}", dev->dd_desc); 225 } 226 if (*dev->dd_pnpinfo) { 227 xo_open_container("pnpinfo"); 228 xo_emit("{D: pnpinfo}"); 229 230 if ((strcmp(dev->dd_pnpinfo, "unknown") == 0)) 231 xo_emit("{D: unknown}"); 232 else 233 print_kvlist(dev->dd_pnpinfo); 234 235 xo_close_container("pnpinfo"); 236 } 237 if (*dev->dd_location) { 238 xo_open_container("location"); 239 xo_emit("{D: at}"); 240 print_kvlist(dev->dd_location); 241 xo_close_container("location"); 242 } 243 244 // If verbose, then always print state for json/xml. 245 if (!(dev->dd_flags & DF_ENABLED)) 246 xo_emit("{e:state/disabled}"); 247 else if (dev->dd_flags & DF_SUSPENDED) 248 xo_emit("{e:state/suspended}"); 249 else 250 xo_emit("{e:state/enabled}"); 251 } 252 253 if (!(dev->dd_flags & DF_ENABLED)) 254 xo_emit("{D: (disabled)}"); 255 else if (dev->dd_flags & DF_SUSPENDED) 256 xo_emit("{D: (suspended)}"); 257 } 258 259 /* 260 * Print information about a device. 261 */ 262 static int 263 print_device(struct devinfo_dev *dev, void *arg) 264 { 265 struct indent_arg ia; 266 int indent, ret; 267 const char* devname = dev->dd_name[0] ? dev->dd_name : "unknown"; 268 bool printit = vflag || (dev->dd_name[0] != 0 && 269 dev->dd_state >= DS_ATTACHED); 270 271 if (printit) { 272 indent = (int)(intptr_t)arg; 273 print_indent(indent); 274 275 xo_open_container(devname); 276 xo_emit("{d:%s}", devname); 277 278 print_device_props(dev); 279 xo_emit("\n"); 280 if (rflag) { 281 ia.indent = indent + 4; 282 ia.arg = dev; 283 devinfo_foreach_rman(print_device_rman_resources, 284 (void *)&ia); 285 } 286 } 287 288 ret = (devinfo_foreach_device_child(dev, print_device, 289 (void *)((char *)arg + 2))); 290 291 if (printit) { 292 xo_close_container(devname); 293 } 294 return(ret); 295 } 296 297 /* 298 * Print information about a resource under a resource manager. 299 */ 300 int 301 print_rman_resource(struct devinfo_res *res, void *arg __unused) 302 { 303 struct devinfo_dev *dev; 304 struct devinfo_rman *rman; 305 rman_res_t end; 306 char *res_str, *entry = NULL; 307 bool hexmode; 308 309 dev = devinfo_handle_to_device(res->dr_device); 310 rman = devinfo_handle_to_rman(res->dr_rman); 311 hexmode = (rman->dm_size > 1000) || (rman->dm_size == 0); 312 end = res->dr_start + res->dr_size - 1; 313 314 if (hexmode) { 315 if (res->dr_size > 1) 316 asprintf(&res_str, "0x%jx-0x%jx", res->dr_start, end); 317 else 318 asprintf(&res_str, "0x%jx", res->dr_start); 319 } else { 320 if (res->dr_size > 1) 321 asprintf(&res_str, "%ju-%ju", res->dr_start, end); 322 else 323 asprintf(&res_str, "%ju", res->dr_start); 324 } 325 326 xo_emit("{P: }"); 327 328 if (last_res == NULL) { 329 // First resource 330 xo_open_list(res_str); 331 } else if (strcmp(res_str, last_res) != 0) { 332 // We can't repeat json keys. So we keep an 333 // open list from the last iteration and only 334 // create a new list when see a new resource. 335 xo_close_list(last_res); 336 xo_open_list(res_str); 337 } 338 339 dev = devinfo_handle_to_device(res->dr_device); 340 if (dev != NULL) { 341 if (dev->dd_name[0] != 0) { 342 printf(" (%s)", dev->dd_name); 343 asprintf(&entry, "{el:%s}{D:%s} {D:(%s)}\n", 344 res_str, res_str, dev->dd_name); 345 xo_emit(entry, dev->dd_name); 346 } else { 347 printf(" (unknown)"); 348 if (vflag && *dev->dd_pnpinfo) 349 printf(" pnpinfo %s", dev->dd_pnpinfo); 350 if (vflag && *dev->dd_location) 351 printf(" at %s", dev->dd_location); 352 } 353 } else { 354 asprintf(&entry, "{el:%s}{D:%s} {D:----}\n", res_str, res_str); 355 xo_emit(entry, "----"); 356 } 357 free(entry); 358 last_res = res_str; 359 return(0); 360 } 361 362 /* 363 * Print information about a resource manager. 364 */ 365 int 366 print_rman(struct devinfo_rman *rman, void *arg __unused) 367 { 368 char* safe_desc = xml_safe_string(rman->dm_desc); 369 370 xo_emit("{d:%s}:\n", rman->dm_desc); 371 xo_open_container(safe_desc); 372 373 devinfo_foreach_rman_resource(rman, print_rman_resource, 0); 374 375 xo_close_list(last_res); 376 xo_close_container(safe_desc); 377 free(safe_desc); 378 return(0); 379 } 380 381 static void 382 print_device_path_entry(struct devinfo_dev *dev) 383 { 384 const char *devname = dev->dd_name[0] ? dev->dd_name : "unknown"; 385 386 xo_open_container(devname); 387 open_tag_count++; 388 xo_emit("{d:%s }", devname); 389 print_device_props(dev); 390 if (vflag) 391 xo_emit("\n"); 392 } 393 394 /* 395 * Recurse until we find the right dev. On the way up we print path. 396 */ 397 static int 398 print_device_path(struct devinfo_dev *dev, void *xname) 399 { 400 const char *name = xname; 401 int rv; 402 403 if (strcmp(dev->dd_name, name) == 0) { 404 print_device_path_entry(dev); 405 return (1); 406 } 407 408 rv = devinfo_foreach_device_child(dev, print_device_path, xname); 409 if (rv == 1) { 410 xo_emit("{P: }"); 411 print_device_path_entry(dev); 412 } 413 return (rv); 414 } 415 416 static void 417 print_path(struct devinfo_dev *root, char *path) 418 { 419 open_tag_count = 0; 420 if (devinfo_foreach_device_child(root, print_device_path, 421 (void *)path) == 0) 422 xo_errx(1, "%s: Not found", path); 423 if (!vflag) 424 xo_emit("\n"); 425 426 while (open_tag_count > 0) { 427 xo_close_container_d(); 428 open_tag_count--; 429 } 430 } 431 432 static void __dead2 433 usage(void) 434 { 435 xo_error( 436 "usage: devinfo [-rv]\n", 437 " devinfo -u [-v]\n", 438 " devinfo -p dev [-v]\n"); 439 exit(1); 440 } 441 442 int 443 main(int argc, char *argv[]) 444 { 445 struct devinfo_dev *root; 446 int c, rv; 447 bool uflag; 448 char *path = NULL; 449 450 argc = xo_parse_args(argc, argv); 451 if (argc < 0) { 452 exit(1); 453 } 454 455 uflag = false; 456 while ((c = getopt(argc, argv, "p:ruv")) != -1) { 457 switch(c) { 458 case 'p': 459 path = optarg; 460 break; 461 case 'r': 462 rflag = true; 463 break; 464 case 'u': 465 uflag = true; 466 break; 467 case 'v': 468 vflag = true; 469 break; 470 default: 471 usage(); 472 } 473 } 474 475 if (path && (rflag || uflag)) 476 usage(); 477 478 if ((rv = devinfo_init()) != 0) { 479 errno = rv; 480 xo_err(1, "devinfo_init"); 481 } 482 483 if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL) 484 xo_errx(1, "can't find root device"); 485 486 if (path) { 487 xo_set_flags(NULL, XOF_DTRT); 488 xo_open_container("device-path"); 489 print_path(root, path); 490 xo_close_container("device-path"); 491 } else if (uflag) { 492 /* print resource usage? */ 493 xo_set_flags(NULL, XOF_DTRT); 494 xo_open_container("device-resources"); 495 devinfo_foreach_rman(print_rman, NULL); 496 xo_close_container("device-resources"); 497 } else { 498 /* print device hierarchy */ 499 xo_open_container("device-information"); 500 devinfo_foreach_device_child(root, print_device, (void *)0); 501 xo_close_container("device-information"); 502 } 503 504 if (xo_finish() < 0) { 505 exit(1); 506 } 507 return(0); 508 } 509