1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2021 Oxide Computer Company 14 */ 15 16 /* 17 * A tool to interface with the pci.ids database driven by libpcidb. 18 */ 19 20 #include <stdio.h> 21 #include <stdarg.h> 22 #include <pcidb.h> 23 #include <err.h> 24 #include <libgen.h> 25 #include <string.h> 26 #include <strings.h> 27 #include <stdlib.h> 28 #include <ofmt.h> 29 #include <errno.h> 30 #include <sys/debug.h> 31 #include <priv.h> 32 33 #define EXIT_USAGE 2 34 35 static char *pcidb_progname; 36 37 typedef enum { 38 PCIDB_MODE_UNKNOWN, 39 PCIDB_MODE_LIST, 40 PCIDB_MODE_SEARCH, 41 PCIDB_MODE_LOOKUP 42 } pcidb_mode_t; 43 44 typedef enum { 45 PCIDB_TABLE_NONE, 46 PCIDB_TABLE_VENDOR, 47 PCIDB_TABLE_DEVICE, 48 PCIDB_TABLE_SUBSYSTEM, 49 PCIDB_TABLE_CLASS, 50 PCIDB_TABLE_SUBCLASS, 51 PCIDB_TABLE_PROGIF 52 } pcidb_table_t; 53 54 typedef enum { 55 PCIDB_OFMT_VID, 56 PCIDB_OFMT_VENSTR, 57 PCIDB_OFMT_DID, 58 PCIDB_OFMT_DEVSTR, 59 PCIDB_OFMT_SVID, 60 PCIDB_OFMT_SDID, 61 PCIDB_OFMT_SUBVENSTR, 62 PCIDB_OFMT_SUBSYSSTR, 63 PCIDB_OFMT_BCC, 64 PCIDB_OFMT_CLASSSTR, 65 PCIDB_OFMT_SCC, 66 PCIDB_OFMT_SUBCLASSSTR, 67 PCIDB_OFMT_PI, 68 PCIDB_OFMT_PROGIFSTR 69 } pcidb_ofmt_t; 70 71 typedef struct pcidb_filter { 72 uint32_t pft_vend; 73 uint32_t pft_dev; 74 uint32_t pft_subven; 75 uint32_t pft_subdev; 76 uint32_t pft_class; 77 uint32_t pft_subclass; 78 uint32_t pft_progif; 79 } pcidb_filter_t; 80 81 #define PCIDB_NOFILTER UINT32_MAX 82 83 typedef struct pcidb_walk { 84 pcidb_hdl_t *pw_hdl; 85 ofmt_handle_t pw_ofmt; 86 pcidb_vendor_t *pw_vendor; 87 pcidb_device_t *pw_device; 88 pcidb_subvd_t *pw_subvd; 89 pcidb_class_t *pw_class; 90 pcidb_subclass_t *pw_subclass; 91 pcidb_progif_t *pw_progif; 92 boolean_t pw_strcase; 93 uint_t pw_nfilters; 94 pcidb_filter_t *pw_filters; 95 } pcidb_walk_t; 96 97 static boolean_t 98 pcidb_write_vendor(ofmt_arg_t *ofarg, char *buf, uint_t buflen) 99 { 100 pcidb_walk_t *walk = ofarg->ofmt_cbarg; 101 102 VERIFY(walk->pw_vendor != NULL); 103 switch (ofarg->ofmt_id) { 104 case PCIDB_OFMT_VID: 105 (void) snprintf(buf, buflen, "%x", 106 pcidb_vendor_id(walk->pw_vendor)); 107 break; 108 case PCIDB_OFMT_VENSTR: 109 (void) strlcpy(buf, pcidb_vendor_name(walk->pw_vendor), buflen); 110 break; 111 default: 112 abort(); 113 } 114 return (B_TRUE); 115 } 116 117 static boolean_t 118 pcidb_write_device(ofmt_arg_t *ofarg, char *buf, uint_t buflen) 119 { 120 pcidb_walk_t *walk = ofarg->ofmt_cbarg; 121 122 VERIFY(walk->pw_device != NULL); 123 switch (ofarg->ofmt_id) { 124 case PCIDB_OFMT_DID: 125 (void) snprintf(buf, buflen, "%x", 126 pcidb_device_id(walk->pw_device)); 127 break; 128 case PCIDB_OFMT_DEVSTR: 129 (void) strlcpy(buf, pcidb_device_name(walk->pw_device), buflen); 130 break; 131 default: 132 abort(); 133 } 134 return (B_TRUE); 135 } 136 137 static boolean_t 138 pcidb_write_subsystem(ofmt_arg_t *ofarg, char *buf, uint_t buflen) 139 { 140 pcidb_walk_t *walk = ofarg->ofmt_cbarg; 141 pcidb_vendor_t *vendor; 142 143 VERIFY(walk->pw_subvd != NULL); 144 switch (ofarg->ofmt_id) { 145 case PCIDB_OFMT_SVID: 146 (void) snprintf(buf, buflen, "%x", 147 pcidb_subvd_svid(walk->pw_subvd)); 148 break; 149 case PCIDB_OFMT_SDID: 150 (void) snprintf(buf, buflen, "%x", 151 pcidb_subvd_sdid(walk->pw_subvd)); 152 break; 153 case PCIDB_OFMT_SUBSYSSTR: 154 (void) strlcpy(buf, pcidb_subvd_name(walk->pw_subvd), buflen); 155 break; 156 case PCIDB_OFMT_SUBVENSTR: 157 vendor = pcidb_lookup_vendor(walk->pw_hdl, 158 pcidb_subvd_svid(walk->pw_subvd)); 159 if (vendor == NULL) { 160 return (B_FALSE); 161 } 162 (void) strlcpy(buf, pcidb_vendor_name(vendor), buflen); 163 break; 164 default: 165 abort(); 166 } 167 return (B_TRUE); 168 } 169 170 static boolean_t 171 pcidb_write_class(ofmt_arg_t *ofarg, char *buf, uint_t buflen) 172 { 173 pcidb_walk_t *walk = ofarg->ofmt_cbarg; 174 175 VERIFY(walk->pw_class != NULL); 176 switch (ofarg->ofmt_id) { 177 case PCIDB_OFMT_BCC: 178 (void) snprintf(buf, buflen, "%x", 179 pcidb_class_code(walk->pw_class)); 180 break; 181 case PCIDB_OFMT_CLASSSTR: 182 (void) strlcpy(buf, pcidb_class_name(walk->pw_class), buflen); 183 break; 184 default: 185 abort(); 186 } 187 return (B_TRUE); 188 } 189 190 static boolean_t 191 pcidb_write_subclass(ofmt_arg_t *ofarg, char *buf, uint_t buflen) 192 { 193 pcidb_walk_t *walk = ofarg->ofmt_cbarg; 194 195 VERIFY(walk->pw_subclass != NULL); 196 switch (ofarg->ofmt_id) { 197 case PCIDB_OFMT_SCC: 198 (void) snprintf(buf, buflen, "%x", 199 pcidb_subclass_code(walk->pw_subclass)); 200 break; 201 case PCIDB_OFMT_SUBCLASSSTR: 202 (void) strlcpy(buf, pcidb_subclass_name(walk->pw_subclass), 203 buflen); 204 break; 205 default: 206 abort(); 207 } 208 return (B_TRUE); 209 } 210 211 static boolean_t 212 pcidb_write_progif(ofmt_arg_t *ofarg, char *buf, uint_t buflen) 213 { 214 pcidb_walk_t *walk = ofarg->ofmt_cbarg; 215 216 VERIFY(walk->pw_progif != NULL); 217 switch (ofarg->ofmt_id) { 218 case PCIDB_OFMT_PI: 219 (void) snprintf(buf, buflen, "%x", 220 pcidb_progif_code(walk->pw_progif)); 221 break; 222 case PCIDB_OFMT_PROGIFSTR: 223 (void) strlcpy(buf, pcidb_progif_name(walk->pw_progif), 224 buflen); 225 break; 226 default: 227 abort(); 228 } 229 return (B_TRUE); 230 } 231 232 static const char *pcidb_vendor_fields = "vid,vendor"; 233 static const ofmt_field_t pcidb_vendor_ofmt[] = { 234 { "VID", 8, PCIDB_OFMT_VID, pcidb_write_vendor }, 235 { "VENDOR", 30, PCIDB_OFMT_VENSTR, pcidb_write_vendor }, 236 { NULL, 0, 0, NULL } 237 }; 238 239 static const char *pcidb_device_fields = "vid,did,vendor,device"; 240 static const ofmt_field_t pcidb_device_ofmt[] = { 241 { "VID", 8, PCIDB_OFMT_VID, pcidb_write_vendor }, 242 { "VENDOR", 30, PCIDB_OFMT_VENSTR, pcidb_write_vendor }, 243 { "DID", 8, PCIDB_OFMT_DID, pcidb_write_device }, 244 { "DEVICE", 30, PCIDB_OFMT_DEVSTR, pcidb_write_device }, 245 { NULL, 0, 0, NULL } 246 }; 247 248 static const char *pcidb_subsystem_fields = "vid,did,svid,sdid,subsystem"; 249 static const ofmt_field_t pcidb_subsystem_ofmt[] = { 250 { "VID", 8, PCIDB_OFMT_VID, pcidb_write_vendor }, 251 { "VENDOR", 30, PCIDB_OFMT_VENSTR, pcidb_write_vendor }, 252 { "DID", 8, PCIDB_OFMT_DID, pcidb_write_device }, 253 { "DEVICE", 30, PCIDB_OFMT_DEVSTR, pcidb_write_device }, 254 { "SVID", 8, PCIDB_OFMT_SVID, pcidb_write_subsystem }, 255 { "SDID", 8, PCIDB_OFMT_SDID, pcidb_write_subsystem }, 256 { "SUBSYSTEM", 30, PCIDB_OFMT_SUBSYSSTR, pcidb_write_subsystem }, 257 { "SUBVENDOR", 30, PCIDB_OFMT_SUBVENSTR, pcidb_write_subsystem }, 258 { NULL, 0, 0, NULL } 259 }; 260 261 static const char *pcidb_class_fields = "bcc,class"; 262 static const ofmt_field_t pcidb_class_ofmt[] = { 263 { "BCC", 6, PCIDB_OFMT_BCC, pcidb_write_class }, 264 { "CLASS", 30, PCIDB_OFMT_CLASSSTR, pcidb_write_class }, 265 { NULL, 0, 0, NULL } 266 }; 267 268 static const char *pcidb_subclass_fields = "bcc,scc,class,subclass"; 269 static const ofmt_field_t pcidb_subclass_ofmt[] = { 270 { "BCC", 6, PCIDB_OFMT_BCC, pcidb_write_class }, 271 { "CLASS", 30, PCIDB_OFMT_CLASSSTR, pcidb_write_class }, 272 { "SCC", 6, PCIDB_OFMT_SCC, pcidb_write_subclass }, 273 { "SUBCLASS", 30, PCIDB_OFMT_SUBCLASSSTR, pcidb_write_subclass }, 274 { NULL, 0, 0, NULL } 275 }; 276 277 static const char *pcidb_progif_fields = "bcc,scc,pi,subclass,interface"; 278 static const ofmt_field_t pcidb_progif_ofmt[] = { 279 { "BCC", 6, PCIDB_OFMT_BCC, pcidb_write_class }, 280 { "CLASS", 30, PCIDB_OFMT_CLASSSTR, pcidb_write_class }, 281 { "SCC", 6, PCIDB_OFMT_SCC, pcidb_write_subclass }, 282 { "SUBCLASS", 30, PCIDB_OFMT_SUBCLASSSTR, pcidb_write_subclass }, 283 { "PI", 6, PCIDB_OFMT_PI, pcidb_write_progif }, 284 { "INTERFACE", 30, PCIDB_OFMT_PROGIFSTR, pcidb_write_progif }, 285 { NULL, 0, 0, NULL } 286 }; 287 288 static void 289 pcidb_ofmt_errx(const char *fmt, ...) 290 { 291 va_list ap; 292 293 va_start(ap, fmt); 294 verrx(EXIT_FAILURE, fmt, ap); 295 } 296 297 static boolean_t 298 pcidb_filter_match(pcidb_walk_t *walk) 299 { 300 if (walk->pw_nfilters == 0) { 301 return (B_TRUE); 302 } 303 304 for (uint_t i = 0; i < walk->pw_nfilters; i++) { 305 const pcidb_filter_t *filt = &walk->pw_filters[i]; 306 if (filt->pft_vend != PCIDB_NOFILTER && 307 (walk->pw_vendor == NULL || 308 filt->pft_vend != pcidb_vendor_id(walk->pw_vendor))) { 309 continue; 310 } 311 312 if (filt->pft_dev != PCIDB_NOFILTER && 313 (walk->pw_device == NULL || 314 filt->pft_dev != pcidb_device_id(walk->pw_device))) { 315 continue; 316 } 317 318 if (filt->pft_subven != PCIDB_NOFILTER && 319 (walk->pw_subvd == NULL || 320 filt->pft_subven != pcidb_subvd_svid(walk->pw_subvd))) { 321 continue; 322 } 323 324 if (filt->pft_subdev != PCIDB_NOFILTER && 325 (walk->pw_subvd == NULL || 326 filt->pft_subdev != pcidb_subvd_sdid(walk->pw_subvd))) { 327 continue; 328 } 329 330 if (filt->pft_class != PCIDB_NOFILTER && 331 (walk->pw_class == NULL || 332 filt->pft_class != pcidb_class_code(walk->pw_class))) { 333 continue; 334 } 335 336 if (filt->pft_subclass != PCIDB_NOFILTER && 337 (walk->pw_subclass == NULL || 338 filt->pft_subclass != 339 pcidb_subclass_code(walk->pw_subclass))) { 340 continue; 341 } 342 343 if (filt->pft_progif != PCIDB_NOFILTER && 344 (walk->pw_progif == NULL || 345 filt->pft_progif != pcidb_progif_code(walk->pw_progif))) { 346 continue; 347 } 348 349 return (B_TRUE); 350 } 351 352 return (B_FALSE); 353 } 354 355 static void 356 pcidb_walk_vendors(pcidb_walk_t *walk) 357 { 358 pcidb_hdl_t *hdl = walk->pw_hdl; 359 360 for (pcidb_vendor_t *vend = pcidb_vendor_iter(hdl); vend != NULL; 361 vend = pcidb_vendor_iter_next(vend)) { 362 walk->pw_vendor = vend; 363 if (!pcidb_filter_match(walk)) 364 continue; 365 ofmt_print(walk->pw_ofmt, walk); 366 } 367 } 368 369 static void 370 pcidb_walk_devices(pcidb_walk_t *walk) 371 { 372 pcidb_hdl_t *hdl = walk->pw_hdl; 373 374 for (pcidb_vendor_t *vend = pcidb_vendor_iter(hdl); vend != NULL; 375 vend = pcidb_vendor_iter_next(vend)) { 376 walk->pw_vendor = vend; 377 for (pcidb_device_t *dev = pcidb_device_iter(vend); dev != NULL; 378 dev = pcidb_device_iter_next(dev)) { 379 walk->pw_device = dev; 380 if (!pcidb_filter_match(walk)) 381 continue; 382 ofmt_print(walk->pw_ofmt, walk); 383 } 384 } 385 } 386 387 static void 388 pcidb_walk_subsystems(pcidb_walk_t *walk) 389 { 390 pcidb_hdl_t *hdl = walk->pw_hdl; 391 392 for (pcidb_vendor_t *vend = pcidb_vendor_iter(hdl); vend != NULL; 393 vend = pcidb_vendor_iter_next(vend)) { 394 walk->pw_vendor = vend; 395 for (pcidb_device_t *dev = pcidb_device_iter(vend); dev != NULL; 396 dev = pcidb_device_iter_next(dev)) { 397 walk->pw_device = dev; 398 for (pcidb_subvd_t *sub = pcidb_subvd_iter(dev); 399 sub != NULL; sub = pcidb_subvd_iter_next(sub)) { 400 walk->pw_subvd = sub; 401 if (!pcidb_filter_match(walk)) 402 continue; 403 ofmt_print(walk->pw_ofmt, walk); 404 } 405 } 406 407 } 408 } 409 410 static void 411 pcidb_walk_classes(pcidb_walk_t *walk) 412 { 413 for (pcidb_class_t *class = pcidb_class_iter(walk->pw_hdl); 414 class != NULL; class = pcidb_class_iter_next(class)) { 415 walk->pw_class = class; 416 if (!pcidb_filter_match(walk)) 417 continue; 418 ofmt_print(walk->pw_ofmt, walk); 419 } 420 } 421 422 static void 423 pcidb_walk_subclasses(pcidb_walk_t *walk) 424 { 425 for (pcidb_class_t *class = pcidb_class_iter(walk->pw_hdl); 426 class != NULL; class = pcidb_class_iter_next(class)) { 427 walk->pw_class = class; 428 for (pcidb_subclass_t *sub = pcidb_subclass_iter(class); 429 sub != NULL; sub = pcidb_subclass_iter_next(sub)) { 430 walk->pw_subclass = sub; 431 if (!pcidb_filter_match(walk)) 432 continue; 433 ofmt_print(walk->pw_ofmt, walk); 434 } 435 } 436 } 437 438 static void 439 pcidb_walk_progifs(pcidb_walk_t *walk) 440 { 441 for (pcidb_class_t *class = pcidb_class_iter(walk->pw_hdl); 442 class != NULL; class = pcidb_class_iter_next(class)) { 443 walk->pw_class = class; 444 for (pcidb_subclass_t *sub = pcidb_subclass_iter(class); 445 sub != NULL; sub = pcidb_subclass_iter_next(sub)) { 446 walk->pw_subclass = sub; 447 for (pcidb_progif_t *progif = pcidb_progif_iter(sub); 448 progif != NULL; 449 progif = pcidb_progif_iter_next(progif)) { 450 walk->pw_progif = progif; 451 if (!pcidb_filter_match(walk)) 452 continue; 453 ofmt_print(walk->pw_ofmt, walk); 454 } 455 } 456 } 457 } 458 459 static void 460 pcidb_parse_class_filter(pcidb_filter_t *filter, char *arg, const char *orig) 461 { 462 size_t len; 463 unsigned long val; 464 char *eptr; 465 466 filter->pft_vend = filter->pft_dev = PCIDB_NOFILTER; 467 filter->pft_subven = filter->pft_subdev = PCIDB_NOFILTER; 468 469 len = strlen(arg); 470 if (len != 2 && len != 4 && len != 6) { 471 errx(EXIT_FAILURE, "invalid class filter: '%s': bad length", 472 orig); 473 } 474 475 errno = 0; 476 val = strtoul(arg, &eptr, 16); 477 if (errno != 0 || *eptr != '\0') { 478 errx(EXIT_FAILURE, "invalid class filter: '%s': failed to " 479 "parse hex string", orig); 480 } 481 482 if (len == 6) { 483 filter->pft_progif = val & 0xff; 484 val = val >> 8; 485 } else { 486 filter->pft_progif = PCIDB_NOFILTER; 487 } 488 489 if (len >= 4) { 490 filter->pft_subclass = val & 0xff; 491 val = val >> 8; 492 } else { 493 filter->pft_subclass = PCIDB_NOFILTER; 494 } 495 496 filter->pft_class = val & 0xff; 497 } 498 499 static void 500 pcidb_parse_device_filter(pcidb_filter_t *filter, char *arg, const char *orig) 501 { 502 unsigned long val; 503 uint32_t primary, secondary; 504 char *eptr; 505 506 filter->pft_vend = filter->pft_dev = PCIDB_NOFILTER; 507 filter->pft_subven = filter->pft_subdev = PCIDB_NOFILTER; 508 filter->pft_class = filter->pft_subclass = PCIDB_NOFILTER; 509 filter->pft_progif = PCIDB_NOFILTER; 510 511 errno = 0; 512 val = strtoul(arg, &eptr, 16); 513 if (errno != 0 || (*eptr != '\0' && *eptr != ',')) { 514 errx(EXIT_FAILURE, "invalid device filter: '%s': failed to " 515 "parse hex string", orig); 516 } 517 518 if (val > UINT16_MAX) { 519 errx(EXIT_FAILURE, "invalid id: %lx is larger than 0xffff", 520 val); 521 } 522 523 primary = (uint32_t)val; 524 if (*eptr == '\0') { 525 filter->pft_vend = primary; 526 return; 527 } else if (strcmp(eptr, ",s") == 0) { 528 filter->pft_subven = primary; 529 return; 530 } else if (eptr[1] == '\0') { 531 errx(EXIT_FAILURE, "invalid device filter: '%s': filter " 532 "terminated early", arg); 533 } 534 535 arg = eptr + 1; 536 val = strtoul(arg, &eptr, 16); 537 if (errno != 0 || (*eptr != '\0' && *eptr != ',' && *eptr != '.')) { 538 errx(EXIT_FAILURE, "invalid device filter: '%s': failed to " 539 "parse hex string at %s", orig, arg); 540 } 541 542 if (val > UINT16_MAX) { 543 errx(EXIT_FAILURE, "invalid id: %lx is larger than 0xffff", 544 val); 545 } 546 547 secondary = (uint32_t)val; 548 if (*eptr == '\0') { 549 filter->pft_vend = primary; 550 filter->pft_dev = secondary; 551 return; 552 } else if (eptr[1] == '\0') { 553 errx(EXIT_FAILURE, "invalid device filter: '%s': filter " 554 "terminated early", arg); 555 } 556 557 if (*eptr == ',') { 558 if (eptr[1] == 'p' && eptr[2] == '\0') { 559 filter->pft_vend = primary; 560 filter->pft_dev = secondary; 561 return; 562 } 563 if (eptr[1] == 's' && eptr[2] == '\0') { 564 filter->pft_subven = primary; 565 filter->pft_subdev = secondary; 566 return; 567 } 568 errx(EXIT_FAILURE, "invalid device filter: '%s': invalid " 569 "trailing comma at %s, expected either ,p or ,s", 570 orig, eptr); 571 } 572 573 filter->pft_vend = primary; 574 filter->pft_dev = secondary; 575 576 arg = eptr + 1; 577 errno = 0; 578 val = strtoul(arg, &eptr, 16); 579 if (errno != 0 || (*eptr != '\0' && *eptr != ',')) { 580 errx(EXIT_FAILURE, "invalid device filter: '%s': failed to " 581 "parse hex string at %s", orig, arg); 582 } 583 584 if (val > UINT16_MAX) { 585 errx(EXIT_FAILURE, "invalid id: %lx is larger than 0xffff", 586 val); 587 } 588 589 filter->pft_subven = (uint32_t)val; 590 if (*eptr == '\0') { 591 return; 592 } else if (eptr[1] == '\0') { 593 errx(EXIT_FAILURE, "invalid device filter: '%s': filter " 594 "terminated early", arg); 595 } 596 597 arg = eptr + 1; 598 errno = 0; 599 val = strtoul(arg, &eptr, 16); 600 if (errno != 0 || *eptr != '\0') { 601 errx(EXIT_FAILURE, "invalid device filter: '%s': failed to " 602 "parse hex string at %s", orig, arg); 603 } 604 605 if (val > UINT16_MAX) { 606 errx(EXIT_FAILURE, "invalid id: %lx is larger than 0xffff", 607 val); 608 } 609 610 filter->pft_subdev = (uint32_t)val; 611 } 612 613 614 /* 615 * Process a series of alias style ways of indicating numeric filters. Use the 616 * basic alias format for now. 617 */ 618 static void 619 pcidb_process_filters(int argc, char *argv[], pcidb_walk_t *walkp) 620 { 621 if (argc <= 0) { 622 walkp->pw_nfilters = 0; 623 return; 624 } 625 626 walkp->pw_nfilters = argc; 627 walkp->pw_filters = calloc(walkp->pw_nfilters, sizeof (pcidb_filter_t)); 628 if (walkp->pw_filters == NULL) { 629 err(EXIT_FAILURE, "failed to allocate memory for filters"); 630 } 631 632 for (int i = 0; i < argc; i++) { 633 char *str = strdup(argv[i]); 634 635 if (str == NULL) { 636 errx(EXIT_FAILURE, "failed to duplicate string %s", 637 argv[i]); 638 } 639 640 if (strncmp(str, "pciexclass,", 11) == 0) { 641 pcidb_parse_class_filter(&walkp->pw_filters[i], 642 str + 11, argv[i]); 643 } else if (strncmp(str, "pciclass,", 9) == 0) { 644 pcidb_parse_class_filter(&walkp->pw_filters[i], str + 9, 645 argv[i]); 646 } else if (strncmp(str, "pciex", 5) == 0) { 647 pcidb_parse_device_filter(&walkp->pw_filters[i], 648 str + 5, argv[i]); 649 } else if (strncmp(str, "pci", 3) == 0) { 650 pcidb_parse_device_filter(&walkp->pw_filters[i], 651 str + 3, argv[i]); 652 } else { 653 errx(EXIT_FAILURE, "invalid filter string: %s", str); 654 } 655 656 free(str); 657 } 658 } 659 660 static void 661 pcidb_drop_privs(void) 662 { 663 priv_set_t *curprivs, *targprivs; 664 665 if ((curprivs = priv_allocset()) == NULL) { 666 err(EXIT_FAILURE, "failed to allocate privilege set to drop " 667 "privs"); 668 } 669 670 if (getppriv(PRIV_EFFECTIVE, curprivs) != 0) { 671 err(EXIT_FAILURE, "failed to get current privileges"); 672 } 673 674 if ((targprivs = priv_allocset()) == NULL) { 675 err(EXIT_FAILURE, "failed to allocate privilege set to drop " 676 "privs"); 677 } 678 679 /* 680 * Set our privileges to the minimum required. Because stdout will have 681 * already been opened, all we need is the ability to read files from 682 * basic privileges. We opt to keep FILE_DAC_READ if the caller has it 683 * just in case there is something weird about the location of the 684 * pci.ids files. 685 */ 686 priv_basicset(targprivs); 687 VERIFY0(priv_delset(targprivs, PRIV_FILE_LINK_ANY)); 688 VERIFY0(priv_delset(targprivs, PRIV_PROC_INFO)); 689 VERIFY0(priv_delset(targprivs, PRIV_PROC_SESSION)); 690 VERIFY0(priv_delset(targprivs, PRIV_PROC_FORK)); 691 VERIFY0(priv_delset(targprivs, PRIV_NET_ACCESS)); 692 VERIFY0(priv_delset(targprivs, PRIV_FILE_WRITE)); 693 VERIFY0(priv_delset(targprivs, PRIV_PROC_EXEC)); 694 VERIFY0(priv_addset(targprivs, PRIV_FILE_DAC_READ)); 695 696 priv_intersect(curprivs, targprivs); 697 698 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, targprivs) != 0) { 699 err(EXIT_FAILURE, "failed to reduce privileges"); 700 } 701 702 priv_freeset(curprivs); 703 priv_freeset(targprivs); 704 } 705 706 static int 707 pcidb_usage(const char *fmt, ...) 708 { 709 if (fmt != NULL) { 710 va_list ap; 711 712 (void) fprintf(stderr, "%s: ", pcidb_progname); 713 va_start(ap, fmt); 714 (void) vfprintf(stderr, fmt, ap); 715 va_end(ap); 716 (void) fprintf(stderr, "\n"); 717 } 718 719 (void) fprintf(stderr, "usage: %s [-v|-d|-s|-c|-S|-i] [-H]" 720 "[[-p] [-o <field>[,...]] [<filter>]\n\n" 721 "\t-v\t\tshow vendor table\n" 722 "\t-d\t\tshow device table\n" 723 "\t-s\t\tshow subsystem table\n" 724 "\t-c\t\tshow class table\n" 725 "\t-S\t\tshow subclass table\n" 726 "\t-i\t\tshow programming interface table\n" 727 "\t-H\t\tdo not output column headers\n" 728 "\t-p\t\toutput in parsable form\n" 729 "\t-o field\toutput only specified fields\n\n" 730 "filters take the form of PCI aliases, e.g. pci8086,1522, " 731 "pci1028,1f44,s, or\n" 732 "pciex1022,1480.1462,7c37. Classes can be specified in a similar " 733 "way, e.g.\npciclass,010802 or pciclass,0403.\n", pcidb_progname); 734 735 return (EXIT_USAGE); 736 } 737 738 int 739 main(int argc, char *argv[]) 740 { 741 pcidb_hdl_t *hdl; 742 int c; 743 uint_t tablecnt = 0; 744 pcidb_table_t table = PCIDB_TABLE_NONE; 745 boolean_t parse = B_FALSE, strcase = B_FALSE; 746 const char *fields = NULL; 747 const char *ofmt_fields_str = NULL; 748 const ofmt_field_t *ofmt_fields = NULL; 749 ofmt_handle_t ofmt; 750 ofmt_status_t oferr; 751 uint_t flags = 0; 752 pcidb_walk_t walk; 753 754 bzero(&walk, sizeof (walk)); 755 pcidb_progname = basename(argv[0]); 756 757 pcidb_drop_privs(); 758 759 while ((c = getopt(argc, argv, ":vdscSipo:hH")) != -1) { 760 switch (c) { 761 case 'v': 762 tablecnt++; 763 table = PCIDB_TABLE_VENDOR; 764 break; 765 case 'd': 766 tablecnt++; 767 table = PCIDB_TABLE_DEVICE; 768 break; 769 case 's': 770 tablecnt++; 771 table = PCIDB_TABLE_SUBSYSTEM; 772 break; 773 case 'c': 774 tablecnt++; 775 table = PCIDB_TABLE_CLASS; 776 break; 777 case 'S': 778 tablecnt++; 779 table = PCIDB_TABLE_SUBCLASS; 780 break; 781 case 'i': 782 tablecnt++; 783 table = PCIDB_TABLE_PROGIF; 784 break; 785 case 'p': 786 parse = B_TRUE; 787 flags |= OFMT_PARSABLE; 788 break; 789 case 'o': 790 fields = optarg; 791 break; 792 case 'h': 793 return (pcidb_usage(NULL)); 794 case 'H': 795 flags |= OFMT_NOHEADER; 796 break; 797 case ':': 798 return (pcidb_usage("Option -%c requires an argument", 799 optopt)); 800 case '?': 801 return (pcidb_usage("unknown option: -%c", optopt)); 802 } 803 } 804 805 if (tablecnt > 1) { 806 errx(EXIT_USAGE, "more than one table specified, only one of " 807 "-v, -d, -s, -c, -S, and -i may be specified"); 808 } 809 810 if (parse && fields == NULL) { 811 errx(EXIT_USAGE, "-p requires fields specified with -o"); 812 } 813 814 argc -= optind; 815 argv += optind; 816 817 pcidb_process_filters(argc, argv, &walk); 818 819 switch (table) { 820 case PCIDB_TABLE_VENDOR: 821 ofmt_fields = pcidb_vendor_ofmt; 822 ofmt_fields_str = pcidb_vendor_fields; 823 break; 824 case PCIDB_TABLE_NONE: 825 case PCIDB_TABLE_DEVICE: 826 ofmt_fields = pcidb_device_ofmt; 827 ofmt_fields_str = pcidb_device_fields; 828 break; 829 case PCIDB_TABLE_SUBSYSTEM: 830 ofmt_fields = pcidb_subsystem_ofmt; 831 ofmt_fields_str = pcidb_subsystem_fields; 832 break; 833 case PCIDB_TABLE_CLASS: 834 ofmt_fields = pcidb_class_ofmt; 835 ofmt_fields_str = pcidb_class_fields; 836 break; 837 case PCIDB_TABLE_SUBCLASS: 838 ofmt_fields = pcidb_subclass_ofmt; 839 ofmt_fields_str = pcidb_subclass_fields; 840 break; 841 case PCIDB_TABLE_PROGIF: 842 ofmt_fields = pcidb_progif_ofmt; 843 ofmt_fields_str = pcidb_progif_fields; 844 break; 845 } 846 847 if (fields == NULL) { 848 fields = ofmt_fields_str; 849 } 850 851 oferr = ofmt_open(fields, ofmt_fields, flags, 0, &ofmt); 852 ofmt_check(oferr, parse, ofmt, pcidb_ofmt_errx, warnx); 853 854 hdl = pcidb_open(PCIDB_VERSION); 855 if (hdl == NULL) { 856 err(EXIT_FAILURE, "failed to initialize PCI IDs database"); 857 } 858 859 walk.pw_hdl = hdl; 860 walk.pw_ofmt = ofmt; 861 walk.pw_strcase = strcase; 862 863 switch (table) { 864 case PCIDB_TABLE_VENDOR: 865 pcidb_walk_vendors(&walk); 866 break; 867 case PCIDB_TABLE_NONE: 868 case PCIDB_TABLE_DEVICE: 869 pcidb_walk_devices(&walk); 870 break; 871 case PCIDB_TABLE_SUBSYSTEM: 872 pcidb_walk_subsystems(&walk); 873 break; 874 case PCIDB_TABLE_CLASS: 875 pcidb_walk_classes(&walk); 876 break; 877 case PCIDB_TABLE_SUBCLASS: 878 pcidb_walk_subclasses(&walk); 879 break; 880 case PCIDB_TABLE_PROGIF: 881 pcidb_walk_progifs(&walk); 882 break; 883 } 884 885 ofmt_close(ofmt); 886 pcidb_close(hdl); 887 return (EXIT_SUCCESS); 888 } 889