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