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