1 /*- 2 * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org> 4 * Copyright (c) 2000 by Matthew Jacob 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer 12 * in this position and unchanged. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/endian.h> 33 #include <sys/param.h> 34 #include <sys/ioctl.h> 35 #include <sys/types.h> 36 37 #include <err.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <getopt.h> 41 #include <glob.h> 42 #include <stdbool.h> 43 #include <stddef.h> 44 #include <stdint.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #include <cam/scsi/scsi_enc.h> 51 52 #include "eltsub.h" 53 54 static int encstatus(int argc, char **argv); 55 static int fault(int argc, char **argv); 56 static int locate(int argc, char **argv); 57 static int objmap(int argc, char **argv); 58 static int sesled(int argc, char **argv, bool fault); 59 static void sesutil_print(bool *title, const char *fmt, ...) __printflike(2,3); 60 61 static struct command { 62 const char *name; 63 const char *param; 64 const char *desc; 65 int (*exec)(int argc, char **argv); 66 } cmds[] = { 67 { "fault", 68 "(<disk>|<sesid>|all) (on|off)", 69 "Change the state of the fault LED associated with a disk", 70 fault }, 71 { "locate", 72 "(<disk>|<sesid>|all) (on|off)", 73 "Change the state of the locate LED associated with a disk", 74 locate }, 75 { "map", "", 76 "Print a map of the devices managed by the enclosure", objmap } , 77 { "status", "", "Print the status of the enclosure", 78 encstatus }, 79 }; 80 81 static const int nbcmds = nitems(cmds); 82 static const char *uflag; 83 84 static void 85 usage(FILE *out, const char *subcmd) 86 { 87 int i; 88 89 if (subcmd == NULL) { 90 fprintf(out, "Usage: %s [-u /dev/ses<N>] <command> [options]\n", 91 getprogname()); 92 fprintf(out, "Commands supported:\n"); 93 } 94 for (i = 0; i < nbcmds; i++) { 95 if (subcmd != NULL) { 96 if (strcmp(subcmd, cmds[i].name) == 0) { 97 fprintf(out, "Usage: %s %s [-u /dev/ses<N>] " 98 "%s\n\t%s\n", getprogname(), subcmd, 99 cmds[i].param, cmds[i].desc); 100 break; 101 } 102 continue; 103 } 104 fprintf(out, " %-12s%s\n\t\t%s\n\n", cmds[i].name, 105 cmds[i].param, cmds[i].desc); 106 } 107 108 exit(EXIT_FAILURE); 109 } 110 111 static void 112 do_led(int fd, unsigned int idx, bool onoff, bool setfault) 113 { 114 encioc_elm_status_t o; 115 116 o.elm_idx = idx; 117 if (ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t) &o) < 0) { 118 close(fd); 119 err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 120 } 121 o.cstat[0] |= 0x80; 122 if (setfault) { 123 if (onoff) 124 o.cstat[3] |= 0x20; 125 else 126 o.cstat[3] &= 0xdf; 127 } else { 128 if (onoff) 129 o.cstat[2] |= 0x02; 130 else 131 o.cstat[2] &= 0xfd; 132 } 133 134 if (ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t) &o) < 0) { 135 close(fd); 136 err(EXIT_FAILURE, "ENCIOC_SETELMSTAT"); 137 } 138 } 139 140 static bool 141 disk_match(const char *devnames, const char *disk, size_t len) 142 { 143 const char *dname; 144 145 dname = devnames; 146 while ((dname = strstr(dname, disk)) != NULL) { 147 if (dname[len] == '\0' || dname[len] == ',') { 148 return (true); 149 } 150 dname++; 151 } 152 153 return (false); 154 } 155 156 static int 157 sesled(int argc, char **argv, bool setfault) 158 { 159 encioc_elm_devnames_t objdn; 160 encioc_element_t *objp; 161 glob_t g; 162 char *disk, *endptr; 163 size_t len, i, ndisks; 164 int fd; 165 unsigned int nobj, j, sesid; 166 bool all, isses, onoff; 167 168 isses = false; 169 all = false; 170 onoff = false; 171 172 if (argc != 3) { 173 usage(stderr, (setfault ? "fault" : "locate")); 174 } 175 176 disk = argv[1]; 177 178 sesid = strtoul(disk, &endptr, 10); 179 if (*endptr == '\0') { 180 endptr = strrchr(uflag, '*'); 181 if (endptr != NULL && *endptr == '*') { 182 warnx("Must specifying a SES device (-u) to use a SES " 183 "id# to identify a disk"); 184 usage(stderr, (setfault ? "fault" : "locate")); 185 } 186 isses = true; 187 } 188 189 if (strcmp(argv[2], "on") == 0) { 190 onoff = true; 191 } else if (strcmp(argv[2], "off") == 0) { 192 onoff = false; 193 } else { 194 usage(stderr, (setfault ? "fault" : "locate")); 195 } 196 197 if (strcmp(disk, "all") == 0) { 198 all = true; 199 } 200 len = strlen(disk); 201 202 /* Get the list of ses devices */ 203 if (glob((uflag != NULL ? uflag : "/dev/ses[0-9]*"), 0, NULL, &g) == 204 GLOB_NOMATCH) { 205 globfree(&g); 206 errx(EXIT_FAILURE, "No SES devices found"); 207 } 208 209 ndisks = 0; 210 for (i = 0; i < g.gl_pathc; i++) { 211 /* ensure we only got numbers after ses */ 212 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 213 strlen(g.gl_pathv[i] + 8)) { 214 continue; 215 } 216 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 217 /* 218 * Don't treat non-access errors as critical if we are 219 * accessing all devices 220 */ 221 if (errno == EACCES && g.gl_pathc > 1) { 222 err(EXIT_FAILURE, "unable to access SES device"); 223 } 224 warn("unable to access SES device: %s", g.gl_pathv[i]); 225 continue; 226 } 227 228 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 229 close(fd); 230 err(EXIT_FAILURE, "ENCIOC_GETNELM"); 231 } 232 233 objp = calloc(nobj, sizeof(encioc_element_t)); 234 if (objp == NULL) { 235 close(fd); 236 err(EXIT_FAILURE, "calloc()"); 237 } 238 239 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) objp) < 0) { 240 close(fd); 241 err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 242 } 243 244 if (isses) { 245 if (sesid > nobj) { 246 close(fd); 247 errx(EXIT_FAILURE, 248 "Requested SES ID does not exist"); 249 } 250 do_led(fd, sesid, onoff, setfault); 251 ndisks++; 252 close(fd); 253 break; 254 } 255 for (j = 0; j < nobj; j++) { 256 if (all) { 257 do_led(fd, objp[j].elm_idx, onoff, setfault); 258 continue; 259 } 260 memset(&objdn, 0, sizeof(objdn)); 261 objdn.elm_idx = objp[j].elm_idx; 262 objdn.elm_names_size = 128; 263 objdn.elm_devnames = calloc(128, sizeof(char)); 264 if (objdn.elm_devnames == NULL) { 265 close(fd); 266 err(EXIT_FAILURE, "calloc()"); 267 } 268 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 269 (caddr_t) &objdn) <0) { 270 continue; 271 } 272 if (objdn.elm_names_len > 0) { 273 if (disk_match(objdn.elm_devnames, disk, len)) { 274 do_led(fd, objdn.elm_idx, 275 onoff, setfault); 276 ndisks++; 277 break; 278 } 279 } 280 } 281 free(objp); 282 close(fd); 283 } 284 globfree(&g); 285 if (ndisks == 0 && all == false) { 286 errx(EXIT_FAILURE, "Count not find the SES id of device '%s'", 287 disk); 288 } 289 290 return (EXIT_SUCCESS); 291 } 292 293 static int 294 locate(int argc, char **argv) 295 { 296 297 return (sesled(argc, argv, false)); 298 } 299 300 static int 301 fault(int argc, char **argv) 302 { 303 304 return (sesled(argc, argv, true)); 305 } 306 307 #define TEMPERATURE_OFFSET 20 308 static void 309 sesutil_print(bool *title, const char *fmt, ...) 310 { 311 va_list args; 312 313 if (!*title) { 314 printf("\t\tExtra status:\n"); 315 *title = true; 316 } 317 va_start(args, fmt); 318 vprintf(fmt, args); 319 va_end(args); 320 } 321 322 static void 323 print_extra_status(int eletype, u_char *cstat) 324 { 325 bool title = false; 326 327 if (cstat[0] & 0x40) { 328 sesutil_print(&title, "\t\t- Predicted Failure\n"); 329 } 330 if (cstat[0] & 0x20) { 331 sesutil_print(&title, "\t\t- Disabled\n"); 332 } 333 if (cstat[0] & 0x10) { 334 sesutil_print(&title, "\t\t- Swapped\n"); 335 } 336 switch (eletype) { 337 case ELMTYP_DEVICE: 338 if (cstat[2] & 0x02) { 339 sesutil_print(&title, "\t\t- LED=locate\n"); 340 } 341 if (cstat[2] & 0x20) { 342 sesutil_print(&title, "\t\t- LED=fault\n"); 343 } 344 break; 345 case ELMTYP_ARRAY_DEV: 346 if (cstat[2] & 0x02) { 347 sesutil_print(&title, "\t\t- LED=locate\n"); 348 } 349 if (cstat[2] & 0x20) { 350 sesutil_print(&title, "\t\t- LED=fault\n"); 351 } 352 break; 353 case ELMTYP_FAN: 354 sesutil_print(&title, "\t\t- Speed: %d rpm\n", 355 (((0x7 & cstat[1]) << 8) + cstat[2]) * 10); 356 break; 357 case ELMTYP_THERM: 358 if (cstat[2]) { 359 sesutil_print(&title, "\t\t- Temperature: %d C\n", 360 cstat[2] - TEMPERATURE_OFFSET); 361 } else { 362 sesutil_print(&title, "\t\t- Temperature: -reserved-\n"); 363 } 364 break; 365 case ELMTYP_VOM: 366 sesutil_print(&title, "\t\t- Voltage: %.2f V\n", 367 be16dec(cstat + 2) / 100.0); 368 break; 369 } 370 } 371 372 static int 373 objmap(int argc, char **argv __unused) 374 { 375 encioc_string_t stri; 376 encioc_elm_devnames_t e_devname; 377 encioc_elm_status_t e_status; 378 encioc_elm_desc_t e_desc; 379 encioc_element_t *e_ptr; 380 glob_t g; 381 int fd; 382 unsigned int j, nobj; 383 size_t i; 384 char str[32]; 385 386 if (argc != 1) { 387 usage(stderr, "map"); 388 } 389 390 /* Get the list of ses devices */ 391 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 392 globfree(&g); 393 errx(EXIT_FAILURE, "No SES devices found"); 394 } 395 for (i = 0; i < g.gl_pathc; i++) { 396 /* ensure we only got numbers after ses */ 397 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 398 strlen(g.gl_pathv[i] + 8)) { 399 continue; 400 } 401 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 402 /* 403 * Don't treat non-access errors as critical if we are 404 * accessing all devices 405 */ 406 if (errno == EACCES && g.gl_pathc > 1) { 407 err(EXIT_FAILURE, "unable to access SES device"); 408 } 409 warn("unable to access SES device: %s", g.gl_pathv[i]); 410 continue; 411 } 412 413 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 414 close(fd); 415 err(EXIT_FAILURE, "ENCIOC_GETNELM"); 416 } 417 418 e_ptr = calloc(nobj, sizeof(encioc_element_t)); 419 if (e_ptr == NULL) { 420 close(fd); 421 err(EXIT_FAILURE, "calloc()"); 422 } 423 424 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) { 425 close(fd); 426 err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 427 } 428 429 printf("%s:\n", g.gl_pathv[i] + 5); 430 stri.bufsiz = sizeof(str); 431 stri.buf = &str[0]; 432 if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0) 433 printf("\tEnclosure Name: %s\n", stri.buf); 434 stri.bufsiz = sizeof(str); 435 stri.buf = &str[0]; 436 if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0) 437 printf("\tEnclosure ID: %s\n", stri.buf); 438 439 for (j = 0; j < nobj; j++) { 440 /* Get the status of the element */ 441 memset(&e_status, 0, sizeof(e_status)); 442 e_status.elm_idx = e_ptr[j].elm_idx; 443 if (ioctl(fd, ENCIOC_GETELMSTAT, 444 (caddr_t) &e_status) < 0) { 445 close(fd); 446 err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 447 } 448 /* Get the description of the element */ 449 memset(&e_desc, 0, sizeof(e_desc)); 450 e_desc.elm_idx = e_ptr[j].elm_idx; 451 e_desc.elm_desc_len = UINT16_MAX; 452 e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char)); 453 if (e_desc.elm_desc_str == NULL) { 454 close(fd); 455 err(EXIT_FAILURE, "calloc()"); 456 } 457 if (ioctl(fd, ENCIOC_GETELMDESC, 458 (caddr_t) &e_desc) < 0) { 459 close(fd); 460 err(EXIT_FAILURE, "ENCIOC_GETELMDESC"); 461 } 462 /* Get the device name(s) of the element */ 463 memset(&e_devname, 0, sizeof(e_devname)); 464 e_devname.elm_idx = e_ptr[j].elm_idx; 465 e_devname.elm_names_size = 128; 466 e_devname.elm_devnames = calloc(128, sizeof(char)); 467 if (e_devname.elm_devnames == NULL) { 468 close(fd); 469 err(EXIT_FAILURE, "calloc()"); 470 } 471 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 472 (caddr_t) &e_devname) <0) { 473 /* We don't care if this fails */ 474 e_devname.elm_devnames[0] = '\0'; 475 } 476 printf("\tElement %u, Type: %s\n", e_ptr[j].elm_idx, 477 geteltnm(e_ptr[j].elm_type)); 478 printf("\t\tStatus: %s (0x%02x 0x%02x 0x%02x 0x%02x)\n", 479 scode2ascii(e_status.cstat[0]), e_status.cstat[0], 480 e_status.cstat[1], e_status.cstat[2], 481 e_status.cstat[3]); 482 if (e_desc.elm_desc_len > 0) { 483 printf("\t\tDescription: %s\n", 484 e_desc.elm_desc_str); 485 } 486 if (e_devname.elm_names_len > 0) { 487 printf("\t\tDevice Names: %s\n", 488 e_devname.elm_devnames); 489 } 490 print_extra_status(e_ptr[j].elm_type, e_status.cstat); 491 free(e_devname.elm_devnames); 492 } 493 free(e_ptr); 494 close(fd); 495 } 496 globfree(&g); 497 498 return (EXIT_SUCCESS); 499 } 500 501 static int 502 encstatus(int argc, char **argv __unused) 503 { 504 glob_t g; 505 int fd, status; 506 size_t i, e; 507 u_char estat; 508 509 status = 0; 510 if (argc != 1) { 511 usage(stderr, "status"); 512 } 513 514 /* Get the list of ses devices */ 515 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 516 globfree(&g); 517 errx(EXIT_FAILURE, "No SES devices found"); 518 } 519 for (i = 0; i < g.gl_pathc; i++) { 520 /* ensure we only got numbers after ses */ 521 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 522 strlen(g.gl_pathv[i] + 8)) { 523 continue; 524 } 525 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 526 /* 527 * Don't treat non-access errors as critical if we are 528 * accessing all devices 529 */ 530 if (errno == EACCES && g.gl_pathc > 1) { 531 err(EXIT_FAILURE, "unable to access SES device"); 532 } 533 warn("unable to access SES device: %s", g.gl_pathv[i]); 534 continue; 535 } 536 537 if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat) < 0) { 538 close(fd); 539 err(EXIT_FAILURE, "ENCIOC_GETENCSTAT"); 540 } 541 542 printf("%s: ", g.gl_pathv[i] + 5); 543 e = 0; 544 if (estat == 0) { 545 if (status == 0) { 546 status = 1; 547 } 548 printf("OK"); 549 } else { 550 if (estat & SES_ENCSTAT_INFO) { 551 printf("INFO"); 552 e++; 553 } 554 if (estat & SES_ENCSTAT_NONCRITICAL) { 555 if (e) 556 printf(","); 557 printf("NONCRITICAL"); 558 e++; 559 } 560 if (estat & SES_ENCSTAT_CRITICAL) { 561 if (e) 562 printf(","); 563 printf("CRITICAL"); 564 e++; 565 status = -1; 566 } 567 if (estat & SES_ENCSTAT_UNRECOV) { 568 if (e) 569 printf(","); 570 printf("UNRECOV"); 571 e++; 572 status = -1; 573 } 574 } 575 printf("\n"); 576 577 close(fd); 578 } 579 globfree(&g); 580 581 if (status == 1) { 582 return (EXIT_SUCCESS); 583 } else { 584 return (EXIT_FAILURE); 585 } 586 } 587 588 int 589 main(int argc, char **argv) 590 { 591 int i, ch; 592 struct command *cmd = NULL; 593 594 uflag = "/dev/ses[0-9]*"; 595 while ((ch = getopt_long(argc, argv, "u:", NULL, NULL)) != -1) { 596 switch (ch) { 597 case 'u': 598 uflag = optarg; 599 break; 600 case '?': 601 default: 602 usage(stderr, NULL); 603 } 604 } 605 argc -= optind; 606 argv += optind; 607 608 if (argc < 1) { 609 warnx("Missing command"); 610 usage(stderr, NULL); 611 } 612 613 for (i = 0; i < nbcmds; i++) { 614 if (strcmp(argv[0], cmds[i].name) == 0) { 615 cmd = &cmds[i]; 616 break; 617 } 618 } 619 620 if (cmd == NULL) { 621 warnx("unknown command %s", argv[0]); 622 usage(stderr, NULL); 623 } 624 625 return (cmd->exec(argc, argv)); 626 } 627