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 encioc_elm_status_t es; 285 memset(&es, 0, sizeof(es)); 286 es.elm_idx = objp[j].elm_idx; 287 if (ioctl(fd, ENCIOC_GETELMSTAT, &es) < 0) { 288 close(fd); 289 xo_err(EXIT_FAILURE, 290 "ENCIOC_GETELMSTAT"); 291 } 292 if ((es.cstat[0] & 0xf) == SES_OBJSTAT_NOACCESS) 293 continue; 294 do_led(fd, objp[j].elm_idx, objp[j].elm_type, 295 onoff, setfault); 296 continue; 297 } 298 memset(&objdn, 0, sizeof(objdn)); 299 memset(devnames, 0, devnames_size); 300 objdn.elm_idx = objp[j].elm_idx; 301 objdn.elm_names_size = devnames_size; 302 objdn.elm_devnames = devnames; 303 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 304 (caddr_t) &objdn) <0) { 305 continue; 306 } 307 if (objdn.elm_names_len > 0) { 308 if (disk_match(objdn.elm_devnames, disk, len)) { 309 do_led(fd, objdn.elm_idx, objp[j].elm_type, 310 onoff, setfault); 311 ndisks++; 312 break; 313 } 314 } 315 } 316 free(objp); 317 close(fd); 318 } 319 globfree(&g); 320 if (ndisks == 0 && all == false) { 321 xo_errx(EXIT_FAILURE, "Count not find the SES id of device '%s'", 322 disk); 323 } 324 325 return (EXIT_SUCCESS); 326 } 327 328 static int 329 locate(int argc, char **argv) 330 { 331 332 return (sesled(argc, argv, false)); 333 } 334 335 static int 336 fault(int argc, char **argv) 337 { 338 339 return (sesled(argc, argv, true)); 340 } 341 342 static void 343 sesutil_print(int *style, const char *fmt, ...) 344 { 345 va_list args; 346 347 if (*style == PRINT_STYLE_DASHED) { 348 xo_open_container("extra_status"); 349 xo_emit("\t\tExtra status:\n"); 350 *style = PRINT_STYLE_DASHED_2; 351 } else if (*style == PRINT_STYLE_CSV) { 352 xo_open_container("extra_status"); 353 *style = PRINT_STYLE_CSV_2; 354 } 355 356 if (*style == PRINT_STYLE_DASHED_2) 357 xo_emit("\t\t- "); 358 else if (*style == PRINT_STYLE_CSV_2) 359 xo_emit(", "); 360 va_start(args, fmt); 361 xo_emit_hv(NULL, fmt, args); 362 va_end(args); 363 if (*style == PRINT_STYLE_DASHED_2) 364 xo_emit("\n"); 365 } 366 367 static void 368 print_extra_status(int eletype, u_char *cstat, int style) 369 { 370 371 if (cstat[0] & 0x40) { 372 sesutil_print(&style, "{e:predicted_failure/true} Predicted Failure"); 373 } 374 if (cstat[0] & 0x20) { 375 sesutil_print(&style, "{e:disabled/true} Disabled"); 376 } 377 if (cstat[0] & 0x10) { 378 sesutil_print(&style, "{e:swapped/true} Swapped"); 379 } 380 switch (eletype) { 381 case ELMTYP_DEVICE: 382 case ELMTYP_ARRAY_DEV: 383 if (cstat[2] & 0x02) { 384 sesutil_print(&style, "LED={q:led/locate}"); 385 } 386 if (cstat[2] & 0x20) { 387 sesutil_print(&style, "LED={q:led/fault}"); 388 } 389 break; 390 case ELMTYP_FAN: 391 sesutil_print(&style, "Speed: {:speed/%d}{Uw:rpm}", 392 (((0x7 & cstat[1]) << 8) + cstat[2]) * 10); 393 break; 394 case ELMTYP_THERM: 395 if (cstat[2]) { 396 sesutil_print(&style, "Temperature: {:temperature/%d}{Uw:C}", 397 cstat[2] - TEMPERATURE_OFFSET); 398 } else { 399 sesutil_print(&style, "Temperature: -{q:temperature/reserved}"); 400 } 401 break; 402 case ELMTYP_VOM: 403 sesutil_print(&style, "Voltage: {:voltage/%.2f}{Uw:V}", 404 be16dec(cstat + 2) / 100.0); 405 break; 406 } 407 if (style) { 408 xo_close_container("extra_status"); 409 } 410 } 411 412 static int 413 objmap(int argc, char **argv __unused) 414 { 415 encioc_string_t stri; 416 encioc_elm_devnames_t e_devname; 417 encioc_elm_status_t e_status; 418 encioc_elm_desc_t e_desc; 419 encioc_element_t *e_ptr; 420 glob_t g; 421 int fd; 422 unsigned int j, nobj; 423 size_t i; 424 char str[32]; 425 426 if (argc != 1) { 427 usage(stderr, "map"); 428 } 429 430 memset(&e_desc, 0, sizeof(e_desc)); 431 /* SES4r02 allows element descriptors of up to 65536 characters */ 432 e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char)); 433 if (e_desc.elm_desc_str == NULL) 434 xo_err(EXIT_FAILURE, "calloc()"); 435 436 e_devname.elm_devnames = calloc(128, sizeof(char)); 437 if (e_devname.elm_devnames == NULL) 438 xo_err(EXIT_FAILURE, "calloc()"); 439 e_devname.elm_names_size = 128; 440 441 /* Get the list of ses devices */ 442 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 443 globfree(&g); 444 xo_errx(EXIT_FAILURE, "No SES devices found"); 445 } 446 xo_set_version(SESUTIL_XO_VERSION); 447 xo_open_container("sesutil"); 448 xo_open_list("enclosures"); 449 for (i = 0; i < g.gl_pathc; i++) { 450 /* ensure we only got numbers after ses */ 451 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 452 strlen(g.gl_pathv[i] + 8)) { 453 continue; 454 } 455 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 456 /* 457 * Don't treat non-access errors as critical if we are 458 * accessing all devices 459 */ 460 if (errno == EACCES && g.gl_pathc > 1) { 461 xo_err(EXIT_FAILURE, "unable to access SES device"); 462 } 463 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 464 continue; 465 } 466 467 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 468 close(fd); 469 xo_err(EXIT_FAILURE, "ENCIOC_GETNELM"); 470 } 471 472 e_ptr = calloc(nobj, sizeof(encioc_element_t)); 473 if (e_ptr == NULL) { 474 close(fd); 475 xo_err(EXIT_FAILURE, "calloc()"); 476 } 477 478 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) { 479 close(fd); 480 xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 481 } 482 483 xo_open_instance("enclosures"); 484 xo_emit("{t:enc/%s}:\n", g.gl_pathv[i] + 5); 485 stri.bufsiz = sizeof(str); 486 stri.buf = &str[0]; 487 if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0) 488 xo_emit("\tEnclosure Name: {t:name/%s}\n", stri.buf); 489 stri.bufsiz = sizeof(str); 490 stri.buf = &str[0]; 491 if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0) 492 xo_emit("\tEnclosure ID: {t:id/%s}\n", stri.buf); 493 494 xo_open_list("elements"); 495 for (j = 0; j < nobj; j++) { 496 /* Get the status of the element */ 497 memset(&e_status, 0, sizeof(e_status)); 498 e_status.elm_idx = e_ptr[j].elm_idx; 499 if (ioctl(fd, ENCIOC_GETELMSTAT, 500 (caddr_t) &e_status) < 0) { 501 close(fd); 502 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 503 } 504 /* Get the description of the element */ 505 e_desc.elm_idx = e_ptr[j].elm_idx; 506 e_desc.elm_desc_len = UINT16_MAX; 507 if (ioctl(fd, ENCIOC_GETELMDESC, 508 (caddr_t) &e_desc) < 0) { 509 close(fd); 510 xo_err(EXIT_FAILURE, "ENCIOC_GETELMDESC"); 511 } 512 e_desc.elm_desc_str[e_desc.elm_desc_len] = '\0'; 513 /* Get the device name(s) of the element */ 514 e_devname.elm_idx = e_ptr[j].elm_idx; 515 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 516 (caddr_t) &e_devname) <0) { 517 /* Continue even if we can't look up devnames */ 518 e_devname.elm_devnames[0] = '\0'; 519 } 520 xo_open_instance("elements"); 521 xo_emit("\tElement {:id/%u}, Type: {:type/%s}\n", e_ptr[j].elm_idx, 522 geteltnm(e_ptr[j].elm_type)); 523 xo_emit("\t\tStatus: {:status/%s} ({q:status_code/0x%02x 0x%02x 0x%02x 0x%02x})\n", 524 scode2ascii(e_status.cstat[0]), e_status.cstat[0], 525 e_status.cstat[1], e_status.cstat[2], 526 e_status.cstat[3]); 527 if (e_desc.elm_desc_len > 0) { 528 xo_emit("\t\tDescription: {:description/%s}\n", 529 e_desc.elm_desc_str); 530 } 531 if (e_devname.elm_names_len > 0) { 532 xo_emit("\t\tDevice Names: {:device_names/%s}\n", 533 e_devname.elm_devnames); 534 } 535 print_extra_status(e_ptr[j].elm_type, e_status.cstat, PRINT_STYLE_DASHED); 536 xo_close_instance("elements"); 537 } 538 xo_close_list("elements"); 539 free(e_ptr); 540 close(fd); 541 } 542 globfree(&g); 543 free(e_devname.elm_devnames); 544 free(e_desc.elm_desc_str); 545 xo_close_list("enclosures"); 546 xo_close_container("sesutil"); 547 xo_finish(); 548 549 return (EXIT_SUCCESS); 550 } 551 552 /* 553 * Get rid of the 'passN' devices, unless there's nothing else to show. 554 */ 555 static void 556 skip_pass_devices(char *devnames, size_t devnameslen) 557 { 558 char *dev, devs[128], passes[128], *tmp; 559 560 devs[0] = passes[0] = '\0'; 561 tmp = devnames; 562 563 while ((dev = strsep(&tmp, ",")) != NULL) { 564 if (strncmp(dev, "pass", 4) == 0) { 565 if (passes[0] != '\0') 566 strlcat(passes, ",", sizeof(passes)); 567 strlcat(passes, dev, sizeof(passes)); 568 } else { 569 if (devs[0] != '\0') 570 strlcat(devs, ",", sizeof(devs)); 571 strlcat(devs, dev, sizeof(devs)); 572 } 573 } 574 strlcpy(devnames, devs, devnameslen); 575 if (devnames[0] == '\0') 576 strlcpy(devnames, passes, devnameslen); 577 } 578 579 static void 580 fetch_device_details(char *devnames, char **model, char **serial, off_t *size) 581 { 582 char ident[DISK_IDENT_SIZE]; 583 struct diocgattr_arg arg; 584 char *tmp; 585 off_t mediasize; 586 int comma; 587 int fd; 588 589 comma = (int)strcspn(devnames, ","); 590 asprintf(&tmp, "/dev/%.*s", comma, devnames); 591 if (tmp == NULL) 592 err(1, "asprintf"); 593 fd = open(tmp, O_RDONLY); 594 free(tmp); 595 if (fd < 0) { 596 /* 597 * This can happen with a disk so broken it cannot 598 * be probed by GEOM. 599 */ 600 *model = strdup("?"); 601 *serial = strdup("?"); 602 *size = -1; 603 close(fd); 604 return; 605 } 606 607 strlcpy(arg.name, "GEOM::descr", sizeof(arg.name)); 608 arg.len = sizeof(arg.value.str); 609 if (ioctl(fd, DIOCGATTR, &arg) == 0) 610 *model = strdup(arg.value.str); 611 else 612 *model = NULL; 613 614 if (ioctl(fd, DIOCGIDENT, ident) == 0) 615 *serial = strdup(ident); 616 else 617 *serial = NULL; 618 619 if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0) 620 *size = mediasize; 621 else 622 *size = -1; 623 close(fd); 624 } 625 626 static void 627 show_device(int fd, int elm_idx, encioc_elm_status_t e_status, encioc_elm_desc_t e_desc) 628 { 629 encioc_elm_devnames_t e_devname; 630 char *model = NULL, *serial = NULL; 631 off_t size; 632 633 /* Get the device name(s) of the element */ 634 memset(&e_devname, 0, sizeof(e_devname)); 635 e_devname.elm_idx = elm_idx; 636 e_devname.elm_names_size = 128; 637 e_devname.elm_devnames = calloc(128, sizeof(char)); 638 if (e_devname.elm_devnames == NULL) { 639 close(fd); 640 xo_err(EXIT_FAILURE, "calloc()"); 641 } 642 643 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 644 (caddr_t) &e_devname) < 0) { 645 /* We don't care if this fails */ 646 e_devname.elm_devnames[0] = '\0'; 647 size = -1; 648 } else { 649 skip_pass_devices(e_devname.elm_devnames, 128); 650 fetch_device_details(e_devname.elm_devnames, &model, &serial, &size); 651 } 652 xo_open_instance("elements"); 653 xo_emit("{e:type/device_slot}"); 654 xo_emit("{d:description/%-15s} ", e_desc.elm_desc_len > 0 ? e_desc.elm_desc_str : "-"); 655 xo_emit("{e:description/%-15s}", e_desc.elm_desc_len > 0 ? e_desc.elm_desc_str : ""); 656 xo_emit("{d:device_names/%-7s} ", e_devname.elm_names_len > 0 ? e_devname.elm_devnames : "-"); 657 xo_emit("{e:device_names/%s}", e_devname.elm_names_len > 0 ? e_devname.elm_devnames : ""); 658 xo_emit("{d:model/%-25s} ", model ? model : "-"); 659 xo_emit("{e:model/%s}", model ? model : ""); 660 xo_emit("{d:serial/%-20s} ", serial != NULL ? serial : "-"); 661 xo_emit("{e:serial/%s}", serial != NULL ? serial : ""); 662 if ((e_status.cstat[0] & 0xf) == SES_OBJSTAT_OK && size >= 0) { 663 xo_emit("{h,hn-1000:size/%ld}{e:status/%s}", 664 size, scode2ascii(e_status.cstat[0])); 665 } else { 666 xo_emit("{:status/%s}", scode2ascii(e_status.cstat[0])); 667 } 668 print_extra_status(ELMTYP_ARRAY_DEV, e_status.cstat, PRINT_STYLE_CSV); 669 xo_emit("\n"); 670 xo_close_instance("elements"); 671 free(serial); 672 free(model); 673 free(e_devname.elm_devnames); 674 } 675 676 static void 677 show_therm(encioc_elm_status_t e_status, encioc_elm_desc_t e_desc) 678 { 679 680 if (e_desc.elm_desc_len <= 0) { 681 /* We don't have a label to display; might as well skip it. */ 682 return; 683 } 684 685 if (e_status.cstat[2] == 0) { 686 /* No temperature to show. */ 687 return; 688 } 689 690 xo_open_instance("elements"); 691 xo_emit("{e:type/temperature_sensor}"); 692 xo_emit("{:description/%s}: {:temperature/%d}{Uw:C}", 693 e_desc.elm_desc_str, e_status.cstat[2] - TEMPERATURE_OFFSET); 694 xo_close_instance("elements"); 695 } 696 697 static void 698 show_vom(encioc_elm_status_t e_status, encioc_elm_desc_t e_desc) 699 { 700 701 if (e_desc.elm_desc_len <= 0) { 702 /* We don't have a label to display; might as well skip it. */ 703 return; 704 } 705 706 if (e_status.cstat[2] == 0) { 707 /* No voltage to show. */ 708 return; 709 } 710 711 xo_open_instance("elements"); 712 xo_emit("{e:type/voltage_sensor}"); 713 xo_emit("{:description/%s}: {:voltage/%.2f}{Uw:V}", 714 e_desc.elm_desc_str, be16dec(e_status.cstat + 2) / 100.0); 715 xo_close_instance("elements"); 716 } 717 718 static int 719 show(int argc, char **argv __unused) 720 { 721 encioc_string_t stri; 722 encioc_elm_status_t e_status; 723 encioc_elm_desc_t e_desc; 724 encioc_element_t *e_ptr; 725 glob_t g; 726 elm_type_t prev_type; 727 int fd; 728 unsigned int j, nobj; 729 size_t i; 730 bool first_ses; 731 char str[32]; 732 733 if (argc != 1) { 734 usage(stderr, "map"); 735 } 736 737 first_ses = true; 738 739 e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char)); 740 if (e_desc.elm_desc_str == NULL) 741 xo_err(EXIT_FAILURE, "calloc()"); 742 743 /* Get the list of ses devices */ 744 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 745 globfree(&g); 746 xo_errx(EXIT_FAILURE, "No SES devices found"); 747 } 748 xo_set_version(SESUTIL_XO_VERSION); 749 xo_open_container("sesutil"); 750 xo_open_list("enclosures"); 751 for (i = 0; i < g.gl_pathc; i++) { 752 /* ensure we only got numbers after ses */ 753 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 754 strlen(g.gl_pathv[i] + 8)) { 755 continue; 756 } 757 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 758 /* 759 * Don't treat non-access errors as critical if we are 760 * accessing all devices 761 */ 762 if (errno == EACCES && g.gl_pathc > 1) { 763 xo_err(EXIT_FAILURE, "unable to access SES device"); 764 } 765 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 766 continue; 767 } 768 769 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 770 close(fd); 771 xo_err(EXIT_FAILURE, "ENCIOC_GETNELM"); 772 } 773 774 e_ptr = calloc(nobj, sizeof(encioc_element_t)); 775 if (e_ptr == NULL) { 776 close(fd); 777 xo_err(EXIT_FAILURE, "calloc()"); 778 } 779 780 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) { 781 close(fd); 782 xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 783 } 784 785 xo_open_instance("enclosures"); 786 787 if (first_ses) 788 first_ses = false; 789 else 790 xo_emit("\n"); 791 792 xo_emit("{t:enc/%s}: ", g.gl_pathv[i] + 5); 793 stri.bufsiz = sizeof(str); 794 stri.buf = &str[0]; 795 if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0) 796 xo_emit("<{t:name/%s}>; ", stri.buf); 797 stri.bufsiz = sizeof(str); 798 stri.buf = &str[0]; 799 if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0) 800 xo_emit("ID: {t:id/%s}", stri.buf); 801 xo_emit("\n"); 802 803 xo_open_list("elements"); 804 prev_type = -1; 805 for (j = 0; j < nobj; j++) { 806 /* Get the status of the element */ 807 memset(&e_status, 0, sizeof(e_status)); 808 e_status.elm_idx = e_ptr[j].elm_idx; 809 if (ioctl(fd, ENCIOC_GETELMSTAT, 810 (caddr_t) &e_status) < 0) { 811 close(fd); 812 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 813 } 814 815 /* 816 * Skip "Unsupported" elements; those usually precede 817 * the actual device entries and are not particularly 818 * interesting. 819 */ 820 if (e_status.cstat[0] == SES_OBJSTAT_UNSUPPORTED) 821 continue; 822 823 /* Get the description of the element */ 824 e_desc.elm_idx = e_ptr[j].elm_idx; 825 e_desc.elm_desc_len = UINT16_MAX; 826 if (ioctl(fd, ENCIOC_GETELMDESC, 827 (caddr_t) &e_desc) < 0) { 828 close(fd); 829 xo_err(EXIT_FAILURE, "ENCIOC_GETELMDESC"); 830 } 831 e_desc.elm_desc_str[e_desc.elm_desc_len] = '\0'; 832 833 switch (e_ptr[j].elm_type) { 834 case ELMTYP_DEVICE: 835 case ELMTYP_ARRAY_DEV: 836 if (e_ptr[j].elm_type != prev_type) 837 xo_emit("Desc Dev Model Ident Size/Status\n"); 838 839 show_device(fd, e_ptr[j].elm_idx, e_status, e_desc); 840 prev_type = e_ptr[j].elm_type; 841 break; 842 case ELMTYP_THERM: 843 if (e_ptr[j].elm_type != prev_type) 844 xo_emit("\nTemperatures: "); 845 else 846 xo_emit(", "); 847 prev_type = e_ptr[j].elm_type; 848 show_therm(e_status, e_desc); 849 break; 850 case ELMTYP_VOM: 851 if (e_ptr[j].elm_type != prev_type) 852 xo_emit("\nVoltages: "); 853 else 854 xo_emit(", "); 855 prev_type = e_ptr[j].elm_type; 856 show_vom(e_status, e_desc); 857 break; 858 default: 859 /* 860 * Ignore stuff not interesting to the user. 861 */ 862 break; 863 } 864 } 865 if (prev_type != (elm_type_t)-1 && 866 prev_type != ELMTYP_DEVICE && prev_type != ELMTYP_ARRAY_DEV) 867 xo_emit("\n"); 868 xo_close_list("elements"); 869 free(e_ptr); 870 close(fd); 871 } 872 globfree(&g); 873 free(e_desc.elm_desc_str); 874 xo_close_list("enclosures"); 875 xo_close_container("sesutil"); 876 xo_finish(); 877 878 return (EXIT_SUCCESS); 879 } 880 881 static int 882 encstatus(int argc, char **argv __unused) 883 { 884 glob_t g; 885 int fd, status; 886 size_t i, e; 887 u_char estat; 888 889 status = 0; 890 if (argc != 1) { 891 usage(stderr, "status"); 892 } 893 894 /* Get the list of ses devices */ 895 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 896 globfree(&g); 897 xo_errx(EXIT_FAILURE, "No SES devices found"); 898 } 899 900 xo_set_version(SESUTIL_XO_VERSION); 901 xo_open_container("sesutil"); 902 xo_open_list("enclosures"); 903 for (i = 0; i < g.gl_pathc; i++) { 904 /* ensure we only got numbers after ses */ 905 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 906 strlen(g.gl_pathv[i] + 8)) { 907 continue; 908 } 909 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 910 /* 911 * Don't treat non-access errors as critical if we are 912 * accessing all devices 913 */ 914 if (errno == EACCES && g.gl_pathc > 1) { 915 xo_err(EXIT_FAILURE, "unable to access SES device"); 916 } 917 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 918 continue; 919 } 920 921 if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat) < 0) { 922 xo_err(EXIT_FAILURE, "ENCIOC_GETENCSTAT"); 923 close(fd); 924 } 925 926 xo_open_instance("enclosures"); 927 xo_emit("{:enc/%s}: ", g.gl_pathv[i] + 5); 928 e = 0; 929 if (estat == 0) { 930 if (status == 0) { 931 status = 1; 932 } 933 xo_emit("{q:status/OK}"); 934 } else { 935 if (estat & SES_ENCSTAT_INFO) { 936 xo_emit("{lq:status/INFO}"); 937 e++; 938 } 939 if (estat & SES_ENCSTAT_NONCRITICAL) { 940 if (e) 941 xo_emit(","); 942 xo_emit("{lq:status/NONCRITICAL}"); 943 e++; 944 } 945 if (estat & SES_ENCSTAT_CRITICAL) { 946 if (e) 947 xo_emit(","); 948 xo_emit("{lq:status/CRITICAL}"); 949 e++; 950 status = -1; 951 } 952 if (estat & SES_ENCSTAT_UNRECOV) { 953 if (e) 954 xo_emit(","); 955 xo_emit("{lq:status/UNRECOV}"); 956 e++; 957 status = -1; 958 } 959 } 960 xo_close_instance("enclosures"); 961 xo_emit("\n"); 962 close(fd); 963 } 964 globfree(&g); 965 966 xo_close_list("enclosures"); 967 xo_close_container("sesutil"); 968 xo_finish(); 969 970 if (status == 1) { 971 return (EXIT_SUCCESS); 972 } else { 973 return (EXIT_FAILURE); 974 } 975 } 976 977 int 978 main(int argc, char **argv) 979 { 980 int i, ch; 981 struct command *cmd = NULL; 982 983 argc = xo_parse_args(argc, argv); 984 if (argc < 0) 985 exit(1); 986 987 uflag = "/dev/ses[0-9]*"; 988 while ((ch = getopt_long(argc, argv, "u:", NULL, NULL)) != -1) { 989 switch (ch) { 990 case 'u': 991 uflag = optarg; 992 break; 993 case '?': 994 default: 995 usage(stderr, NULL); 996 } 997 } 998 argc -= optind; 999 argv += optind; 1000 1001 if (argc < 1) { 1002 warnx("Missing command"); 1003 usage(stderr, NULL); 1004 } 1005 1006 for (i = 0; i < nbcmds; i++) { 1007 if (strcmp(argv[0], cmds[i].name) == 0) { 1008 cmd = &cmds[i]; 1009 break; 1010 } 1011 } 1012 1013 if (cmd == NULL) { 1014 warnx("unknown command %s", argv[0]); 1015 usage(stderr, NULL); 1016 } 1017 1018 return (cmd->exec(argc, argv)); 1019 } 1020