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