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/param.h> 33 #include <sys/ioctl.h> 34 #include <sys/types.h> 35 #include <sys/sbuf.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 60 static struct command { 61 const char *name; 62 const char *param; 63 const char *desc; 64 int (*exec)(int argc, char **argv); 65 } cmds[] = { 66 { "fault", 67 "(<disk>|<sesid>|all) (on|off)", 68 "Change the state of the fault LED associated with a disk", 69 fault }, 70 { "locate", 71 "(<disk>|<sesid>|all) (on|off)", 72 "Change the state of the locate LED associated with a disk", 73 locate }, 74 { "map", "", 75 "Print a map of the devices managed by the enclosure", objmap } , 76 { "status", "", "Print the status of the enclosure", 77 encstatus }, 78 }; 79 80 static const int nbcmds = nitems(cmds); 81 static const char *uflag; 82 83 static void 84 usage(FILE *out, const char *subcmd) 85 { 86 int i; 87 88 if (subcmd == NULL) { 89 fprintf(out, "Usage: %s [-u /dev/ses<N>] <command> [options]\n", 90 getprogname()); 91 fprintf(out, "Commands supported:\n"); 92 } 93 for (i = 0; i < nbcmds; i++) { 94 if (subcmd != NULL) { 95 if (strcmp(subcmd, cmds[i].name) == 0) { 96 fprintf(out, "Usage: %s %s [-u /dev/ses<N>] " 97 "%s\n\t%s\n", getprogname(), subcmd, 98 cmds[i].param, cmds[i].desc); 99 break; 100 } 101 continue; 102 } 103 fprintf(out, " %-12s%s\n\t\t%s\n\n", cmds[i].name, 104 cmds[i].param, cmds[i].desc); 105 } 106 107 exit(EXIT_FAILURE); 108 } 109 110 static void 111 do_led(int fd, unsigned int idx, bool onoff, bool setfault) 112 { 113 encioc_elm_status_t o; 114 115 o.elm_idx = idx; 116 if (ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t) &o) < 0) { 117 close(fd); 118 err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 119 } 120 o.cstat[0] |= 0x80; 121 if (setfault) { 122 if (onoff) 123 o.cstat[3] |= 0x20; 124 else 125 o.cstat[3] &= 0xdf; 126 } else { 127 if (onoff) 128 o.cstat[2] |= 0x02; 129 else 130 o.cstat[2] &= 0xfd; 131 } 132 133 if (ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t) &o) < 0) { 134 close(fd); 135 err(EXIT_FAILURE, "ENCIOC_SETELMSTAT"); 136 } 137 } 138 139 static bool 140 disk_match(const char *devnames, const char *disk, size_t len) 141 { 142 const char *dname; 143 144 dname = devnames; 145 while ((dname = strstr(dname, disk)) != NULL) { 146 if (dname[len] == '\0' || dname[len] == ',') { 147 return (true); 148 } 149 dname++; 150 } 151 152 return (false); 153 } 154 155 static int 156 sesled(int argc, char **argv, bool setfault) 157 { 158 encioc_elm_devnames_t objdn; 159 encioc_element_t *objp; 160 glob_t g; 161 char *disk, *endptr; 162 size_t len, i, ndisks; 163 int fd; 164 unsigned int nobj, j, sesid; 165 bool all, isses, onoff; 166 167 isses = false; 168 all = false; 169 onoff = false; 170 171 if (argc != 3) { 172 usage(stderr, (setfault ? "fault" : "locate")); 173 } 174 175 disk = argv[1]; 176 177 sesid = strtoul(disk, &endptr, 10); 178 if (*endptr == '\0') { 179 endptr = strrchr(uflag, '*'); 180 if (endptr != NULL && *endptr == '*') { 181 warnx("Must specifying a SES device (-u) to use a SES " 182 "id# to identify a disk"); 183 usage(stderr, (setfault ? "fault" : "locate")); 184 } 185 isses = true; 186 } 187 188 if (strcmp(argv[2], "on") == 0) { 189 onoff = true; 190 } else if (strcmp(argv[2], "off") == 0) { 191 onoff = false; 192 } else { 193 usage(stderr, (setfault ? "fault" : "locate")); 194 } 195 196 if (strcmp(disk, "all") == 0) { 197 all = true; 198 } 199 len = strlen(disk); 200 201 /* Get the list of ses devices */ 202 if (glob((uflag != NULL ? uflag : "/dev/ses[0-9]*"), 0, NULL, &g) == 203 GLOB_NOMATCH) { 204 globfree(&g); 205 errx(EXIT_FAILURE, "No SES devices found"); 206 } 207 208 ndisks = 0; 209 for (i = 0; i < g.gl_pathc; i++) { 210 /* ensure we only got numbers after ses */ 211 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 212 strlen(g.gl_pathv[i] + 8)) { 213 continue; 214 } 215 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 216 /* 217 * Don't treat non-access errors as critical if we are 218 * accessing all devices 219 */ 220 if (errno == EACCES && g.gl_pathc > 1) { 221 err(EXIT_FAILURE, "unable to access SES device"); 222 } 223 warn("unable to access SES device: %s", g.gl_pathv[i]); 224 continue; 225 } 226 227 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 228 close(fd); 229 err(EXIT_FAILURE, "ENCIOC_GETNELM"); 230 } 231 232 objp = calloc(nobj, sizeof(encioc_element_t)); 233 if (objp == NULL) { 234 close(fd); 235 err(EXIT_FAILURE, "calloc()"); 236 } 237 238 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) objp) < 0) { 239 close(fd); 240 err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 241 } 242 243 if (isses) { 244 if (sesid > nobj) { 245 close(fd); 246 errx(EXIT_FAILURE, 247 "Requested SES ID does not exist"); 248 } 249 do_led(fd, sesid, onoff, setfault); 250 ndisks++; 251 close(fd); 252 break; 253 } 254 for (j = 0; j < nobj; j++) { 255 if (all) { 256 do_led(fd, objp[j].elm_idx, onoff, setfault); 257 continue; 258 } 259 memset(&objdn, 0, sizeof(objdn)); 260 objdn.elm_idx = objp[j].elm_idx; 261 objdn.elm_names_size = 128; 262 objdn.elm_devnames = calloc(128, sizeof(char)); 263 if (objdn.elm_devnames == NULL) { 264 close(fd); 265 err(EXIT_FAILURE, "calloc()"); 266 } 267 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 268 (caddr_t) &objdn) <0) { 269 continue; 270 } 271 if (objdn.elm_names_len > 0) { 272 if (disk_match(objdn.elm_devnames, disk, len)) { 273 do_led(fd, objdn.elm_idx, 274 onoff, setfault); 275 ndisks++; 276 break; 277 } 278 } 279 } 280 free(objp); 281 close(fd); 282 } 283 globfree(&g); 284 if (ndisks == 0 && all == false) { 285 errx(EXIT_FAILURE, "Count not find the SES id of device '%s'", 286 disk); 287 } 288 289 return (EXIT_SUCCESS); 290 } 291 292 static int 293 locate(int argc, char **argv) 294 { 295 296 return (sesled(argc, argv, false)); 297 } 298 299 static int 300 fault(int argc, char **argv) 301 { 302 303 return (sesled(argc, argv, true)); 304 } 305 306 static int 307 objmap(int argc, char **argv __unused) 308 { 309 struct sbuf *extra; 310 encioc_string_t stri; 311 encioc_elm_devnames_t e_devname; 312 encioc_elm_status_t e_status; 313 encioc_elm_desc_t e_desc; 314 encioc_element_t *e_ptr; 315 glob_t g; 316 int fd; 317 unsigned int j, nobj; 318 size_t i; 319 char str[32]; 320 321 if (argc != 1) { 322 usage(stderr, "map"); 323 } 324 325 /* Get the list of ses devices */ 326 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 327 globfree(&g); 328 errx(EXIT_FAILURE, "No SES devices found"); 329 } 330 for (i = 0; i < g.gl_pathc; i++) { 331 /* ensure we only got numbers after ses */ 332 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 333 strlen(g.gl_pathv[i] + 8)) { 334 continue; 335 } 336 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 337 /* 338 * Don't treat non-access errors as critical if we are 339 * accessing all devices 340 */ 341 if (errno == EACCES && g.gl_pathc > 1) { 342 err(EXIT_FAILURE, "unable to access SES device"); 343 } 344 warn("unable to access SES device: %s", g.gl_pathv[i]); 345 continue; 346 } 347 348 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) { 349 close(fd); 350 err(EXIT_FAILURE, "ENCIOC_GETNELM"); 351 } 352 353 e_ptr = calloc(nobj, sizeof(encioc_element_t)); 354 if (e_ptr == NULL) { 355 close(fd); 356 err(EXIT_FAILURE, "calloc()"); 357 } 358 359 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) { 360 close(fd); 361 err(EXIT_FAILURE, "ENCIOC_GETELMMAP"); 362 } 363 364 printf("%s:\n", g.gl_pathv[i] + 5); 365 stri.bufsiz = sizeof(str); 366 stri.buf = &str[0]; 367 if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0) 368 printf("\tEnclosure Name: %s\n", stri.buf); 369 stri.bufsiz = sizeof(str); 370 stri.buf = &str[0]; 371 if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0) 372 printf("\tEnclosure ID: %s\n", stri.buf); 373 374 for (j = 0; j < nobj; j++) { 375 /* Get the status of the element */ 376 memset(&e_status, 0, sizeof(e_status)); 377 e_status.elm_idx = e_ptr[j].elm_idx; 378 if (ioctl(fd, ENCIOC_GETELMSTAT, 379 (caddr_t) &e_status) < 0) { 380 close(fd); 381 err(EXIT_FAILURE, "ENCIOC_GETELMSTAT"); 382 } 383 /* Get the description of the element */ 384 memset(&e_desc, 0, sizeof(e_desc)); 385 e_desc.elm_idx = e_ptr[j].elm_idx; 386 e_desc.elm_desc_len = UINT16_MAX; 387 e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char)); 388 if (e_desc.elm_desc_str == NULL) { 389 close(fd); 390 err(EXIT_FAILURE, "calloc()"); 391 } 392 if (ioctl(fd, ENCIOC_GETELMDESC, 393 (caddr_t) &e_desc) < 0) { 394 close(fd); 395 err(EXIT_FAILURE, "ENCIOC_GETELMDESC"); 396 } 397 /* Get the device name(s) of the element */ 398 memset(&e_devname, 0, sizeof(e_devname)); 399 e_devname.elm_idx = e_ptr[j].elm_idx; 400 e_devname.elm_names_size = 128; 401 e_devname.elm_devnames = calloc(128, sizeof(char)); 402 if (e_devname.elm_devnames == NULL) { 403 close(fd); 404 err(EXIT_FAILURE, "calloc()"); 405 } 406 if (ioctl(fd, ENCIOC_GETELMDEVNAMES, 407 (caddr_t) &e_devname) <0) { 408 /* We don't care if this fails */ 409 e_devname.elm_devnames[0] = '\0'; 410 } 411 printf("\tElement %u, Type: %s\n", e_ptr[j].elm_idx, 412 geteltnm(e_ptr[j].elm_type)); 413 printf("\t\tStatus: %s (0x%02x 0x%02x 0x%02x 0x%02x)\n", 414 scode2ascii(e_status.cstat[0]), e_status.cstat[0], 415 e_status.cstat[1], e_status.cstat[2], 416 e_status.cstat[3]); 417 if (e_desc.elm_desc_len > 0) { 418 printf("\t\tDescription: %s\n", 419 e_desc.elm_desc_str); 420 } 421 if (e_devname.elm_names_len > 0) { 422 printf("\t\tDevice Names: %s\n", 423 e_devname.elm_devnames); 424 } 425 extra = stat2sbuf(e_ptr[j].elm_type, e_status.cstat); 426 if (sbuf_len(extra) > 0) { 427 printf("\t\tExtra status:\n%s", 428 sbuf_data(extra)); 429 } 430 sbuf_delete(extra); 431 free(e_devname.elm_devnames); 432 } 433 free(e_ptr); 434 close(fd); 435 } 436 globfree(&g); 437 438 return (EXIT_SUCCESS); 439 } 440 441 static int 442 encstatus(int argc, char **argv __unused) 443 { 444 glob_t g; 445 int fd, status; 446 size_t i, e; 447 u_char estat; 448 449 status = 0; 450 if (argc != 1) { 451 usage(stderr, "status"); 452 } 453 454 /* Get the list of ses devices */ 455 if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) { 456 globfree(&g); 457 errx(EXIT_FAILURE, "No SES devices found"); 458 } 459 for (i = 0; i < g.gl_pathc; i++) { 460 /* ensure we only got numbers after ses */ 461 if (strspn(g.gl_pathv[i] + 8, "0123456789") != 462 strlen(g.gl_pathv[i] + 8)) { 463 continue; 464 } 465 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) { 466 /* 467 * Don't treat non-access errors as critical if we are 468 * accessing all devices 469 */ 470 if (errno == EACCES && g.gl_pathc > 1) { 471 err(EXIT_FAILURE, "unable to access SES device"); 472 } 473 warn("unable to access SES device: %s", g.gl_pathv[i]); 474 continue; 475 } 476 477 if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat) < 0) { 478 close(fd); 479 err(EXIT_FAILURE, "ENCIOC_GETENCSTAT"); 480 } 481 482 printf("%s: ", g.gl_pathv[i] + 5); 483 e = 0; 484 if (estat == 0) { 485 if (status == 0) { 486 status = 1; 487 } 488 printf("OK"); 489 } else { 490 if (estat & SES_ENCSTAT_INFO) { 491 printf("INFO"); 492 e++; 493 } 494 if (estat & SES_ENCSTAT_NONCRITICAL) { 495 if (e) 496 printf(","); 497 printf("NONCRITICAL"); 498 e++; 499 } 500 if (estat & SES_ENCSTAT_CRITICAL) { 501 if (e) 502 printf(","); 503 printf("CRITICAL"); 504 e++; 505 status = -1; 506 } 507 if (estat & SES_ENCSTAT_UNRECOV) { 508 if (e) 509 printf(","); 510 printf("UNRECOV"); 511 e++; 512 status = -1; 513 } 514 } 515 printf("\n"); 516 517 close(fd); 518 } 519 globfree(&g); 520 521 if (status == 1) { 522 return (EXIT_SUCCESS); 523 } else { 524 return (EXIT_FAILURE); 525 } 526 } 527 528 int 529 main(int argc, char **argv) 530 { 531 int i, ch; 532 struct command *cmd = NULL; 533 534 uflag = "/dev/ses[0-9]*"; 535 while ((ch = getopt_long(argc, argv, "u:", NULL, NULL)) != -1) { 536 switch (ch) { 537 case 'u': 538 uflag = optarg; 539 break; 540 case '?': 541 default: 542 usage(stderr, NULL); 543 } 544 } 545 argc -= optind; 546 argv += optind; 547 548 if (argc < 1) { 549 warnx("Missing command"); 550 usage(stderr, NULL); 551 } 552 553 for (i = 0; i < nbcmds; i++) { 554 if (strcmp(argv[0], cmds[i].name) == 0) { 555 cmd = &cmds[i]; 556 break; 557 } 558 } 559 560 if (cmd == NULL) { 561 warnx("unknown command %s", argv[0]); 562 usage(stderr, NULL); 563 } 564 565 return (cmd->exec(argc, argv)); 566 } 567