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) 2018 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 34 #include <fm/libtopo.h> 35 #include <fm/topo_hc.h> 36 #include <fm/topo_list.h> 37 #include <sys/fm/protocol.h> 38 #include <modules/common/disk/disk.h> 39 40 typedef struct di_opts { 41 boolean_t di_scripted; 42 boolean_t di_parseable; 43 boolean_t di_physical; 44 boolean_t di_condensed; 45 } di_opts_t; 46 47 typedef struct di_phys { 48 const char *dp_dev; 49 const char *dp_serial; 50 const char *dp_slotname; 51 int dp_chassis; 52 int dp_slot; 53 int dp_faulty; 54 int dp_locate; 55 } di_phys_t; 56 57 static void __NORETURN 58 fatal(int rv, const char *fmt, ...) 59 { 60 va_list ap; 61 62 va_start(ap, fmt); 63 (void) vfprintf(stderr, fmt, ap); 64 va_end(ap); 65 66 exit(rv); 67 } 68 69 static void 70 usage(const char *execname) 71 { 72 (void) fprintf(stderr, "Usage: %s [-Hp] [{-c|-P}]\n", execname); 73 } 74 75 static void 76 nvlist_query_string(nvlist_t *nvl, const char *label, char **val) 77 { 78 if (nvlist_lookup_string(nvl, label, val) != 0) 79 *val = "-"; 80 } 81 82 static const char * 83 display_string(const char *label) 84 { 85 return ((label) ? label : "-"); 86 } 87 88 static const char * 89 display_tristate(int val) 90 { 91 if (val == 0) 92 return ("no"); 93 if (val == 1) 94 return ("yes"); 95 96 return ("-"); 97 } 98 99 static char 100 condensed_tristate(int val, char c) 101 { 102 if (val == 0) 103 return ('-'); 104 if (val == 1) 105 return (c); 106 107 return ('?'); 108 } 109 static int 110 disk_walker(topo_hdl_t *hp, tnode_t *np, void *arg) 111 { 112 di_phys_t *pp = arg; 113 tnode_t *pnp; 114 tnode_t *ppnp; 115 topo_faclist_t fl; 116 topo_faclist_t *lp; 117 int err; 118 topo_led_state_t mode; 119 topo_led_type_t type; 120 char *name, *slotname, *serial; 121 122 if (strcmp(topo_node_name(np), DISK) != 0) 123 return (TOPO_WALK_NEXT); 124 125 if (topo_prop_get_string(np, TOPO_PGROUP_STORAGE, 126 TOPO_STORAGE_LOGICAL_DISK_NAME, &name, &err) != 0) { 127 return (TOPO_WALK_NEXT); 128 } 129 130 if (strcmp(name, pp->dp_dev) != 0) 131 return (TOPO_WALK_NEXT); 132 133 if (topo_prop_get_string(np, TOPO_PGROUP_STORAGE, 134 TOPO_STORAGE_SERIAL_NUM, &serial, &err) == 0) { 135 pp->dp_serial = serial; 136 } 137 138 pnp = topo_node_parent(np); 139 ppnp = topo_node_parent(pnp); 140 pp->dp_chassis = topo_node_instance(ppnp); 141 if (strcmp(topo_node_name(pnp), BAY) == 0) { 142 if (topo_node_facility(hp, pnp, TOPO_FAC_TYPE_INDICATOR, 143 TOPO_FAC_TYPE_ANY, &fl, &err) == 0) { 144 for (lp = topo_list_next(&fl.tf_list); lp != NULL; 145 lp = topo_list_next(lp)) { 146 uint32_t prop; 147 148 if (topo_prop_get_uint32(lp->tf_node, 149 TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE, 150 &prop, &err) != 0) { 151 continue; 152 } 153 type = (topo_led_type_t)prop; 154 155 if (topo_prop_get_uint32(lp->tf_node, 156 TOPO_PGROUP_FACILITY, TOPO_LED_MODE, 157 &prop, &err) != 0) { 158 continue; 159 } 160 mode = (topo_led_state_t)prop; 161 162 switch (type) { 163 case TOPO_LED_TYPE_SERVICE: 164 pp->dp_faulty = mode ? 1 : 0; 165 break; 166 case TOPO_LED_TYPE_LOCATE: 167 pp->dp_locate = mode ? 1 : 0; 168 break; 169 default: 170 break; 171 } 172 } 173 } 174 175 if (topo_prop_get_string(pnp, TOPO_PGROUP_PROTOCOL, 176 TOPO_PROP_LABEL, &slotname, &err) == 0) { 177 pp->dp_slotname = slotname; 178 } 179 180 pp->dp_slot = topo_node_instance(pnp); 181 } else if (strcmp(topo_node_name(pnp), USB_DEVICE) == 0) { 182 if (topo_prop_get_string(pnp, TOPO_PGROUP_PROTOCOL, 183 TOPO_PROP_LABEL, &slotname, &err) == 0) { 184 pp->dp_slotname = slotname; 185 } 186 187 /* 188 * Override dp_chassis for USB devices since they show up 189 * everywhere in the name space and may not be under a logical 190 * bay. 191 */ 192 pp->dp_chassis = -1; 193 } 194 195 return (TOPO_WALK_TERMINATE); 196 } 197 198 static void 199 populate_physical(topo_hdl_t *hp, di_phys_t *pp) 200 { 201 int err; 202 topo_walk_t *wp; 203 204 pp->dp_faulty = pp->dp_locate = -1; 205 pp->dp_chassis = pp->dp_slot = -1; 206 207 err = 0; 208 wp = topo_walk_init(hp, FM_FMRI_SCHEME_HC, disk_walker, pp, &err); 209 if (wp == NULL) { 210 fatal(-1, "unable to initialise topo walker: %s", 211 topo_strerror(err)); 212 } 213 214 while ((err = topo_walk_step(wp, TOPO_WALK_CHILD)) == TOPO_WALK_NEXT) 215 ; 216 217 if (err == TOPO_WALK_ERR) 218 fatal(-1, "topo walk failed"); 219 220 topo_walk_fini(wp); 221 } 222 223 static void 224 enumerate_disks(di_opts_t *opts) 225 { 226 topo_hdl_t *hp; 227 dm_descriptor_t *media; 228 int err, i; 229 int filter[] = { DM_DT_FIXED, -1 }; 230 dm_descriptor_t *disk, *controller; 231 nvlist_t *mattrs, *dattrs, *cattrs = NULL; 232 233 uint64_t size, total; 234 uint32_t blocksize; 235 double total_in_GiB; 236 char sizestr[32]; 237 char slotname[32]; 238 char statestr[8]; 239 240 char *vid, *pid, *opath, *c, *ctype = NULL; 241 boolean_t removable; 242 boolean_t ssd; 243 char device[MAXPATHLEN]; 244 di_phys_t phys; 245 size_t len; 246 247 err = 0; 248 if ((media = dm_get_descriptors(DM_MEDIA, filter, &err)) == NULL) { 249 fatal(-1, "failed to obtain media descriptors: %s\n", 250 strerror(err)); 251 } 252 253 err = 0; 254 hp = topo_open(TOPO_VERSION, NULL, &err); 255 if (hp == NULL) { 256 fatal(-1, "unable to obtain topo handle: %s", 257 topo_strerror(err)); 258 } 259 260 err = 0; 261 (void) topo_snap_hold(hp, NULL, &err); 262 if (err != 0) { 263 fatal(-1, "unable to hold topo snapshot: %s", 264 topo_strerror(err)); 265 } 266 267 for (i = 0; media != NULL && media[i] != NULL; i++) { 268 if ((disk = dm_get_associated_descriptors(media[i], 269 DM_DRIVE, &err)) == NULL) { 270 continue; 271 } 272 273 mattrs = dm_get_attributes(media[i], &err); 274 err = nvlist_lookup_uint64(mattrs, DM_SIZE, &size); 275 assert(err == 0); 276 err = nvlist_lookup_uint32(mattrs, DM_BLOCKSIZE, &blocksize); 277 assert(err == 0); 278 nvlist_free(mattrs); 279 280 dattrs = dm_get_attributes(disk[0], &err); 281 282 nvlist_query_string(dattrs, DM_VENDOR_ID, &vid); 283 nvlist_query_string(dattrs, DM_PRODUCT_ID, &pid); 284 nvlist_query_string(dattrs, DM_OPATH, &opath); 285 286 removable = B_FALSE; 287 if (nvlist_lookup_boolean(dattrs, DM_REMOVABLE) == 0) 288 removable = B_TRUE; 289 290 ssd = B_FALSE; 291 if (nvlist_lookup_boolean(dattrs, DM_SOLIDSTATE) == 0) 292 ssd = B_TRUE; 293 294 if ((controller = dm_get_associated_descriptors(disk[0], 295 DM_CONTROLLER, &err)) != NULL) { 296 cattrs = dm_get_attributes(controller[0], &err); 297 nvlist_query_string(cattrs, DM_CTYPE, &ctype); 298 ctype = strdup(ctype); 299 for (c = ctype; *c != '\0'; c++) 300 *c = toupper(*c); 301 } 302 303 /* 304 * Parse full device path to only show the device name, 305 * i.e. c0t1d0. Many paths will reference a particular 306 * slice (c0t1d0s0), so remove the slice if present. 307 */ 308 if ((c = strrchr(opath, '/')) != NULL) 309 (void) strlcpy(device, c + 1, sizeof (device)); 310 else 311 (void) strlcpy(device, opath, sizeof (device)); 312 len = strlen(device); 313 if (device[len - 2] == 's' && 314 (device[len - 1] >= '0' && device[len - 1] <= '9')) 315 device[len - 2] = '\0'; 316 317 bzero(&phys, sizeof (phys)); 318 phys.dp_dev = device; 319 populate_physical(hp, &phys); 320 321 /* 322 * The size is given in blocks, so multiply the number 323 * of blocks by the block size to get the total size, 324 * then convert to GiB. 325 */ 326 total = size * blocksize; 327 328 if (opts->di_parseable) { 329 (void) snprintf(sizestr, sizeof (sizestr), 330 "%llu", total); 331 } else { 332 total_in_GiB = (double)total / 333 1024.0 / 1024.0 / 1024.0; 334 (void) snprintf(sizestr, sizeof (sizestr), 335 "%7.2f GiB", total_in_GiB); 336 } 337 338 if (opts->di_parseable) { 339 (void) snprintf(slotname, sizeof (slotname), "%d,%d", 340 phys.dp_chassis, phys.dp_slot); 341 } else if (phys.dp_slotname != NULL && phys.dp_chassis != -1) { 342 (void) snprintf(slotname, sizeof (slotname), 343 "[%d] %s", phys.dp_chassis, phys.dp_slotname); 344 } else if (phys.dp_slotname != NULL) { 345 (void) snprintf(slotname, sizeof (slotname), 346 "%s", phys.dp_slotname); 347 } else { 348 slotname[0] = '-'; 349 slotname[1] = '\0'; 350 } 351 352 if (opts->di_condensed) { 353 (void) snprintf(statestr, sizeof (statestr), "%c%c%c%c", 354 condensed_tristate(phys.dp_faulty, 'F'), 355 condensed_tristate(phys.dp_locate, 'L'), 356 condensed_tristate(removable, 'R'), 357 condensed_tristate(ssd, 'S')); 358 } 359 360 if (opts->di_physical) { 361 if (opts->di_scripted) { 362 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 363 device, vid, pid, 364 display_string(phys.dp_serial), 365 display_tristate(phys.dp_faulty), 366 display_tristate(phys.dp_locate), slotname); 367 } else { 368 printf("%-22s %-8s %-16s " 369 "%-20s %-3s %-3s %s\n", 370 device, vid, pid, 371 display_string(phys.dp_serial), 372 display_tristate(phys.dp_faulty), 373 display_tristate(phys.dp_locate), slotname); 374 } 375 } else if (opts->di_condensed) { 376 if (opts->di_scripted) { 377 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 378 ctype, device, vid, pid, 379 display_string(phys.dp_serial), 380 sizestr, statestr, slotname); 381 } else { 382 printf("%-7s %-22s %-8s %-16s " 383 "%-20s\n\t%-13s %-4s %s\n", 384 ctype, device, vid, pid, 385 display_string(phys.dp_serial), 386 sizestr, statestr, slotname); 387 } 388 } else { 389 if (opts->di_scripted) { 390 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", 391 ctype, device, vid, pid, sizestr, 392 display_tristate(removable), 393 display_tristate(ssd)); 394 } else { 395 printf("%-7s %-22s %-8s %-16s " 396 "%-13s %-3s %-3s\n", ctype, device, 397 vid, pid, sizestr, 398 display_tristate(removable), 399 display_tristate(ssd)); 400 } 401 } 402 403 free(ctype); 404 nvlist_free(cattrs); 405 nvlist_free(dattrs); 406 dm_free_descriptors(controller); 407 dm_free_descriptors(disk); 408 } 409 410 dm_free_descriptors(media); 411 topo_snap_release(hp); 412 topo_close(hp); 413 } 414 415 int 416 main(int argc, char *argv[]) 417 { 418 char c; 419 420 di_opts_t opts = { 421 .di_condensed = B_FALSE, 422 .di_scripted = B_FALSE, 423 .di_physical = B_FALSE, 424 .di_parseable = B_FALSE 425 }; 426 427 while ((c = getopt(argc, argv, ":cHPp")) != EOF) { 428 switch (c) { 429 case 'c': 430 if (opts.di_physical) { 431 usage(argv[0]); 432 fatal(1, "-c and -P are mutually exclusive\n"); 433 } 434 opts.di_condensed = B_TRUE; 435 break; 436 case 'H': 437 opts.di_scripted = B_TRUE; 438 break; 439 case 'P': 440 if (opts.di_condensed) { 441 usage(argv[0]); 442 fatal(1, "-c and -P are mutually exclusive\n"); 443 } 444 opts.di_physical = B_TRUE; 445 break; 446 case 'p': 447 opts.di_parseable = B_TRUE; 448 break; 449 case '?': 450 usage(argv[0]); 451 fatal(1, "unknown option -%c\n", optopt); 452 default: 453 fatal(-1, "unexpected error on option -%c\n", optopt); 454 } 455 } 456 457 if (!opts.di_scripted) { 458 if (opts.di_physical) { 459 printf("DISK VID PID" 460 " SERIAL FLT LOC" 461 " LOCATION\n"); 462 } else if (opts.di_condensed) { 463 printf("TYPE DISK VID PID" 464 " SERIAL\n"); 465 printf("\tSIZE FLRS LOCATION\n"); 466 } else { 467 printf("TYPE DISK VID PID" 468 " SIZE RMV SSD\n"); 469 } 470 } 471 472 enumerate_disks(&opts); 473 474 return (0); 475 } 476