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 (c) 2013 Joyent Inc., All rights reserved. 14 */ 15 16 #include <sys/types.h> 17 #include <sys/stat.h> 18 #include <fcntl.h> 19 #include <errno.h> 20 #include <string.h> 21 #include <stdio.h> 22 #include <unistd.h> 23 #include <limits.h> 24 #include <assert.h> 25 #include <ctype.h> 26 #include <stdarg.h> 27 #include <strings.h> 28 29 #include <libdiskmgt.h> 30 #include <sys/nvpair.h> 31 #include <sys/param.h> 32 #include <sys/ccompile.h> 33 #include <sys/list.h> 34 35 #include <fm/libtopo.h> 36 #include <fm/topo_hc.h> 37 #include <fm/topo_list.h> 38 #include <sys/fm/protocol.h> 39 #include <modules/common/disk/disk.h> 40 41 typedef struct di_opts { 42 boolean_t di_allslots; 43 boolean_t di_scripted; 44 boolean_t di_parseable; 45 boolean_t di_physical; 46 boolean_t di_condensed; 47 } di_opts_t; 48 49 static list_t g_disks; 50 static di_opts_t g_opts = { B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_FALSE }; 51 52 typedef struct di_phys { 53 uint64_t dp_size; 54 uint32_t dp_blksize; 55 char *dp_vid; 56 char *dp_pid; 57 boolean_t dp_removable; 58 boolean_t dp_ssd; 59 char *dp_dev; 60 char *dp_ctype; 61 char *dp_serial; 62 char *dp_slotname; 63 int dp_chassis; 64 int dp_slot; 65 int dp_faulty; 66 int dp_locate; 67 list_node_t dp_next; 68 } di_phys_t; 69 70 static void __NORETURN 71 fatal(int rv, const char *fmt, ...) 72 { 73 va_list ap; 74 75 va_start(ap, fmt); 76 (void) vfprintf(stderr, fmt, ap); 77 va_end(ap); 78 79 exit(rv); 80 } 81 82 static void * 83 safe_zmalloc(size_t size) 84 { 85 void *ptr = malloc(size); 86 if (ptr == NULL) 87 fatal(-1, "failed to allocate memeory"); 88 memset(ptr, 0, size); 89 return (ptr); 90 } 91 92 static char * 93 safe_strdup(const char *s1) 94 { 95 char *s2 = strdup(s1); 96 if (s2 == NULL) 97 fatal(-1, "failed to allocate memeory"); 98 return (s2); 99 } 100 101 static int 102 safe_asprintf(char **ret, const char *fmt, ...) 103 { 104 va_list ap; 105 int v; 106 107 va_start(ap, fmt); 108 v = vasprintf(ret, fmt, ap); 109 va_end(ap); 110 if (v == -1) 111 fatal(-1, "failed to allocate memeory"); 112 return (v); 113 } 114 115 static void 116 usage(const char *execname) 117 { 118 (void) fprintf(stderr, "Usage: %s [-aHp] [{-c|-P}]\n", execname); 119 } 120 121 static void 122 nvlist_query_string(nvlist_t *nvl, const char *label, char **val) 123 { 124 if (nvlist_lookup_string(nvl, label, val) != 0) 125 *val = "-"; 126 } 127 128 static const char * 129 display_string(const char *label) 130 { 131 return ((label) ? label : "-"); 132 } 133 134 static const char * 135 display_tristate(int val) 136 { 137 if (val == 0) 138 return ("no"); 139 if (val == 1) 140 return ("yes"); 141 142 return ("-"); 143 } 144 145 static char 146 condensed_tristate(int val, char c) 147 { 148 if (val == 0) 149 return ('-'); 150 if (val == 1) 151 return (c); 152 153 return ('?'); 154 } 155 156 static void 157 set_disk_bay_info(topo_hdl_t *hp, tnode_t *np, di_phys_t *dip) 158 { 159 topo_faclist_t fl; 160 topo_faclist_t *lp; 161 topo_led_state_t mode; 162 topo_led_type_t type; 163 int err; 164 165 if (strcmp(topo_node_name(np), BAY) != 0) 166 return; 167 168 if (topo_node_facility(hp, np, TOPO_FAC_TYPE_INDICATOR, 169 TOPO_FAC_TYPE_ANY, &fl, &err) == 0) { 170 for (lp = topo_list_next(&fl.tf_list); lp != NULL; 171 lp = topo_list_next(lp)) { 172 uint32_t prop; 173 174 if (topo_prop_get_uint32(lp->tf_node, 175 TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE, 176 &prop, &err) != 0) { 177 continue; 178 } 179 type = (topo_led_type_t)prop; 180 181 if (topo_prop_get_uint32(lp->tf_node, 182 TOPO_PGROUP_FACILITY, TOPO_LED_MODE, 183 &prop, &err) != 0) { 184 continue; 185 } 186 mode = (topo_led_state_t)prop; 187 188 switch (type) { 189 case TOPO_LED_TYPE_SERVICE: 190 dip->dp_faulty = mode ? 1 : 0; 191 break; 192 case TOPO_LED_TYPE_LOCATE: 193 dip->dp_locate = mode ? 1 : 0; 194 break; 195 default: 196 break; 197 } 198 } 199 } 200 201 if (topo_prop_get_string(np, TOPO_PGROUP_PROTOCOL, 202 TOPO_PROP_LABEL, &dip->dp_slotname, &err) == 0) { 203 dip->dp_slotname = safe_strdup(dip->dp_slotname); 204 } 205 206 dip->dp_slot = topo_node_instance(np); 207 dip->dp_chassis = topo_node_instance(topo_node_parent(np)); 208 } 209 210 static int 211 bay_walker(topo_hdl_t *hp, tnode_t *np, void *arg) 212 { 213 di_phys_t *dip; 214 int slot, chassis; 215 216 if (strcmp(topo_node_name(np), BAY) != 0) 217 return (TOPO_WALK_NEXT); 218 219 slot = topo_node_instance(np); 220 chassis = topo_node_instance(topo_node_parent(np)); 221 222 for (dip = list_head(&g_disks); dip != NULL; 223 dip = list_next(&g_disks, dip)) { 224 if (dip->dp_slot == slot && dip->dp_chassis == chassis) 225 return (TOPO_WALK_NEXT); 226 } 227 228 dip = safe_zmalloc(sizeof (di_phys_t)); 229 set_disk_bay_info(hp, np, dip); 230 list_insert_tail(&g_disks, dip); 231 return (TOPO_WALK_NEXT); 232 } 233 234 static int 235 disk_walker(topo_hdl_t *hp, tnode_t *np, void *arg) 236 { 237 char *dev; 238 di_phys_t *dip; 239 int err; 240 241 if (strcmp(topo_node_name(np), DISK) != 0) 242 return (TOPO_WALK_NEXT); 243 244 if (topo_prop_get_string(np, TOPO_PGROUP_STORAGE, 245 TOPO_STORAGE_LOGICAL_DISK_NAME, &dev, &err) != 0) { 246 return (TOPO_WALK_NEXT); 247 } 248 249 for (dip = list_head(&g_disks); dip != NULL; 250 dip = list_next(&g_disks, dip)) { 251 if (strcmp(dip->dp_dev, dev) == 0) { 252 if (topo_prop_get_string(np, TOPO_PGROUP_STORAGE, 253 TOPO_STORAGE_SERIAL_NUM, 254 &dip->dp_serial, &err) == 0) { 255 dip->dp_serial = safe_strdup(dip->dp_serial); 256 } 257 set_disk_bay_info(hp, topo_node_parent(np), dip); 258 } 259 } 260 return (TOPO_WALK_NEXT); 261 } 262 263 static void 264 walk_topo_snapshot(topo_hdl_t *hp, topo_walk_cb_t cb) 265 { 266 int err = 0; 267 topo_walk_t *wp; 268 269 if ((wp = topo_walk_init(hp, FM_FMRI_SCHEME_HC, cb, NULL, 270 &err)) == NULL) { 271 fatal(-1, "unable to initialise topo walker: %s", 272 topo_strerror(err)); 273 } 274 275 while ((err = topo_walk_step(wp, TOPO_WALK_CHILD)) == TOPO_WALK_NEXT) 276 ; 277 278 if (err == TOPO_WALK_ERR) 279 fatal(-1, "topo walk failed"); 280 topo_walk_fini(wp); 281 } 282 283 static void 284 enumerate_disks() 285 { 286 topo_hdl_t *hp; 287 dm_descriptor_t *media; 288 int filter[] = { DM_DT_FIXED, -1 }; 289 dm_descriptor_t *disk, *controller; 290 nvlist_t *mattrs, *dattrs, *cattrs; 291 292 char *s, *c; 293 di_phys_t *dip; 294 size_t len; 295 int err, i; 296 297 list_create(&g_disks, sizeof (di_phys_t), offsetof(di_phys_t, dp_next)); 298 299 err = 0; 300 if ((media = dm_get_descriptors(DM_MEDIA, filter, &err)) == NULL) { 301 fatal(-1, "failed to obtain media descriptors: %s\n", 302 strerror(err)); 303 } 304 305 for (i = 0; media != NULL && media[i] != NULL; i++) { 306 if ((disk = dm_get_associated_descriptors(media[i], 307 DM_DRIVE, &err)) == NULL) { 308 continue; 309 } 310 311 dip = safe_zmalloc(sizeof (di_phys_t)); 312 313 mattrs = dm_get_attributes(media[i], &err); 314 err = nvlist_lookup_uint64(mattrs, DM_SIZE, &dip->dp_size); 315 assert(err == 0); 316 err = nvlist_lookup_uint32(mattrs, DM_BLOCKSIZE, 317 &dip->dp_blksize); 318 assert(err == 0); 319 nvlist_free(mattrs); 320 321 dattrs = dm_get_attributes(disk[0], &err); 322 323 nvlist_query_string(dattrs, DM_VENDOR_ID, &dip->dp_vid); 324 nvlist_query_string(dattrs, DM_PRODUCT_ID, &dip->dp_pid); 325 nvlist_query_string(dattrs, DM_OPATH, &dip->dp_dev); 326 327 dip->dp_vid = safe_strdup(dip->dp_vid); 328 dip->dp_pid = safe_strdup(dip->dp_pid); 329 dip->dp_dev = safe_strdup(dip->dp_dev); 330 331 dip->dp_removable = B_FALSE; 332 if (nvlist_lookup_boolean(dattrs, DM_REMOVABLE) == 0) 333 dip->dp_removable = B_TRUE; 334 335 dip->dp_ssd = B_FALSE; 336 if (nvlist_lookup_boolean(dattrs, DM_SOLIDSTATE) == 0) 337 dip->dp_ssd = B_TRUE; 338 339 nvlist_free(dattrs); 340 341 if ((controller = dm_get_associated_descriptors(disk[0], 342 DM_CONTROLLER, &err)) != NULL) { 343 cattrs = dm_get_attributes(controller[0], &err); 344 nvlist_query_string(cattrs, DM_CTYPE, &dip->dp_ctype); 345 dip->dp_ctype = safe_strdup(dip->dp_ctype); 346 for (c = dip->dp_ctype; *c != '\0'; c++) 347 *c = toupper(*c); 348 nvlist_free(cattrs); 349 } 350 351 dm_free_descriptors(controller); 352 dm_free_descriptors(disk); 353 354 /* 355 * Parse full device path to only show the device name, 356 * i.e. c0t1d0. Many paths will reference a particular 357 * slice (c0t1d0s0), so remove the slice if present. 358 */ 359 if ((c = strrchr(dip->dp_dev, '/')) != NULL) { 360 s = dip->dp_dev; 361 while ((*s++ = *++c)) 362 ; 363 } 364 len = strlen(dip->dp_dev); 365 if (dip->dp_dev[len - 2] == 's' && 366 dip->dp_dev[len - 1] >= '0' && 367 dip->dp_dev[len - 1] <= '9') 368 dip->dp_dev[len - 2] = '\0'; 369 370 dip->dp_faulty = dip->dp_locate = -1; 371 dip->dp_chassis = dip->dp_slot = -1; 372 list_insert_tail(&g_disks, dip); 373 } 374 375 dm_free_descriptors(media); 376 377 /* 378 * Walk toplogy information to populate serial, chassis, 379 * slot, faulty and locator information. 380 */ 381 382 err = 0; 383 hp = topo_open(TOPO_VERSION, NULL, &err); 384 if (hp == NULL) { 385 fatal(-1, "unable to obtain topo handle: %s", 386 topo_strerror(err)); 387 } 388 389 err = 0; 390 (void) topo_snap_hold(hp, NULL, &err); 391 if (err != 0) { 392 fatal(-1, "unable to hold topo snapshot: %s", 393 topo_strerror(err)); 394 } 395 396 walk_topo_snapshot(hp, disk_walker); 397 398 if (g_opts.di_allslots) 399 walk_topo_snapshot(hp, bay_walker); 400 401 topo_snap_release(hp); 402 topo_close(hp); 403 } 404 405 static void 406 show_disks() 407 { 408 uint64_t total; 409 double total_in_GiB; 410 char *sizestr = NULL, *slotname = NULL, *statestr = NULL; 411 di_phys_t *dip; 412 413 for (dip = list_head(&g_disks); dip != NULL; 414 dip = list_next(&g_disks, dip)) { 415 /* 416 * The size is given in blocks, so multiply the number 417 * of blocks by the block size to get the total size, 418 * then convert to GiB. 419 */ 420 total = dip->dp_size * dip->dp_blksize; 421 422 if (g_opts.di_parseable) { 423 (void) safe_asprintf(&sizestr, "%llu", total); 424 } else { 425 total_in_GiB = (double)total / 426 1024.0 / 1024.0 / 1024.0; 427 (void) safe_asprintf(&sizestr, 428 "%7.2f GiB", (total_in_GiB)); 429 } 430 431 if (g_opts.di_parseable) { 432 (void) safe_asprintf(&slotname, "%d,%d", 433 dip->dp_chassis, dip->dp_slot); 434 } else if (dip->dp_slotname != NULL) { 435 (void) safe_asprintf(&slotname, "[%d] %s", 436 dip->dp_chassis, dip->dp_slotname); 437 } else { 438 slotname = safe_strdup("-"); 439 } 440 441 if (g_opts.di_condensed) { 442 (void) safe_asprintf(&statestr, "%c%c%c%c", 443 condensed_tristate(dip->dp_faulty, 'F'), 444 condensed_tristate(dip->dp_locate, 'L'), 445 condensed_tristate(dip->dp_removable, 'R'), 446 condensed_tristate(dip->dp_ssd, 'S')); 447 } 448 449 if (g_opts.di_physical) { 450 if (g_opts.di_scripted) { 451 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 452 display_string(dip->dp_dev), 453 display_string(dip->dp_vid), 454 display_string(dip->dp_pid), 455 display_string(dip->dp_serial), 456 display_tristate(dip->dp_faulty), 457 display_tristate(dip->dp_locate), slotname); 458 } else { 459 printf("%-22s %-8s %-16s " 460 "%-20s %-3s %-3s %s\n", 461 display_string(dip->dp_dev), 462 display_string(dip->dp_vid), 463 display_string(dip->dp_pid), 464 display_string(dip->dp_serial), 465 display_tristate(dip->dp_faulty), 466 display_tristate(dip->dp_locate), slotname); 467 } 468 } else if (g_opts.di_condensed) { 469 if (g_opts.di_scripted) { 470 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 471 display_string(dip->dp_ctype), 472 display_string(dip->dp_dev), 473 display_string(dip->dp_vid), 474 display_string(dip->dp_pid), 475 display_string(dip->dp_serial), 476 sizestr, statestr, slotname); 477 } else { 478 printf("%-7s %-22s %-8s %-16s " 479 "%-20s\n\t%-13s %-4s %s\n", 480 display_string(dip->dp_ctype), 481 display_string(dip->dp_dev), 482 display_string(dip->dp_vid), 483 display_string(dip->dp_pid), 484 display_string(dip->dp_serial), 485 sizestr, statestr, slotname); 486 } 487 } else { 488 if (g_opts.di_scripted) { 489 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 490 display_string(dip->dp_ctype), 491 display_string(dip->dp_dev), 492 display_string(dip->dp_vid), 493 display_string(dip->dp_pid), sizestr, 494 display_tristate(dip->dp_removable), 495 display_tristate(dip->dp_ssd)); 496 } else { 497 printf("%-7s %-22s %-8s %-16s " 498 "%-13s %-3s %-3s\n", 499 display_string(dip->dp_ctype), 500 display_string(dip->dp_dev), 501 display_string(dip->dp_vid), 502 display_string(dip->dp_pid), sizestr, 503 display_tristate(dip->dp_removable), 504 display_tristate(dip->dp_ssd)); 505 } 506 } 507 free(sizestr); free(slotname); free(statestr); 508 sizestr = slotname = statestr = NULL; 509 } 510 } 511 512 static void 513 cleanup() 514 { 515 di_phys_t *dip; 516 while ((dip = list_head(&g_disks)) != NULL) { 517 list_remove(&g_disks, dip); 518 free(dip->dp_vid); 519 free(dip->dp_pid); 520 free(dip->dp_dev); 521 free(dip->dp_ctype); 522 free(dip->dp_serial); 523 free(dip->dp_slotname); 524 free(dip); 525 } 526 list_destroy(&g_disks); 527 } 528 529 int 530 main(int argc, char *argv[]) 531 { 532 char c; 533 534 while ((c = getopt(argc, argv, ":acHPp")) != EOF) { 535 switch (c) { 536 case 'a': 537 g_opts.di_allslots = B_TRUE; 538 break; 539 case 'c': 540 g_opts.di_condensed = B_TRUE; 541 break; 542 case 'H': 543 g_opts.di_scripted = B_TRUE; 544 break; 545 case 'P': 546 g_opts.di_physical = B_TRUE; 547 break; 548 case 'p': 549 g_opts.di_parseable = B_TRUE; 550 break; 551 case '?': 552 usage(argv[0]); 553 fatal(1, "unknown option -%c\n", optopt); 554 default: 555 fatal(-1, "unexpected error on option -%c\n", optopt); 556 } 557 } 558 559 if (g_opts.di_condensed && g_opts.di_physical) { 560 usage(argv[0]); 561 fatal(1, "-c and -P are mutually exclusive\n"); 562 } 563 564 if (!g_opts.di_scripted) { 565 if (g_opts.di_physical) { 566 printf("DISK VID PID" 567 " SERIAL FLT LOC" 568 " LOCATION\n"); 569 } else if (g_opts.di_condensed) { 570 printf("TYPE DISK VID PID" 571 " SERIAL\n"); 572 printf("\tSIZE FLRS LOCATION\n"); 573 } else { 574 printf("TYPE DISK VID PID" 575 " SIZE RMV SSD\n"); 576 } 577 } 578 579 enumerate_disks(); 580 show_disks(); 581 cleanup(); 582 583 return (0); 584 }