1 /*- 2 * Copyright (c) 2019 Klara Inc. 3 * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org> 4 * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org> 5 * Copyright (c) 2000 by Matthew Jacob 6 * All rights reserved. 7 * 8 * Portions of this software were developed by Edward Tomasz Napierala 9 * under sponsorship from Klara Inc. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer 16 * in this position and unchanged. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/endian.h> 37 #include <sys/param.h> 38 #include <sys/disk.h> 39 #include <sys/ioctl.h> 40 #include <sys/types.h> 41 42 #include <err.h> 43 #include <errno.h> 44 #include <fcntl.h> 45 #include <getopt.h> 46 #include <glob.h> 47 #include <stdbool.h> 48 #include <stddef.h> 49 #include <stdint.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <libxo/xo.h> 55 56 #include <cam/scsi/scsi_enc.h> 57 58 #include "eltsub.h" 59 60 #define SESUTIL_XO_VERSION "1" 61 62 #define TEMPERATURE_OFFSET 20 63 64 #define PRINT_STYLE_DASHED 0 65 #define PRINT_STYLE_DASHED_2 1 66 #define PRINT_STYLE_CSV 2 67 #define PRINT_STYLE_CSV_2 3 68 69 static int encstatus(int argc, char **argv); 70 static int fault(int argc, char **argv); 71 static int locate(int argc, char **argv); 72 static int objmap(int argc, char **argv); 73 static int sesled(int argc, char **argv, bool fault); 74 static int show(int argc, char **argv); 75 static void sesutil_print(int *style, const char *fmt, ...) __printflike(2,3); 76 77 static struct command { 78 const char *name; 79 const char *param; 80 const char *desc; 81 int (*exec)(int argc, char **argv); 82 } cmds[] = { 83 { "fault", 84 "(<disk>|<sesid>|all) (on|off)", 85 "Change the state of the fault LED associated with a disk", 86 fault }, 87 { "locate", 88 "(<disk>|<sesid>|all) (on|off)", 89 "Change the state of the locate LED associated with a disk", 90 locate }, 91 { "map", "", 92 "Print a map of the devices managed by the enclosure", objmap } , 93 { "show", "", 94 "Print a human-friendly summary of the enclosure", show } , 95 { "status", "", "Print the status of the enclosure", 96 encstatus }, 97 }; 98 99 static const int nbcmds = nitems(cmds); 100 static const char *uflag; 101 102 static void 103 usage(FILE *out, const char *subcmd) 104 { 105 int i; 106 107 if (subcmd == NULL) { 108 fprintf(out, "Usage: %s [-u /dev/ses<N>] <command> [options]\n", 109 getprogname()); 110 fprintf(out, "Commands supported:\n"); 111 } 112 for (i = 0; i < nbcmds; i++) { 113 if (subcmd != NULL) { 114 if (strcmp(subcmd, cmds[i].name) == 0) { 115 fprintf(out, "Usage: %s %s [-u /dev/ses<N>] " 116 "%s\n\t%s\n", getprogname(), subcmd, 117 cmds[i].param, cmds[i].desc); 118 break; 119 } 120 continue; 121 } 122 fprintf(out, " %-12s%s\n\t\t%s\n\n", cmds[i].name, 123 cmds[i].param, cmds[i].desc); 124 } 125 126 exit(EXIT_FAILURE); 127 } 128 129 static void 130 do_led(int fd, unsigned int idx, elm_type_t type, bool onoff, bool setfault) 131 { 132 int state = onoff ? 1 : 0; 133 encioc_elm_status_t o; 134 struct ses_ctrl_dev_slot *slot; 135 136 o.elm_idx = idx; 137 if (ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t) &o) < 0) { 138 close(fd); 139 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 140 } 141 ses_status_to_ctrl(type, &o.cstat[0]); 142 switch (type) { 143 case ELMTYP_DEVICE: 144 case ELMTYP_ARRAY_DEV: 145 slot = (struct ses_ctrl_dev_slot *) &o.cstat[0]; 146 ses_ctrl_common_set_select(&slot->common, 1); 147 if (setfault) 148 ses_ctrl_dev_slot_set_rqst_fault(slot, state); 149 else 150 ses_ctrl_dev_slot_set_rqst_ident(slot, state); 151 break; 152 default: 153 return; 154 } 155 if (ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t) &o) < 0) { 156 close(fd); 157 xo_err(EXIT_FAILURE, "ENCIOC_SETELMSTAT"); 158 } 159 } 160 161 static bool 162 disk_match(const char *devnames, const char *disk, size_t len) 163 { 164 const char *dname; 165 166 dname = devnames; 167 while ((dname = strstr(dname, disk)) != NULL) { 168 if (dname[len] == '\0' || dname[len] == ',') { 169 return (true); 170 } 171 dname++; 172 } 173 174 return (false); 175 } 176 177 static int 178 sesled(int argc, char **argv, bool setfault) 179 { 180 encioc_elm_devnames_t objdn; 181 encioc_element_t *objp; 182 glob_t g; 183 char *disk, *endptr; 184 size_t len, i, ndisks; 185 int fd; 186 unsigned int nobj, j, sesid; 187 bool all, isses, onoff; 188 189 isses = false; 190 all = false; 191 onoff = false; 192 193 if (argc != 3) { 194 usage(stderr, (setfault ? "fault" : "locate")); 195 } 196 197 disk = argv[1]; 198 199 sesid = strtoul(disk, &endptr, 10); 200 if (*endptr == '\0') { 201 endptr = strrchr(uflag, '*'); 202 if (endptr != NULL && *endptr == '*') { 203 xo_warnx("Must specifying a SES device (-u) to use a SES " 204 "id# to identify a disk"); 205 usage(stderr, (setfault ? "fault" : "locate")); 206 } 207 isses = true; 208 } 209 210 if (strcmp(argv[2], "on") == 0) { 211 onoff = true; 212 } else if (strcmp(argv[2], "off") == 0) { 213 onoff = false; 214 } else { 215 usage(stderr, (setfault ? "fault" : "locate")); 216 } 217 218 if (strcmp(disk, "all") == 0) { 219 all = true; 220 } 221 len = strlen(disk); 222 223 /* Get the list of ses devices */ 224 if (glob((uflag != NULL ? uflag : "/dev/ses[0-9]*"), 0, NULL, &g) == 225 GLOB_NOMATCH) { 226 globfree(&g); 227 xo_errx(EXIT_FAILURE, "No SES devices found"); 228 } 229 230 ndisks = 0; 231 for (i = 0; i < g.gl_pathc; i++) { 232 /* ensure we only got numbers after ses */ 233 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 234 strlen(g.gl_pathv[i] + 8)) { 235 continue; 236 } 237 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 238 /* 239 * Don't treat non-access errors as critical if we are 240 * accessing all devices 241 */ 242 if (errno == EACCES && g.gl_pathc > 1) { 243 xo_err(EXIT_FAILURE, "unable to access SES device"); 244 } 245 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 246 continue; 247 } 248 249 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 250 close(fd); 251 xo_err(EXIT_FAILURE, "ENCIOC_GETNELM"); 252 } 253 254 objp = calloc(nobj, sizeof(encioc_element_t)); 255 if (objp == NULL) { 256 close(fd); 257 xo_err(EXIT_FAILURE, "calloc()"); 258 } 259 260 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) objp) < 0) { 261 free(objp); 262 close(fd); 263 xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 264 } 265 266 if (isses) { 267 if (sesid >= nobj) { 268 free(objp); 269 close(fd); 270 xo_errx(EXIT_FAILURE, 271 "Requested SES ID does not exist"); 272 } 273 do_led(fd, sesid, objp[sesid].elm_type, onoff, setfault); 274 ndisks++; 275 free(objp); 276 close(fd); 277 break; 278 } 279 for (j = 0; j < nobj; j++) { 280 const int devnames_size = 128; 281 char devnames[devnames_size]; 282 283 if (all) { 284 do_led(fd, objp[j].elm_idx, objp[j].elm_type, 285 onoff, setfault); 286 continue; 287 } 288 memset(&objdn, 0, sizeof(objdn)); 289 memset(devnames, 0, devnames_size); 290 objdn.elm_idx = objp[j].elm_idx; 291 objdn.elm_names_size = devnames_size; 292 objdn.elm_devnames = devnames; 293 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 294 (caddr_t) &objdn) <0) { 295 continue; 296 } 297 if (objdn.elm_names_len > 0) { 298 if (disk_match(objdn.elm_devnames, disk, len)) { 299 do_led(fd, objdn.elm_idx, objp[j].elm_type, 300 onoff, setfault); 301 ndisks++; 302 break; 303 } 304 } 305 } 306 free(objp); 307 close(fd); 308 } 309 globfree(&g); 310 if (ndisks == 0 && all == false) { 311 xo_errx(EXIT_FAILURE, "Count not find the SES id of device '%s'", 312 disk); 313 } 314 315 return (EXIT_SUCCESS); 316 } 317 318 static int 319 locate(int argc, char **argv) 320 { 321 322 return (sesled(argc, argv, false)); 323 } 324 325 static int 326 fault(int argc, char **argv) 327 { 328 329 return (sesled(argc, argv, true)); 330 } 331 332 static void 333 sesutil_print(int *style, const char *fmt, ...) 334 { 335 va_list args; 336 337 if (*style == PRINT_STYLE_DASHED) { 338 xo_open_container("extra_status"); 339 xo_emit("\t\tExtra status:\n"); 340 *style = PRINT_STYLE_DASHED_2; 341 } else if (*style == PRINT_STYLE_CSV) { 342 xo_open_container("extra_status"); 343 *style = PRINT_STYLE_CSV_2; 344 } 345 346 if (*style == PRINT_STYLE_DASHED_2) 347 xo_emit("\t\t- "); 348 else if (*style == PRINT_STYLE_CSV_2) 349 xo_emit(", "); 350 va_start(args, fmt); 351 xo_emit_hv(NULL, fmt, args); 352 va_end(args); 353 if (*style == PRINT_STYLE_DASHED_2) 354 xo_emit("\n"); 355 } 356 357 static void 358 print_extra_status(int eletype, u_char *cstat, int style) 359 { 360 361 if (cstat[0] & 0x40) { 362 sesutil_print(&style, "{e:predicted_failure/true} Predicted Failure"); 363 } 364 if (cstat[0] & 0x20) { 365 sesutil_print(&style, "{e:disabled/true} Disabled"); 366 } 367 if (cstat[0] & 0x10) { 368 sesutil_print(&style, "{e:swapped/true} Swapped"); 369 } 370 switch (eletype) { 371 case ELMTYP_DEVICE: 372 case ELMTYP_ARRAY_DEV: 373 if (cstat[2] & 0x02) { 374 sesutil_print(&style, "LED={q:led/locate}"); 375 } 376 if (cstat[2] & 0x20) { 377 sesutil_print(&style, "LED={q:led/fault}"); 378 } 379 break; 380 case ELMTYP_FAN: 381 sesutil_print(&style, "Speed: {:speed/%d}{Uw:rpm}", 382 (((0x7 & cstat[1]) << 8) + cstat[2]) * 10); 383 break; 384 case ELMTYP_THERM: 385 if (cstat[2]) { 386 sesutil_print(&style, "Temperature: {:temperature/%d}{Uw:C}", 387 cstat[2] - TEMPERATURE_OFFSET); 388 } else { 389 sesutil_print(&style, "Temperature: -{q:temperature/reserved}"); 390 } 391 break; 392 case ELMTYP_VOM: 393 sesutil_print(&style, "Voltage: {:voltage/%.2f}{Uw:V}", 394 be16dec(cstat + 2) / 100.0); 395 break; 396 } 397 if (style) { 398 xo_close_container("extra_status"); 399 } 400 } 401 402 static int 403 objmap(int argc, char **argv __unused) 404 { 405 encioc_string_t stri; 406 encioc_elm_devnames_t e_devname; 407 encioc_elm_status_t e_status; 408 encioc_elm_desc_t e_desc; 409 encioc_element_t *e_ptr; 410 glob_t g; 411 int fd; 412 unsigned int j, nobj; 413 size_t i; 414 char str[32]; 415 416 if (argc != 1) { 417 usage(stderr, "map"); 418 } 419 420 /* Get the list of ses devices */ 421 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 422 globfree(&g); 423 xo_errx(EXIT_FAILURE, "No SES devices found"); 424 } 425 xo_set_version(SESUTIL_XO_VERSION); 426 xo_open_container("sesutil"); 427 xo_open_list("enclosures"); 428 for (i = 0; i < g.gl_pathc; i++) { 429 /* ensure we only got numbers after ses */ 430 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 431 strlen(g.gl_pathv[i] + 8)) { 432 continue; 433 } 434 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 435 /* 436 * Don't treat non-access errors as critical if we are 437 * accessing all devices 438 */ 439 if (errno == EACCES && g.gl_pathc > 1) { 440 xo_err(EXIT_FAILURE, "unable to access SES device"); 441 } 442 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 443 continue; 444 } 445 446 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 447 close(fd); 448 xo_err(EXIT_FAILURE, "ENCIOC_GETNELM"); 449 } 450 451 e_ptr = calloc(nobj, sizeof(encioc_element_t)); 452 if (e_ptr == NULL) { 453 close(fd); 454 xo_err(EXIT_FAILURE, "calloc()"); 455 } 456 457 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) { 458 close(fd); 459 xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 460 } 461 462 xo_open_instance("enclosures"); 463 xo_emit("{t:enc/%s}:\n", g.gl_pathv[i] + 5); 464 stri.bufsiz = sizeof(str); 465 stri.buf = &str[0]; 466 if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0) 467 xo_emit("\tEnclosure Name: {t:name/%s}\n", stri.buf); 468 stri.bufsiz = sizeof(str); 469 stri.buf = &str[0]; 470 if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0) 471 xo_emit("\tEnclosure ID: {t:id/%s}\n", stri.buf); 472 473 xo_open_list("elements"); 474 for (j = 0; j < nobj; j++) { 475 /* Get the status of the element */ 476 memset(&e_status, 0, sizeof(e_status)); 477 e_status.elm_idx = e_ptr[j].elm_idx; 478 if (ioctl(fd, ENCIOC_GETELMSTAT, 479 (caddr_t) &e_status) < 0) { 480 close(fd); 481 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 482 } 483 /* Get the description of the element */ 484 memset(&e_desc, 0, sizeof(e_desc)); 485 e_desc.elm_idx = e_ptr[j].elm_idx; 486 e_desc.elm_desc_len = UINT16_MAX; 487 /* XXX memory leak! */ 488 e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char)); 489 if (e_desc.elm_desc_str == NULL) { 490 close(fd); 491 xo_err(EXIT_FAILURE, "calloc()"); 492 } 493 if (ioctl(fd, ENCIOC_GETELMDESC, 494 (caddr_t) &e_desc) < 0) { 495 close(fd); 496 xo_err(EXIT_FAILURE, "ENCIOC_GETELMDESC"); 497 } 498 /* Get the device name(s) of the element */ 499 memset(&e_devname, 0, sizeof(e_devname)); 500 e_devname.elm_idx = e_ptr[j].elm_idx; 501 e_devname.elm_names_size = 128; 502 e_devname.elm_devnames = calloc(128, sizeof(char)); 503 if (e_devname.elm_devnames == NULL) { 504 close(fd); 505 xo_err(EXIT_FAILURE, "calloc()"); 506 } 507 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 508 (caddr_t) &e_devname) <0) { 509 /* Continue even if we can't look up devnames */ 510 e_devname.elm_devnames[0] = '\0'; 511 } 512 xo_open_instance("elements"); 513 xo_emit("\tElement {:id/%u}, Type: {:type/%s}\n", e_ptr[j].elm_idx, 514 geteltnm(e_ptr[j].elm_type)); 515 xo_emit("\t\tStatus: {:status/%s} ({q:status_code/0x%02x 0x%02x 0x%02x 0x%02x})\n", 516 scode2ascii(e_status.cstat[0]), e_status.cstat[0], 517 e_status.cstat[1], e_status.cstat[2], 518 e_status.cstat[3]); 519 if (e_desc.elm_desc_len > 0) { 520 xo_emit("\t\tDescription: {:description/%s}\n", 521 e_desc.elm_desc_str); 522 } 523 if (e_devname.elm_names_len > 0) { 524 xo_emit("\t\tDevice Names: {:device_names/%s}\n", 525 e_devname.elm_devnames); 526 } 527 print_extra_status(e_ptr[j].elm_type, e_status.cstat, PRINT_STYLE_DASHED); 528 xo_close_instance("elements"); 529 free(e_devname.elm_devnames); 530 } 531 xo_close_list("elements"); 532 free(e_ptr); 533 close(fd); 534 } 535 globfree(&g); 536 xo_close_list("enclosures"); 537 xo_close_container("sesutil"); 538 xo_finish(); 539 540 return (EXIT_SUCCESS); 541 } 542 543 /* 544 * Get rid of the 'passN' devices, unless there's nothing else to show. 545 */ 546 static void 547 skip_pass_devices(char *devnames, size_t devnameslen) 548 { 549 char *dev, devs[128], passes[128], *tmp; 550 551 devs[0] = passes[0] = '\0'; 552 tmp = devnames; 553 554 while ((dev = strsep(&tmp, ",")) != NULL) { 555 if (strncmp(dev, "pass", 4) == 0) { 556 if (passes[0] != '\0') 557 strlcat(passes, ",", sizeof(passes)); 558 strlcat(passes, dev, sizeof(passes)); 559 } else { 560 if (devs[0] != '\0') 561 strlcat(devs, ",", sizeof(devs)); 562 strlcat(devs, dev, sizeof(devs)); 563 } 564 } 565 strlcpy(devnames, devs, devnameslen); 566 if (devnames[0] == '\0') 567 strlcpy(devnames, passes, devnameslen); 568 } 569 570 static void 571 fetch_device_details(char *devnames, char **model, char **serial, off_t *size) 572 { 573 char ident[DISK_IDENT_SIZE]; 574 struct diocgattr_arg arg; 575 char *tmp; 576 off_t mediasize; 577 int comma; 578 int fd; 579 580 comma = (int)strcspn(devnames, ","); 581 asprintf(&tmp, "/dev/%.*s", comma, devnames); 582 if (tmp == NULL) 583 err(1, "asprintf"); 584 fd = open(tmp, O_RDONLY); 585 free(tmp); 586 if (fd < 0) { 587 /* 588 * This can happen with a disk so broken it cannot 589 * be probed by GEOM. 590 */ 591 *model = strdup("?"); 592 *serial = strdup("?"); 593 *size = -1; 594 close(fd); 595 return; 596 } 597 598 strlcpy(arg.name, "GEOM::descr", sizeof(arg.name)); 599 arg.len = sizeof(arg.value.str); 600 if (ioctl(fd, DIOCGATTR, &arg) == 0) 601 *model = strdup(arg.value.str); 602 else 603 *model = NULL; 604 605 if (ioctl(fd, DIOCGIDENT, ident) == 0) 606 *serial = strdup(ident); 607 else 608 *serial = NULL; 609 610 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0) 611 *size = mediasize; 612 else 613 *size = -1; 614 close(fd); 615 } 616 617 static void 618 show_device(int fd, int elm_idx, encioc_elm_status_t e_status, encioc_elm_desc_t e_desc) 619 { 620 encioc_elm_devnames_t e_devname; 621 char *model, *serial; 622 off_t size; 623 624 /* Get the device name(s) of the element */ 625 memset(&e_devname, 0, sizeof(e_devname)); 626 e_devname.elm_idx = elm_idx; 627 e_devname.elm_names_size = 128; 628 e_devname.elm_devnames = calloc(128, sizeof(char)); 629 if (e_devname.elm_devnames == NULL) { 630 close(fd); 631 xo_err(EXIT_FAILURE, "calloc()"); 632 } 633 634 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 635 (caddr_t) &e_devname) < 0) { 636 /* We don't care if this fails */ 637 e_devname.elm_devnames[0] = '\0'; 638 model = NULL; 639 serial = NULL; 640 size = -1; 641 } else { 642 skip_pass_devices(e_devname.elm_devnames, 128); 643 fetch_device_details(e_devname.elm_devnames, &model, &serial, &size); 644 } 645 xo_open_instance("elements"); 646 xo_emit("{e:type/device_slot}"); 647 xo_emit("{d:description/%-15s} ", e_desc.elm_desc_len > 0 ? e_desc.elm_desc_str : "-"); 648 xo_emit("{e:description/%-15s}", e_desc.elm_desc_len > 0 ? e_desc.elm_desc_str : ""); 649 xo_emit("{d:device_names/%-7s} ", e_devname.elm_names_len > 0 ? e_devname.elm_devnames : "-"); 650 xo_emit("{e:device_names/%s}", e_devname.elm_names_len > 0 ? e_devname.elm_devnames : ""); 651 xo_emit("{d:model/%-25s} ", model ? model : "-"); 652 xo_emit("{e:model/%s}", model ? model : ""); 653 xo_emit("{d:serial/%-20s} ", serial != NULL ? serial : "-"); 654 xo_emit("{e:serial/%s}", serial != NULL ? serial : ""); 655 if ((e_status.cstat[0] & 0xf) == SES_OBJSTAT_OK && size >= 0) { 656 xo_emit("{h,hn-1000:size/%ld}{e:status/%s}", 657 size, scode2ascii(e_status.cstat[0])); 658 } else { 659 xo_emit("{:status/%s}", scode2ascii(e_status.cstat[0])); 660 } 661 print_extra_status(ELMTYP_ARRAY_DEV, e_status.cstat, PRINT_STYLE_CSV); 662 xo_emit("\n"); 663 xo_close_instance("elements"); 664 free(e_devname.elm_devnames); 665 } 666 667 static void 668 show_therm(encioc_elm_status_t e_status, encioc_elm_desc_t e_desc) 669 { 670 671 if (e_desc.elm_desc_len <= 0) { 672 /* We don't have a label to display; might as well skip it. */ 673 return; 674 } 675 676 if (e_status.cstat[2] == 0) { 677 /* No temperature to show. */ 678 return; 679 } 680 681 xo_open_instance("elements"); 682 xo_emit("{e:type/temperature_sensor}"); 683 xo_emit("{:description/%s}: {:temperature/%d}{Uw:C}", 684 e_desc.elm_desc_str, e_status.cstat[2] - TEMPERATURE_OFFSET); 685 xo_close_instance("elements"); 686 } 687 688 static void 689 show_vom(encioc_elm_status_t e_status, encioc_elm_desc_t e_desc) 690 { 691 692 if (e_desc.elm_desc_len <= 0) { 693 /* We don't have a label to display; might as well skip it. */ 694 return; 695 } 696 697 if (e_status.cstat[2] == 0) { 698 /* No voltage to show. */ 699 return; 700 } 701 702 xo_open_instance("elements"); 703 xo_emit("{e:type/voltage_sensor}"); 704 xo_emit("{:description/%s}: {:voltage/%.2f}{Uw:V}", 705 e_desc.elm_desc_str, be16dec(e_status.cstat + 2) / 100.0); 706 xo_close_instance("elements"); 707 } 708 709 static int 710 show(int argc, char **argv __unused) 711 { 712 encioc_string_t stri; 713 encioc_elm_status_t e_status; 714 encioc_elm_desc_t e_desc; 715 encioc_element_t *e_ptr; 716 glob_t g; 717 elm_type_t prev_type; 718 int fd; 719 unsigned int j, nobj; 720 size_t i; 721 bool first_ses; 722 char str[32]; 723 724 if (argc != 1) { 725 usage(stderr, "map"); 726 } 727 728 first_ses = true; 729 730 /* Get the list of ses devices */ 731 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 732 globfree(&g); 733 xo_errx(EXIT_FAILURE, "No SES devices found"); 734 } 735 xo_set_version(SESUTIL_XO_VERSION); 736 xo_open_container("sesutil"); 737 xo_open_list("enclosures"); 738 for (i = 0; i < g.gl_pathc; i++) { 739 /* ensure we only got numbers after ses */ 740 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 741 strlen(g.gl_pathv[i] + 8)) { 742 continue; 743 } 744 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 745 /* 746 * Don't treat non-access errors as critical if we are 747 * accessing all devices 748 */ 749 if (errno == EACCES && g.gl_pathc > 1) { 750 xo_err(EXIT_FAILURE, "unable to access SES device"); 751 } 752 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 753 continue; 754 } 755 756 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 757 close(fd); 758 xo_err(EXIT_FAILURE, "ENCIOC_GETNELM"); 759 } 760 761 e_ptr = calloc(nobj, sizeof(encioc_element_t)); 762 if (e_ptr == NULL) { 763 close(fd); 764 xo_err(EXIT_FAILURE, "calloc()"); 765 } 766 767 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) { 768 close(fd); 769 xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 770 } 771 772 xo_open_instance("enclosures"); 773 774 if (first_ses) 775 first_ses = false; 776 else 777 xo_emit("\n"); 778 779 xo_emit("{t:enc/%s}: ", g.gl_pathv[i] + 5); 780 stri.bufsiz = sizeof(str); 781 stri.buf = &str[0]; 782 if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0) 783 xo_emit("<{t:name/%s}>; ", stri.buf); 784 stri.bufsiz = sizeof(str); 785 stri.buf = &str[0]; 786 if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0) 787 xo_emit("ID: {t:id/%s}", stri.buf); 788 xo_emit("\n"); 789 790 xo_open_list("elements"); 791 prev_type = -1; 792 for (j = 0; j < nobj; j++) { 793 /* Get the status of the element */ 794 memset(&e_status, 0, sizeof(e_status)); 795 e_status.elm_idx = e_ptr[j].elm_idx; 796 if (ioctl(fd, ENCIOC_GETELMSTAT, 797 (caddr_t) &e_status) < 0) { 798 close(fd); 799 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 800 } 801 802 /* 803 * Skip "Unsupported" elements; those usually precede 804 * the actual device entries and are not particularly 805 * interesting. 806 */ 807 if (e_status.cstat[0] == SES_OBJSTAT_UNSUPPORTED) 808 continue; 809 810 /* Get the description of the element */ 811 memset(&e_desc, 0, sizeof(e_desc)); 812 e_desc.elm_idx = e_ptr[j].elm_idx; 813 e_desc.elm_desc_len = UINT16_MAX; 814 e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char)); 815 if (e_desc.elm_desc_str == NULL) { 816 close(fd); 817 xo_err(EXIT_FAILURE, "calloc()"); 818 } 819 if (ioctl(fd, ENCIOC_GETELMDESC, 820 (caddr_t) &e_desc) < 0) { 821 close(fd); 822 xo_err(EXIT_FAILURE, "ENCIOC_GETELMDESC"); 823 } 824 825 switch (e_ptr[j].elm_type) { 826 case ELMTYP_DEVICE: 827 case ELMTYP_ARRAY_DEV: 828 if (e_ptr[j].elm_type != prev_type) 829 xo_emit("Desc Dev Model Ident Size/Status\n"); 830 831 show_device(fd, e_ptr[j].elm_idx, e_status, e_desc); 832 prev_type = e_ptr[j].elm_type; 833 break; 834 case ELMTYP_THERM: 835 if (e_ptr[j].elm_type != prev_type) 836 xo_emit("\nTemperatures: "); 837 else 838 xo_emit(", "); 839 prev_type = e_ptr[j].elm_type; 840 show_therm(e_status, e_desc); 841 break; 842 case ELMTYP_VOM: 843 if (e_ptr[j].elm_type != prev_type) 844 xo_emit("\nVoltages: "); 845 else 846 xo_emit(", "); 847 prev_type = e_ptr[j].elm_type; 848 show_vom(e_status, e_desc); 849 break; 850 default: 851 /* 852 * Ignore stuff not interesting to the user. 853 */ 854 break; 855 } 856 } 857 if (prev_type != (elm_type_t)-1 && 858 prev_type != ELMTYP_DEVICE && prev_type != ELMTYP_ARRAY_DEV) 859 xo_emit("\n"); 860 xo_close_list("elements"); 861 free(e_ptr); 862 close(fd); 863 } 864 globfree(&g); 865 xo_close_list("enclosures"); 866 xo_close_container("sesutil"); 867 xo_finish(); 868 869 return (EXIT_SUCCESS); 870 } 871 872 static int 873 encstatus(int argc, char **argv __unused) 874 { 875 glob_t g; 876 int fd, status; 877 size_t i, e; 878 u_char estat; 879 880 status = 0; 881 if (argc != 1) { 882 usage(stderr, "status"); 883 } 884 885 /* Get the list of ses devices */ 886 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 887 globfree(&g); 888 xo_errx(EXIT_FAILURE, "No SES devices found"); 889 } 890 891 xo_set_version(SESUTIL_XO_VERSION); 892 xo_open_container("sesutil"); 893 xo_open_list("enclosures"); 894 for (i = 0; i < g.gl_pathc; i++) { 895 /* ensure we only got numbers after ses */ 896 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 897 strlen(g.gl_pathv[i] + 8)) { 898 continue; 899 } 900 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 901 /* 902 * Don't treat non-access errors as critical if we are 903 * accessing all devices 904 */ 905 if (errno == EACCES && g.gl_pathc > 1) { 906 xo_err(EXIT_FAILURE, "unable to access SES device"); 907 } 908 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 909 continue; 910 } 911 912 if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat) < 0) { 913 xo_err(EXIT_FAILURE, "ENCIOC_GETENCSTAT"); 914 close(fd); 915 } 916 917 xo_open_instance("enclosures"); 918 xo_emit("{:enc/%s}: ", g.gl_pathv[i] + 5); 919 e = 0; 920 if (estat == 0) { 921 if (status == 0) { 922 status = 1; 923 } 924 xo_emit("{q:status/OK}"); 925 } else { 926 if (estat & SES_ENCSTAT_INFO) { 927 xo_emit("{lq:status/INFO}"); 928 e++; 929 } 930 if (estat & SES_ENCSTAT_NONCRITICAL) { 931 if (e) 932 xo_emit(","); 933 xo_emit("{lq:status/NONCRITICAL}"); 934 e++; 935 } 936 if (estat & SES_ENCSTAT_CRITICAL) { 937 if (e) 938 xo_emit(","); 939 xo_emit("{lq:status/CRITICAL}"); 940 e++; 941 status = -1; 942 } 943 if (estat & SES_ENCSTAT_UNRECOV) { 944 if (e) 945 xo_emit(","); 946 xo_emit("{lq:status/UNRECOV}"); 947 e++; 948 status = -1; 949 } 950 } 951 xo_close_instance("enclosures"); 952 xo_emit("\n"); 953 close(fd); 954 } 955 globfree(&g); 956 957 xo_close_list("enclosures"); 958 xo_close_container("sesutil"); 959 xo_finish(); 960 961 if (status == 1) { 962 return (EXIT_SUCCESS); 963 } else { 964 return (EXIT_FAILURE); 965 } 966 } 967 968 int 969 main(int argc, char **argv) 970 { 971 int i, ch; 972 struct command *cmd = NULL; 973 974 argc = xo_parse_args(argc, argv); 975 if (argc < 0) 976 exit(1); 977 978 uflag = "/dev/ses[0-9]*"; 979 while ((ch = getopt_long(argc, argv, "u:", NULL, NULL)) != -1) { 980 switch (ch) { 981 case 'u': 982 uflag = optarg; 983 break; 984 case '?': 985 default: 986 usage(stderr, NULL); 987 } 988 } 989 argc -= optind; 990 argv += optind; 991 992 if (argc < 1) { 993 warnx("Missing command"); 994 usage(stderr, NULL); 995 } 996 997 for (i = 0; i < nbcmds; i++) { 998 if (strcmp(argv[0], cmds[i].name) == 0) { 999 cmd = &cmds[i]; 1000 break; 1001 } 1002 } 1003 1004 if (cmd == NULL) { 1005 warnx("unknown command %s", argv[0]); 1006 usage(stderr, NULL); 1007 } 1008 1009 return (cmd->exec(argc, argv)); 1010 } 1011