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: %x is larger than 0xffff", val); 520 } 521 522 primary = (uint32_t)val; 523 if (*eptr == '\0') { 524 filter->pft_vend = primary; 525 return; 526 } else if (strcmp(eptr, ",s") == 0) { 527 filter->pft_subven = primary; 528 return; 529 } else if (eptr[1] == '\0') { 530 errx(EXIT_FAILURE, "invalid device filter: '%s': filter " 531 "terminated early", arg); 532 } 533 534 arg = eptr + 1; 535 val = strtoul(arg, &eptr, 16); 536 if (errno != 0 || (*eptr != '\0' && *eptr != ',' && *eptr != '.')) { 537 errx(EXIT_FAILURE, "invalid device filter: '%s': failed to " 538 "parse hex string at %s", orig, arg); 539 } 540 541 if (val > UINT16_MAX) { 542 errx(EXIT_FAILURE, "invalid id: %x is larger than 0xffff", val); 543 } 544 545 secondary = (uint32_t)val; 546 if (*eptr == '\0') { 547 filter->pft_vend = primary; 548 filter->pft_dev = secondary; 549 return; 550 } else if (eptr[1] == '\0') { 551 errx(EXIT_FAILURE, "invalid device filter: '%s': filter " 552 "terminated early", arg); 553 } 554 555 if (*eptr == ',') { 556 if (eptr[1] == 'p' && eptr[2] == '\0') { 557 filter->pft_vend = primary; 558 filter->pft_dev = secondary; 559 return; 560 } 561 if (eptr[1] == 's' && eptr[2] == '\0') { 562 filter->pft_subven = primary; 563 filter->pft_subdev = secondary; 564 return; 565 } 566 errx(EXIT_FAILURE, "invalid device filter: '%s': invalid " 567 "trailing comma at %s, expected either ,p or ,s", 568 orig, eptr); 569 } 570 571 filter->pft_vend = primary; 572 filter->pft_dev = secondary; 573 574 arg = eptr + 1; 575 errno = 0; 576 val = strtoul(arg, &eptr, 16); 577 if (errno != 0 || (*eptr != '\0' && *eptr != ',')) { 578 errx(EXIT_FAILURE, "invalid device filter: '%s': failed to " 579 "parse hex string at %s", orig, arg); 580 } 581 582 if (val > UINT16_MAX) { 583 errx(EXIT_FAILURE, "invalid id: %x is larger than 0xffff", val); 584 } 585 586 filter->pft_subven = (uint32_t)val; 587 if (*eptr == '\0') { 588 return; 589 } else if (eptr[1] == '\0') { 590 errx(EXIT_FAILURE, "invalid device filter: '%s': filter " 591 "terminated early", arg); 592 } 593 594 arg = eptr + 1; 595 errno = 0; 596 val = strtoul(arg, &eptr, 16); 597 if (errno != 0 || *eptr != '\0') { 598 errx(EXIT_FAILURE, "invalid device filter: '%s': failed to " 599 "parse hex string at %s", orig, arg); 600 } 601 602 if (val > UINT16_MAX) { 603 errx(EXIT_FAILURE, "invalid id: %x is larger than 0xffff", val); 604 } 605 606 filter->pft_subdev = (uint32_t)val; 607 } 608 609 610 /* 611 * Process a series of alias style ways of indicating numeric filters. Use the 612 * basic alias format for now. 613 */ 614 static void 615 pcidb_process_filters(int argc, char *argv[], pcidb_walk_t *walkp) 616 { 617 if (argc <= 0) { 618 walkp->pw_nfilters = 0; 619 return; 620 } 621 622 walkp->pw_nfilters = argc; 623 walkp->pw_filters = calloc(walkp->pw_nfilters, sizeof (pcidb_filter_t)); 624 if (walkp->pw_filters == NULL) { 625 err(EXIT_FAILURE, "failed to allocate memory for filters"); 626 } 627 628 for (int i = 0; i < argc; i++) { 629 char *str = strdup(argv[i]); 630 631 if (str == NULL) { 632 errx(EXIT_FAILURE, "failed to duplicate string %s", 633 argv[i]); 634 } 635 636 if (strncmp(str, "pciexclass,", 11) == 0) { 637 pcidb_parse_class_filter(&walkp->pw_filters[i], 638 str + 11, argv[i]); 639 } else if (strncmp(str, "pciclass,", 9) == 0) { 640 pcidb_parse_class_filter(&walkp->pw_filters[i], str + 9, 641 argv[i]); 642 } else if (strncmp(str, "pciex", 5) == 0) { 643 pcidb_parse_device_filter(&walkp->pw_filters[i], 644 str + 5, argv[i]); 645 } else if (strncmp(str, "pci", 3) == 0) { 646 pcidb_parse_device_filter(&walkp->pw_filters[i], 647 str + 3, argv[i]); 648 } else { 649 errx(EXIT_FAILURE, "invalid filter string: %s", str); 650 } 651 652 free(str); 653 } 654 } 655 656 static void 657 pcidb_drop_privs(void) 658 { 659 priv_set_t *curprivs, *targprivs; 660 661 if ((curprivs = priv_allocset()) == NULL) { 662 err(EXIT_FAILURE, "failed to allocate privilege set to drop " 663 "privs"); 664 } 665 666 if (getppriv(PRIV_EFFECTIVE, curprivs) != 0) { 667 err(EXIT_FAILURE, "failed to get current privileges"); 668 } 669 670 if ((targprivs = priv_allocset()) == NULL) { 671 err(EXIT_FAILURE, "failed to allocate privilege set to drop " 672 "privs"); 673 } 674 675 /* 676 * Set our privileges to the minimum required. Because stdout will have 677 * already been opened, all we need is the ability to read files from 678 * basic privileges. We opt to keep FILE_DAC_READ if the caller has it 679 * just in case there is something weird about the location of the 680 * pci.ids files. 681 */ 682 priv_basicset(targprivs); 683 VERIFY0(priv_delset(targprivs, PRIV_FILE_LINK_ANY)); 684 VERIFY0(priv_delset(targprivs, PRIV_PROC_INFO)); 685 VERIFY0(priv_delset(targprivs, PRIV_PROC_SESSION)); 686 VERIFY0(priv_delset(targprivs, PRIV_PROC_FORK)); 687 VERIFY0(priv_delset(targprivs, PRIV_NET_ACCESS)); 688 VERIFY0(priv_delset(targprivs, PRIV_FILE_WRITE)); 689 VERIFY0(priv_delset(targprivs, PRIV_PROC_EXEC)); 690 VERIFY0(priv_addset(targprivs, PRIV_FILE_DAC_READ)); 691 692 priv_intersect(curprivs, targprivs); 693 694 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, targprivs) != 0) { 695 err(EXIT_FAILURE, "failed to reduce privileges"); 696 } 697 698 priv_freeset(curprivs); 699 priv_freeset(targprivs); 700 } 701 702 static int 703 pcidb_usage(const char *fmt, ...) 704 { 705 if (fmt != NULL) { 706 va_list ap; 707 708 (void) fprintf(stderr, "%s: ", pcidb_progname); 709 va_start(ap, fmt); 710 (void) vfprintf(stderr, fmt, ap); 711 va_end(ap); 712 (void) fprintf(stderr, "\n"); 713 } 714 715 (void) fprintf(stderr, "usage: %s [-v|-d|-s|-c|-S|-i] [-H]" 716 "[[-p] [-o <field>[,...]] [<filter>]\n\n" 717 "\t-v\t\tshow vendor table\n" 718 "\t-d\t\tshow device table\n" 719 "\t-s\t\tshow subsystem table\n" 720 "\t-c\t\tshow class table\n" 721 "\t-S\t\tshow subclass table\n" 722 "\t-i\t\tshow programming interface table\n" 723 "\t-H\t\tdo not output column headers\n" 724 "\t-p\t\toutput in parsable form\n" 725 "\t-o field\toutput only specified fields\n\n" 726 "filters take the form of PCI aliases, e.g. pci8086,1522, " 727 "pci1028,1f44,s, or\n" 728 "pciex1022,1480.1462,7c37. Classes can be specified in a similar " 729 "way, e.g.\npciclass,010802 or pciclass,0403.\n", pcidb_progname); 730 731 return (EXIT_USAGE); 732 } 733 734 int 735 main(int argc, char *argv[]) 736 { 737 pcidb_hdl_t *hdl; 738 int c; 739 uint_t tablecnt = 0; 740 pcidb_table_t table = PCIDB_TABLE_NONE; 741 boolean_t parse = B_FALSE, strcase = B_FALSE; 742 const char *fields = NULL; 743 const char *ofmt_fields_str = NULL; 744 const ofmt_field_t *ofmt_fields = NULL; 745 ofmt_handle_t ofmt; 746 ofmt_status_t oferr; 747 uint_t flags = 0; 748 pcidb_walk_t walk; 749 750 bzero(&walk, sizeof (walk)); 751 pcidb_progname = basename(argv[0]); 752 753 pcidb_drop_privs(); 754 755 while ((c = getopt(argc, argv, ":vdscSipo:hH")) != -1) { 756 switch (c) { 757 case 'v': 758 tablecnt++; 759 table = PCIDB_TABLE_VENDOR; 760 break; 761 case 'd': 762 tablecnt++; 763 table = PCIDB_TABLE_DEVICE; 764 break; 765 case 's': 766 tablecnt++; 767 table = PCIDB_TABLE_SUBSYSTEM; 768 break; 769 case 'c': 770 tablecnt++; 771 table = PCIDB_TABLE_CLASS; 772 break; 773 case 'S': 774 tablecnt++; 775 table = PCIDB_TABLE_SUBCLASS; 776 break; 777 case 'i': 778 tablecnt++; 779 table = PCIDB_TABLE_PROGIF; 780 break; 781 case 'p': 782 parse = B_TRUE; 783 flags |= OFMT_PARSABLE; 784 break; 785 case 'o': 786 fields = optarg; 787 break; 788 case 'h': 789 return (pcidb_usage(NULL)); 790 case 'H': 791 flags |= OFMT_NOHEADER; 792 break; 793 case ':': 794 return (pcidb_usage("Option -%c requires an argument", 795 optopt)); 796 case '?': 797 return (pcidb_usage("unknown option: -%c", optopt)); 798 } 799 } 800 801 if (tablecnt > 1) { 802 errx(EXIT_USAGE, "more than one table specified, only one of " 803 "-v, -d, -s, -c, -S, and -i may be specified"); 804 } 805 806 if (parse && fields == NULL) { 807 errx(EXIT_USAGE, "-p requires fields specified with -o"); 808 } 809 810 argc -= optind; 811 argv += optind; 812 813 pcidb_process_filters(argc, argv, &walk); 814 815 switch (table) { 816 case PCIDB_TABLE_VENDOR: 817 ofmt_fields = pcidb_vendor_ofmt; 818 ofmt_fields_str = pcidb_vendor_fields; 819 break; 820 case PCIDB_TABLE_NONE: 821 case PCIDB_TABLE_DEVICE: 822 ofmt_fields = pcidb_device_ofmt; 823 ofmt_fields_str = pcidb_device_fields; 824 break; 825 case PCIDB_TABLE_SUBSYSTEM: 826 ofmt_fields = pcidb_subsystem_ofmt; 827 ofmt_fields_str = pcidb_subsystem_fields; 828 break; 829 case PCIDB_TABLE_CLASS: 830 ofmt_fields = pcidb_class_ofmt; 831 ofmt_fields_str = pcidb_class_fields; 832 break; 833 case PCIDB_TABLE_SUBCLASS: 834 ofmt_fields = pcidb_subclass_ofmt; 835 ofmt_fields_str = pcidb_subclass_fields; 836 break; 837 case PCIDB_TABLE_PROGIF: 838 ofmt_fields = pcidb_progif_ofmt; 839 ofmt_fields_str = pcidb_progif_fields; 840 break; 841 } 842 843 if (fields == NULL) { 844 fields = ofmt_fields_str; 845 } 846 847 oferr = ofmt_open(fields, ofmt_fields, flags, 0, &ofmt); 848 ofmt_check(oferr, parse, ofmt, pcidb_ofmt_errx, warnx); 849 850 hdl = pcidb_open(PCIDB_VERSION); 851 if (hdl == NULL) { 852 err(EXIT_FAILURE, "failed to initialize PCI IDs database"); 853 } 854 855 walk.pw_hdl = hdl; 856 walk.pw_ofmt = ofmt; 857 walk.pw_strcase = strcase; 858 859 switch (table) { 860 case PCIDB_TABLE_VENDOR: 861 pcidb_walk_vendors(&walk); 862 break; 863 case PCIDB_TABLE_NONE: 864 case PCIDB_TABLE_DEVICE: 865 pcidb_walk_devices(&walk); 866 break; 867 case PCIDB_TABLE_SUBSYSTEM: 868 pcidb_walk_subsystems(&walk); 869 break; 870 case PCIDB_TABLE_CLASS: 871 pcidb_walk_classes(&walk); 872 break; 873 case PCIDB_TABLE_SUBCLASS: 874 pcidb_walk_subclasses(&walk); 875 break; 876 case PCIDB_TABLE_PROGIF: 877 pcidb_walk_progifs(&walk); 878 break; 879 } 880 881 ofmt_close(ofmt); 882 pcidb_close(hdl); 883 return (EXIT_SUCCESS); 884 } 885