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 
  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         if (strcmp(topo_node_name(pnp), BAY) == 0) {
 141                 if (topo_node_facility(hp, pnp, TOPO_FAC_TYPE_INDICATOR,
 142                     TOPO_FAC_TYPE_ANY, &fl, &err) == 0) {
 143                         for (lp = topo_list_next(&fl.tf_list); lp != NULL;
 144                             lp = topo_list_next(lp)) {
 145                                 uint32_t prop;
 146 
 147                                 if (topo_prop_get_uint32(lp->tf_node,
 148                                     TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
 149                                     &prop, &err) != 0) {
 150                                         continue;
 151                                 }
 152                                 type = (topo_led_type_t)prop;
 153 
 154                                 if (topo_prop_get_uint32(lp->tf_node,
 155                                     TOPO_PGROUP_FACILITY, TOPO_LED_MODE,
 156                                     &prop, &err) != 0) {
 157                                         continue;
 158                                 }
 159                                 mode = (topo_led_state_t)prop;
 160 
 161                                 switch (type) {
 162                                 case TOPO_LED_TYPE_SERVICE:
 163                                         pp->dp_faulty = mode ? 1 : 0;
 164                                         break;
 165                                 case TOPO_LED_TYPE_LOCATE:
 166                                         pp->dp_locate = mode ? 1 : 0;
 167                                         break;
 168                                 default:
 169                                         break;
 170                                 }
 171                         }
 172                 }
 173 
 174                 if (topo_prop_get_string(pnp, TOPO_PGROUP_PROTOCOL,
 175                     TOPO_PROP_LABEL, &slotname, &err) == 0) {
 176                         pp->dp_slotname = slotname;
 177                 }
 178 
 179                 pp->dp_slot = topo_node_instance(pnp);
 180         }
 181 
 182         pp->dp_chassis = topo_node_instance(ppnp);
 183 
 184         return (TOPO_WALK_TERMINATE);
 185 }
 186 
 187 static void
 188 populate_physical(topo_hdl_t *hp, di_phys_t *pp)
 189 {
 190         int err;
 191         topo_walk_t *wp;
 192 
 193         pp->dp_faulty = pp->dp_locate = -1;
 194         pp->dp_chassis = pp->dp_slot = -1;
 195 
 196         err = 0;
 197         wp = topo_walk_init(hp, FM_FMRI_SCHEME_HC, disk_walker, pp, &err);
 198         if (wp == NULL) {
 199                 fatal(-1, "unable to initialise topo walker: %s",
 200                     topo_strerror(err));
 201         }
 202 
 203         while ((err = topo_walk_step(wp, TOPO_WALK_CHILD)) == TOPO_WALK_NEXT)
 204                 ;
 205 
 206         if (err == TOPO_WALK_ERR)
 207                 fatal(-1, "topo walk failed");
 208 
 209         topo_walk_fini(wp);
 210 }
 211 
 212 static void
 213 enumerate_disks(di_opts_t *opts)
 214 {
 215         topo_hdl_t *hp;
 216         dm_descriptor_t *media;
 217         int err, i;
 218         int filter[] = { DM_DT_FIXED, -1 };
 219         dm_descriptor_t *disk, *controller;
 220         nvlist_t *mattrs, *dattrs, *cattrs = NULL;
 221 
 222         uint64_t size, total;
 223         uint32_t blocksize;
 224         double total_in_GiB;
 225         char sizestr[32];
 226         char slotname[32];
 227         char statestr[8];
 228 
 229         char *vid, *pid, *opath, *c, *ctype = NULL;
 230         boolean_t removable;
 231         boolean_t ssd;
 232         char device[MAXPATHLEN];
 233         di_phys_t phys;
 234         size_t len;
 235 
 236         err = 0;
 237         if ((media = dm_get_descriptors(DM_MEDIA, filter, &err)) == NULL) {
 238                 fatal(-1, "failed to obtain media descriptors: %s\n",
 239                     strerror(err));
 240         }
 241 
 242         err = 0;
 243         hp = topo_open(TOPO_VERSION, NULL, &err);
 244         if (hp == NULL) {
 245                 fatal(-1, "unable to obtain topo handle: %s",
 246                     topo_strerror(err));
 247         }
 248 
 249         err = 0;
 250         (void) topo_snap_hold(hp, NULL, &err);
 251         if (err != 0) {
 252                 fatal(-1, "unable to hold topo snapshot: %s",
 253                     topo_strerror(err));
 254         }
 255 
 256         for (i = 0; media != NULL && media[i] != NULL; i++) {
 257                 if ((disk = dm_get_associated_descriptors(media[i],
 258                     DM_DRIVE, &err)) == NULL) {
 259                         continue;
 260                 }
 261 
 262                 mattrs = dm_get_attributes(media[i], &err);
 263                 err = nvlist_lookup_uint64(mattrs, DM_SIZE, &size);
 264                 assert(err == 0);
 265                 err = nvlist_lookup_uint32(mattrs, DM_BLOCKSIZE, &blocksize);
 266                 assert(err == 0);
 267                 nvlist_free(mattrs);
 268 
 269                 dattrs = dm_get_attributes(disk[0], &err);
 270 
 271                 nvlist_query_string(dattrs, DM_VENDOR_ID, &vid);
 272                 nvlist_query_string(dattrs, DM_PRODUCT_ID, &pid);
 273                 nvlist_query_string(dattrs, DM_OPATH, &opath);
 274 
 275                 removable = B_FALSE;
 276                 if (nvlist_lookup_boolean(dattrs, DM_REMOVABLE) == 0)
 277                         removable = B_TRUE;
 278 
 279                 ssd = B_FALSE;
 280                 if (nvlist_lookup_boolean(dattrs, DM_SOLIDSTATE) == 0)
 281                         ssd = B_TRUE;
 282 
 283                 if ((controller = dm_get_associated_descriptors(disk[0],
 284                     DM_CONTROLLER, &err)) != NULL) {
 285                         cattrs = dm_get_attributes(controller[0], &err);
 286                         nvlist_query_string(cattrs, DM_CTYPE, &ctype);
 287                         ctype = strdup(ctype);
 288                         for (c = ctype; *c != '\0'; c++)
 289                                 *c = toupper(*c);
 290                 }
 291 
 292                 /*
 293                  * Parse full device path to only show the device name,
 294                  * i.e. c0t1d0.  Many paths will reference a particular
 295                  * slice (c0t1d0s0), so remove the slice if present.
 296                  */
 297                 if ((c = strrchr(opath, '/')) != NULL)
 298                         (void) strlcpy(device, c + 1, sizeof (device));
 299                 else
 300                         (void) strlcpy(device, opath, sizeof (device));
 301                 len = strlen(device);
 302                 if (device[len - 2] == 's' &&
 303                     (device[len - 1] >= '0' && device[len - 1] <= '9'))
 304                         device[len - 2] = '\0';
 305 
 306                 bzero(&phys, sizeof (phys));
 307                 phys.dp_dev = device;
 308                 populate_physical(hp, &phys);
 309 
 310                 /*
 311                  * The size is given in blocks, so multiply the number
 312                  * of blocks by the block size to get the total size,
 313                  * then convert to GiB.
 314                  */
 315                 total = size * blocksize;
 316 
 317                 if (opts->di_parseable) {
 318                         (void) snprintf(sizestr, sizeof (sizestr),
 319                             "%llu", total);
 320                 } else {
 321                         total_in_GiB = (double)total /
 322                             1024.0 / 1024.0 / 1024.0;
 323                         (void) snprintf(sizestr, sizeof (sizestr),
 324                             "%7.2f GiB", total_in_GiB);
 325                 }
 326 
 327                 if (opts->di_parseable) {
 328                         (void) snprintf(slotname, sizeof (slotname), "%d,%d",
 329                             phys.dp_chassis, phys.dp_slot);
 330                 } else if (phys.dp_slotname != NULL) {
 331                         (void) snprintf(slotname, sizeof (slotname),
 332                             "[%d] %s", phys.dp_chassis, phys.dp_slotname);
 333                 } else {
 334                         slotname[0] = '-';
 335                         slotname[1] = '\0';
 336                 }
 337 
 338                 if (opts->di_condensed) {
 339                         (void) snprintf(statestr, sizeof (statestr), "%c%c%c%c",
 340                             condensed_tristate(phys.dp_faulty, 'F'),
 341                             condensed_tristate(phys.dp_locate, 'L'),
 342                             condensed_tristate(removable, 'R'),
 343                             condensed_tristate(ssd, 'S'));
 344                 }
 345 
 346                 if (opts->di_physical) {
 347                         if (opts->di_scripted) {
 348                                 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
 349                                     device, vid, pid,
 350                                     display_string(phys.dp_serial),
 351                                     display_tristate(phys.dp_faulty),
 352                                     display_tristate(phys.dp_locate), slotname);
 353                         } else {
 354                                 printf("%-22s  %-8s %-16s "
 355                                     "%-20s %-3s %-3s %s\n",
 356                                     device, vid, pid,
 357                                     display_string(phys.dp_serial),
 358                                     display_tristate(phys.dp_faulty),
 359                                     display_tristate(phys.dp_locate), slotname);
 360                         }
 361                 } else if (opts->di_condensed) {
 362                         if (opts->di_scripted) {
 363                                 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
 364                                     ctype, device, vid, pid,
 365                                     display_string(phys.dp_serial),
 366                                     sizestr, statestr, slotname);
 367                         } else {
 368                                 printf("%-7s %-22s  %-8s %-16s "
 369                                     "%-20s\n\t%-13s %-4s %s\n",
 370                                     ctype, device, vid, pid,
 371                                     display_string(phys.dp_serial),
 372                                     sizestr, statestr, slotname);
 373                         }
 374                 } else {
 375                         if (opts->di_scripted) {
 376                                 printf("%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
 377                                     ctype, device, vid, pid, sizestr,
 378                                     display_tristate(removable),
 379                                     display_tristate(ssd));
 380                         } else {
 381                                 printf("%-7s %-22s  %-8s %-16s "
 382                                     "%-13s %-3s %-3s\n", ctype, device,
 383                                     vid, pid, sizestr,
 384                                     display_tristate(removable),
 385                                     display_tristate(ssd));
 386                         }
 387                 }
 388 
 389                 free(ctype);
 390                 nvlist_free(cattrs);
 391                 nvlist_free(dattrs);
 392                 dm_free_descriptors(controller);
 393                 dm_free_descriptors(disk);
 394         }
 395 
 396         dm_free_descriptors(media);
 397         topo_snap_release(hp);
 398         topo_close(hp);
 399 }
 400 
 401 int
 402 main(int argc, char *argv[])
 403 {
 404         char c;
 405 
 406         di_opts_t opts = {
 407                 .di_condensed = B_FALSE,
 408                 .di_scripted = B_FALSE,
 409                 .di_physical = B_FALSE,
 410                 .di_parseable = B_FALSE
 411         };
 412 
 413         while ((c = getopt(argc, argv, ":cHPp")) != EOF) {
 414                 switch (c) {
 415                 case 'c':
 416                         if (opts.di_physical) {
 417                                 usage(argv[0]);
 418                                 fatal(1, "-c and -P are mutually exclusive\n");
 419                         }
 420                         opts.di_condensed = B_TRUE;
 421                         break;
 422                 case 'H':
 423                         opts.di_scripted = B_TRUE;
 424                         break;
 425                 case 'P':
 426                         if (opts.di_condensed) {
 427                                 usage(argv[0]);
 428                                 fatal(1, "-c and -P are mutually exclusive\n");
 429                         }
 430                         opts.di_physical = B_TRUE;
 431                         break;
 432                 case 'p':
 433                         opts.di_parseable = B_TRUE;
 434                         break;
 435                 case '?':
 436                         usage(argv[0]);
 437                         fatal(1, "unknown option -%c\n", optopt);
 438                 default:
 439                         fatal(-1, "unexpected error on option -%c\n", optopt);
 440                 }
 441         }
 442 
 443         if (!opts.di_scripted) {
 444                 if (opts.di_physical) {
 445                         printf("DISK                    VID      PID"
 446                             "              SERIAL               FLT LOC"
 447                             " LOCATION\n");
 448                 } else if (opts.di_condensed) {
 449                         printf("TYPE    DISK                    VID      PID"
 450                             "              SERIAL\n");
 451                         printf("\tSIZE          FLRS LOCATION\n");
 452                 } else {
 453                         printf("TYPE    DISK                    VID      PID"
 454                             "              SIZE          RMV SSD\n");
 455                 }
 456         }
 457 
 458         enumerate_disks(&opts);
 459 
 460         return (0);
 461 }