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 <errno.h> 41 #include <fcntl.h> 42 #include <getopt.h> 43 #include <glob.h> 44 #include <stdbool.h> 45 #include <stddef.h> 46 #include <stdint.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <unistd.h> 51 #include <libxo/xo.h> 52 53 #include <cam/scsi/scsi_enc.h> 54 55 #include "eltsub.h" 56 57 #define SESUTIL_XO_VERSION "1" 58 59 #define TEMPERATURE_OFFSET 20 60 61 #define PRINT_STYLE_DASHED 0 62 #define PRINT_STYLE_DASHED_2 1 63 #define PRINT_STYLE_CSV 2 64 #define PRINT_STYLE_CSV_2 3 65 66 static int encstatus(int argc, char **argv); 67 static int fault(int argc, char **argv); 68 static int locate(int argc, char **argv); 69 static int objmap(int argc, char **argv); 70 static int sesled(int argc, char **argv, bool fault); 71 static int show(int argc, char **argv); 72 static void sesutil_print(int *style, const char *fmt, ...) __printflike(2,3); 73 74 static struct command { 75 const char *name; 76 const char *param; 77 const char *desc; 78 int (*exec)(int argc, char **argv); 79 } cmds[] = { 80 { "fault", 81 "(<disk>|<sesid>|all) (on|off)", 82 "Change the state of the fault LED associated with a disk", 83 fault }, 84 { "locate", 85 "(<disk>|<sesid>|all) (on|off)", 86 "Change the state of the locate LED associated with a disk", 87 locate }, 88 { "map", "", 89 "Print a map of the devices managed by the enclosure", objmap } , 90 { "show", "", 91 "Print a human-friendly summary of the enclosure", show } , 92 { "status", "", "Print the status of the enclosure", 93 encstatus }, 94 }; 95 96 static const int nbcmds = nitems(cmds); 97 static const char *uflag; 98 99 static void 100 usage(const char *subcmd) 101 { 102 int i; 103 104 if (subcmd == NULL) { 105 xo_error("usage: %s [-u /dev/ses<N>] <command> [options]\n", 106 getprogname()); 107 xo_error("Commands supported:\n"); 108 } 109 for (i = 0; i < nbcmds; i++) { 110 if (subcmd != NULL) { 111 if (strcmp(subcmd, cmds[i].name) == 0) { 112 xo_error("usage: %s %s [-u /dev/ses<N>] " 113 "%s\n\t%s\n", getprogname(), subcmd, 114 cmds[i].param, cmds[i].desc); 115 break; 116 } 117 continue; 118 } 119 xo_error(" %-12s%s\n\t\t%s\n\n", cmds[i].name, 120 cmds[i].param, cmds[i].desc); 121 } 122 123 exit(EXIT_FAILURE); 124 } 125 126 static void 127 do_led(int fd, unsigned int idx, elm_type_t type, bool onoff, bool setfault) 128 { 129 int state = onoff ? 1 : 0; 130 encioc_elm_status_t o; 131 struct ses_ctrl_dev_slot *slot; 132 133 o.elm_idx = idx; 134 if (ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t) &o) < 0) { 135 close(fd); 136 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 137 } 138 ses_status_to_ctrl(type, &o.cstat[0]); 139 switch (type) { 140 case ELMTYP_DEVICE: 141 case ELMTYP_ARRAY_DEV: 142 slot = (struct ses_ctrl_dev_slot *) &o.cstat[0]; 143 ses_ctrl_common_set_select(&slot->common, 1); 144 if (setfault) 145 ses_ctrl_dev_slot_set_rqst_fault(slot, state); 146 else 147 ses_ctrl_dev_slot_set_rqst_ident(slot, state); 148 break; 149 default: 150 return; 151 } 152 if (ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t) &o) < 0) { 153 close(fd); 154 xo_err(EXIT_FAILURE, "ENCIOC_SETELMSTAT"); 155 } 156 } 157 158 static bool 159 disk_match(const char *devnames, const char *disk, size_t len) 160 { 161 const char *dname; 162 163 dname = devnames; 164 while ((dname = strstr(dname, disk)) != NULL) { 165 if (dname[len] == '\0' || dname[len] == ',') { 166 return (true); 167 } 168 dname++; 169 } 170 171 return (false); 172 } 173 174 static int 175 sesled(int argc, char **argv, bool setfault) 176 { 177 encioc_elm_devnames_t objdn; 178 encioc_element_t *objp; 179 glob_t g; 180 char *disk, *endptr; 181 size_t len, i, ndisks; 182 int fd; 183 unsigned int nobj, j, sesid; 184 bool all, isses, onoff; 185 186 isses = false; 187 all = false; 188 onoff = false; 189 190 if (argc != 3) { 191 usage(setfault ? "fault" : "locate"); 192 } 193 194 disk = argv[1]; 195 196 sesid = strtoul(disk, &endptr, 10); 197 if (*endptr == '\0') { 198 endptr = strrchr(uflag, '*'); 199 if (endptr != NULL && *endptr == '*') { 200 xo_warnx("Must specifying a SES device (-u) to use a SES " 201 "id# to identify a disk"); 202 usage(setfault ? "fault" : "locate"); 203 } 204 isses = true; 205 } 206 207 if (strcmp(argv[2], "on") == 0) { 208 onoff = true; 209 } else if (strcmp(argv[2], "off") == 0) { 210 onoff = false; 211 } else { 212 usage(setfault ? "fault" : "locate"); 213 } 214 215 if (strcmp(disk, "all") == 0) { 216 all = true; 217 } 218 len = strlen(disk); 219 220 /* Get the list of ses devices */ 221 if (glob((uflag != NULL ? uflag : "/dev/ses[0-9]*"), 0, NULL, &g) == 222 GLOB_NOMATCH) { 223 globfree(&g); 224 xo_errx(EXIT_FAILURE, "No SES devices found"); 225 } 226 227 ndisks = 0; 228 for (i = 0; i < g.gl_pathc; i++) { 229 /* ensure we only got numbers after ses */ 230 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 231 strlen(g.gl_pathv[i] + 8)) { 232 continue; 233 } 234 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 235 /* 236 * Don't treat non-access errors as critical if we are 237 * accessing all devices 238 */ 239 if (errno == EACCES && g.gl_pathc > 1) { 240 xo_err(EXIT_FAILURE, "unable to access SES device"); 241 } 242 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 243 continue; 244 } 245 246 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 247 close(fd); 248 xo_err(EXIT_FAILURE, "ENCIOC_GETNELM"); 249 } 250 251 objp = calloc(nobj, sizeof(encioc_element_t)); 252 if (objp == NULL) { 253 close(fd); 254 xo_err(EXIT_FAILURE, "calloc()"); 255 } 256 257 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) objp) < 0) { 258 free(objp); 259 close(fd); 260 xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 261 } 262 263 if (isses) { 264 if (sesid >= nobj) { 265 free(objp); 266 close(fd); 267 xo_errx(EXIT_FAILURE, 268 "Requested SES ID does not exist"); 269 } 270 do_led(fd, sesid, objp[sesid].elm_type, onoff, setfault); 271 ndisks++; 272 free(objp); 273 close(fd); 274 break; 275 } 276 for (j = 0; j < nobj; j++) { 277 const int devnames_size = 128; 278 char devnames[devnames_size]; 279 280 if (all) { 281 encioc_elm_status_t es; 282 memset(&es, 0, sizeof(es)); 283 es.elm_idx = objp[j].elm_idx; 284 if (ioctl(fd, ENCIOC_GETELMSTAT, &es) < 0) { 285 close(fd); 286 xo_err(EXIT_FAILURE, 287 "ENCIOC_GETELMSTAT"); 288 } 289 if ((es.cstat[0] & 0xf) == SES_OBJSTAT_NOACCESS) 290 continue; 291 do_led(fd, objp[j].elm_idx, objp[j].elm_type, 292 onoff, setfault); 293 continue; 294 } 295 memset(&objdn, 0, sizeof(objdn)); 296 memset(devnames, 0, devnames_size); 297 objdn.elm_idx = objp[j].elm_idx; 298 objdn.elm_names_size = devnames_size; 299 objdn.elm_devnames = devnames; 300 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 301 (caddr_t) &objdn) <0) { 302 continue; 303 } 304 if (objdn.elm_names_len > 0) { 305 if (disk_match(objdn.elm_devnames, disk, len)) { 306 do_led(fd, objdn.elm_idx, objp[j].elm_type, 307 onoff, setfault); 308 ndisks++; 309 break; 310 } 311 } 312 } 313 free(objp); 314 close(fd); 315 } 316 globfree(&g); 317 if (ndisks == 0 && all == false) { 318 xo_errx(EXIT_FAILURE, "Could not find the SES id of device '%s'", 319 disk); 320 } 321 322 return (EXIT_SUCCESS); 323 } 324 325 static int 326 locate(int argc, char **argv) 327 { 328 329 return (sesled(argc, argv, false)); 330 } 331 332 static int 333 fault(int argc, char **argv) 334 { 335 336 return (sesled(argc, argv, true)); 337 } 338 339 static void 340 sesutil_print(int *style, const char *fmt, ...) 341 { 342 va_list args; 343 344 if (*style == PRINT_STYLE_DASHED) { 345 xo_open_container("extra_status"); 346 xo_emit("\t\tExtra status:\n"); 347 *style = PRINT_STYLE_DASHED_2; 348 } else if (*style == PRINT_STYLE_CSV) { 349 xo_open_container("extra_status"); 350 *style = PRINT_STYLE_CSV_2; 351 } 352 353 if (*style == PRINT_STYLE_DASHED_2) 354 xo_emit("\t\t- "); 355 else if (*style == PRINT_STYLE_CSV_2) 356 xo_emit(", "); 357 va_start(args, fmt); 358 xo_emit_hv(NULL, fmt, args); 359 va_end(args); 360 if (*style == PRINT_STYLE_DASHED_2) 361 xo_emit("\n"); 362 } 363 364 static void 365 print_extra_status(int eletype, u_char *cstat, int style) 366 { 367 368 if (cstat[0] & 0x40) { 369 sesutil_print(&style, "{e:predicted_failure/true} Predicted Failure"); 370 } 371 if (cstat[0] & 0x20) { 372 sesutil_print(&style, "{e:disabled/true} Disabled"); 373 } 374 if (cstat[0] & 0x10) { 375 sesutil_print(&style, "{e:swapped/true} Swapped"); 376 } 377 switch (eletype) { 378 case ELMTYP_DEVICE: 379 case ELMTYP_ARRAY_DEV: 380 if (cstat[2] & 0x02) { 381 sesutil_print(&style, "LED={q:led/locate}"); 382 } 383 if (cstat[2] & 0x20) { 384 sesutil_print(&style, "LED={q:led/fault}"); 385 } 386 break; 387 case ELMTYP_FAN: 388 sesutil_print(&style, "Speed: {:speed/%d}{Uw:rpm}", 389 (((0x7 & cstat[1]) << 8) + cstat[2]) * 10); 390 break; 391 case ELMTYP_THERM: 392 if (cstat[2]) { 393 sesutil_print(&style, "Temperature: {:temperature/%d}{Uw:C}", 394 cstat[2] - TEMPERATURE_OFFSET); 395 } else { 396 sesutil_print(&style, "Temperature: -{q:temperature/reserved}"); 397 } 398 break; 399 case ELMTYP_VOM: 400 sesutil_print(&style, "Voltage: {:voltage/%.2f}{Uw:V}", 401 be16dec(cstat + 2) / 100.0); 402 break; 403 } 404 if (style) { 405 xo_close_container("extra_status"); 406 } 407 } 408 409 static int 410 objmap(int argc, char **argv __unused) 411 { 412 encioc_string_t stri; 413 encioc_elm_devnames_t e_devname; 414 encioc_elm_status_t e_status; 415 encioc_elm_desc_t e_desc; 416 encioc_element_t *e_ptr; 417 glob_t g; 418 int fd; 419 unsigned int j, nobj; 420 size_t i; 421 char str[32]; 422 423 if (argc != 1) { 424 usage("map"); 425 } 426 427 memset(&e_desc, 0, sizeof(e_desc)); 428 /* SES4r02 allows element descriptors of up to 65536 characters */ 429 e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char)); 430 if (e_desc.elm_desc_str == NULL) 431 xo_err(EXIT_FAILURE, "calloc()"); 432 433 e_devname.elm_devnames = calloc(128, sizeof(char)); 434 if (e_devname.elm_devnames == NULL) 435 xo_err(EXIT_FAILURE, "calloc()"); 436 e_devname.elm_names_size = 128; 437 438 /* Get the list of ses devices */ 439 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 440 globfree(&g); 441 xo_errx(EXIT_FAILURE, "No SES devices found"); 442 } 443 xo_set_version(SESUTIL_XO_VERSION); 444 xo_open_container("sesutil"); 445 xo_open_list("enclosures"); 446 for (i = 0; i < g.gl_pathc; i++) { 447 /* ensure we only got numbers after ses */ 448 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 449 strlen(g.gl_pathv[i] + 8)) { 450 continue; 451 } 452 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 453 /* 454 * Don't treat non-access errors as critical if we are 455 * accessing all devices 456 */ 457 if (errno == EACCES && g.gl_pathc > 1) { 458 xo_err(EXIT_FAILURE, "unable to access SES device"); 459 } 460 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 461 continue; 462 } 463 464 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 465 close(fd); 466 xo_err(EXIT_FAILURE, "ENCIOC_GETNELM"); 467 } 468 469 e_ptr = calloc(nobj, sizeof(encioc_element_t)); 470 if (e_ptr == NULL) { 471 close(fd); 472 xo_err(EXIT_FAILURE, "calloc()"); 473 } 474 475 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) { 476 close(fd); 477 xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 478 } 479 480 xo_open_instance("enclosures"); 481 xo_emit("{t:enc/%s}:\n", g.gl_pathv[i] + 5); 482 stri.bufsiz = sizeof(str); 483 stri.buf = &str[0]; 484 if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0) 485 xo_emit("\tEnclosure Name: {t:name/%s}\n", stri.buf); 486 stri.bufsiz = sizeof(str); 487 stri.buf = &str[0]; 488 if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0) 489 xo_emit("\tEnclosure ID: {t:id/%s}\n", stri.buf); 490 491 xo_open_list("elements"); 492 for (j = 0; j < nobj; j++) { 493 /* Get the status of the element */ 494 memset(&e_status, 0, sizeof(e_status)); 495 e_status.elm_idx = e_ptr[j].elm_idx; 496 if (ioctl(fd, ENCIOC_GETELMSTAT, 497 (caddr_t) &e_status) < 0) { 498 close(fd); 499 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 500 } 501 /* Get the description of the element */ 502 e_desc.elm_idx = e_ptr[j].elm_idx; 503 e_desc.elm_desc_len = UINT16_MAX; 504 if (ioctl(fd, ENCIOC_GETELMDESC, 505 (caddr_t) &e_desc) < 0) { 506 close(fd); 507 xo_err(EXIT_FAILURE, "ENCIOC_GETELMDESC"); 508 } 509 e_desc.elm_desc_str[e_desc.elm_desc_len] = '\0'; 510 /* Get the device name(s) of the element */ 511 e_devname.elm_idx = e_ptr[j].elm_idx; 512 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 513 (caddr_t) &e_devname) <0) { 514 /* Continue even if we can't look up devnames */ 515 e_devname.elm_devnames[0] = '\0'; 516 } 517 xo_open_instance("elements"); 518 xo_emit("\tElement {:id/%u}, Type: {:type/%s}\n", e_ptr[j].elm_idx, 519 geteltnm(e_ptr[j].elm_type)); 520 xo_emit("\t\tStatus: {:status/%s} ({q:status_code/0x%02x 0x%02x 0x%02x 0x%02x})\n", 521 scode2ascii(e_status.cstat[0]), e_status.cstat[0], 522 e_status.cstat[1], e_status.cstat[2], 523 e_status.cstat[3]); 524 if (e_desc.elm_desc_len > 0) { 525 xo_emit("\t\tDescription: {:description/%s}\n", 526 e_desc.elm_desc_str); 527 } 528 if (e_devname.elm_names_len > 0) { 529 xo_emit("\t\tDevice Names: {:device_names/%s}\n", 530 e_devname.elm_devnames); 531 } 532 print_extra_status(e_ptr[j].elm_type, e_status.cstat, PRINT_STYLE_DASHED); 533 xo_close_instance("elements"); 534 } 535 xo_close_list("elements"); 536 free(e_ptr); 537 close(fd); 538 } 539 globfree(&g); 540 free(e_devname.elm_devnames); 541 free(e_desc.elm_desc_str); 542 xo_close_list("enclosures"); 543 xo_close_container("sesutil"); 544 if (xo_finish() < 0) 545 xo_err(EXIT_FAILURE, "stdout"); 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 xo_err(EXIT_FAILURE, "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("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 if (xo_finish() < 0) 875 xo_err(EXIT_FAILURE, "stdout"); 876 877 return (EXIT_SUCCESS); 878 } 879 880 static int 881 encstatus(int argc, char **argv __unused) 882 { 883 glob_t g; 884 int fd, status; 885 size_t i, e; 886 u_char estat; 887 888 status = 0; 889 if (argc != 1) { 890 usage("status"); 891 } 892 893 /* Get the list of ses devices */ 894 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 895 globfree(&g); 896 xo_errx(EXIT_FAILURE, "No SES devices found"); 897 } 898 899 xo_set_version(SESUTIL_XO_VERSION); 900 xo_open_container("sesutil"); 901 xo_open_list("enclosures"); 902 for (i = 0; i < g.gl_pathc; i++) { 903 /* ensure we only got numbers after ses */ 904 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 905 strlen(g.gl_pathv[i] + 8)) { 906 continue; 907 } 908 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 909 /* 910 * Don't treat non-access errors as critical if we are 911 * accessing all devices 912 */ 913 if (errno == EACCES && g.gl_pathc > 1) { 914 xo_err(EXIT_FAILURE, "unable to access SES device"); 915 } 916 xo_warn("unable to access SES device: %s", g.gl_pathv[i]); 917 continue; 918 } 919 920 if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat) < 0) { 921 xo_err(EXIT_FAILURE, "ENCIOC_GETENCSTAT"); 922 close(fd); 923 } 924 925 xo_open_instance("enclosures"); 926 xo_emit("{:enc/%s}: ", g.gl_pathv[i] + 5); 927 e = 0; 928 if (estat == 0) { 929 if (status == 0) { 930 status = 1; 931 } 932 xo_emit("{q:status/OK}"); 933 } else { 934 if (estat & SES_ENCSTAT_INFO) { 935 xo_emit("{lq:status/INFO}"); 936 e++; 937 } 938 if (estat & SES_ENCSTAT_NONCRITICAL) { 939 if (e) 940 xo_emit(","); 941 xo_emit("{lq:status/NONCRITICAL}"); 942 e++; 943 } 944 if (estat & SES_ENCSTAT_CRITICAL) { 945 if (e) 946 xo_emit(","); 947 xo_emit("{lq:status/CRITICAL}"); 948 e++; 949 status = -1; 950 } 951 if (estat & SES_ENCSTAT_UNRECOV) { 952 if (e) 953 xo_emit(","); 954 xo_emit("{lq:status/UNRECOV}"); 955 e++; 956 status = -1; 957 } 958 } 959 xo_close_instance("enclosures"); 960 xo_emit("\n"); 961 close(fd); 962 } 963 globfree(&g); 964 965 xo_close_list("enclosures"); 966 xo_close_container("sesutil"); 967 if (xo_finish() < 0) 968 xo_err(EXIT_FAILURE, "stdout"); 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(EXIT_FAILURE); 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(NULL); 996 } 997 } 998 argc -= optind; 999 argv += optind; 1000 1001 if (argc < 1) { 1002 xo_warnx("Missing command"); 1003 usage(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 xo_warnx("unknown command %s", argv[0]); 1015 usage(NULL); 1016 } 1017 1018 return (cmd->exec(argc, argv)); 1019 } 1020