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