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