]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/sesutil/sesutil.c
Merge llvm-project main llvmorg-16-init-18548-gb0daacf58f41
[FreeBSD/FreeBSD.git] / usr.sbin / sesutil / sesutil.c
1 /*-
2  * Copyright (c) 2019 Klara Inc.
3  * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
4  * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org>
5  * Copyright (c) 2000 by Matthew Jacob
6  * All rights reserved.
7  *
8  * Portions of this software were developed by Edward Tomasz Napierala
9  * under sponsorship from Klara Inc.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer
16  *    in this position and unchanged.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/endian.h>
37 #include <sys/param.h>
38 #include <sys/disk.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <getopt.h>
46 #include <glob.h>
47 #include <stdbool.h>
48 #include <stddef.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <libxo/xo.h>
55
56 #include <cam/scsi/scsi_enc.h>
57
58 #include "eltsub.h"
59
60 #define SESUTIL_XO_VERSION      "1"
61
62 #define TEMPERATURE_OFFSET      20
63
64 #define PRINT_STYLE_DASHED      0
65 #define PRINT_STYLE_DASHED_2    1
66 #define PRINT_STYLE_CSV         2
67 #define PRINT_STYLE_CSV_2       3
68
69 static int encstatus(int argc, char **argv);
70 static int fault(int argc, char **argv);
71 static int locate(int argc, char **argv);
72 static int objmap(int argc, char **argv);
73 static int sesled(int argc, char **argv, bool fault);
74 static int show(int argc, char **argv);
75 static void sesutil_print(int *style, const char *fmt, ...) __printflike(2,3);
76
77 static struct command {
78         const char *name;
79         const char *param;
80         const char *desc;
81         int (*exec)(int argc, char **argv);
82 } cmds[] = {
83         { "fault",
84             "(<disk>|<sesid>|all) (on|off)",
85             "Change the state of the fault LED associated with a disk",
86             fault },
87         { "locate",
88             "(<disk>|<sesid>|all) (on|off)",
89             "Change the state of the locate LED associated with a disk",
90             locate },
91         { "map", "",
92             "Print a map of the devices managed by the enclosure", objmap } ,
93         { "show", "",
94             "Print a human-friendly summary of the enclosure", show } ,
95         { "status", "", "Print the status of the enclosure",
96             encstatus },
97 };
98
99 static const int nbcmds = nitems(cmds);
100 static const char *uflag;
101
102 static void
103 usage(FILE *out, const char *subcmd)
104 {
105         int i;
106
107         if (subcmd == NULL) {
108                 fprintf(out, "Usage: %s [-u /dev/ses<N>] <command> [options]\n",
109                     getprogname());
110                 fprintf(out, "Commands supported:\n");
111         }
112         for (i = 0; i < nbcmds; i++) {
113                 if (subcmd != NULL) {
114                         if (strcmp(subcmd, cmds[i].name) == 0) {
115                                 fprintf(out, "Usage: %s %s [-u /dev/ses<N>] "
116                                     "%s\n\t%s\n", getprogname(), subcmd,
117                                     cmds[i].param, cmds[i].desc);
118                                 break;
119                         }
120                         continue;
121                 }
122                 fprintf(out, "    %-12s%s\n\t\t%s\n\n", cmds[i].name,
123                     cmds[i].param, cmds[i].desc);
124         }
125
126         exit(EXIT_FAILURE);
127 }
128
129 static void
130 do_led(int fd, unsigned int idx, elm_type_t type, bool onoff, bool setfault)
131 {
132         int state = onoff ? 1 : 0;
133         encioc_elm_status_t o;
134         struct ses_ctrl_dev_slot *slot;
135
136         o.elm_idx = idx;
137         if (ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t) &o) < 0) {
138                 close(fd);
139                 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT");
140         }
141         ses_status_to_ctrl(type, &o.cstat[0]);
142         switch (type) {
143         case ELMTYP_DEVICE:
144         case ELMTYP_ARRAY_DEV:
145                 slot = (struct ses_ctrl_dev_slot *) &o.cstat[0];
146                 ses_ctrl_common_set_select(&slot->common, 1);
147                 if (setfault)
148                         ses_ctrl_dev_slot_set_rqst_fault(slot, state);
149                 else
150                         ses_ctrl_dev_slot_set_rqst_ident(slot, state);
151                 break;
152         default:
153                 return;
154         }
155         if (ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t) &o) < 0) {
156                 close(fd);
157                 xo_err(EXIT_FAILURE, "ENCIOC_SETELMSTAT");
158         }
159 }
160
161 static bool
162 disk_match(const char *devnames, const char *disk, size_t len)
163 {
164         const char *dname;
165
166         dname = devnames;
167         while ((dname = strstr(dname, disk)) != NULL) {
168                 if (dname[len] == '\0' || dname[len] == ',') {
169                         return (true);
170                 }
171                 dname++;
172         }
173
174         return (false);
175 }
176
177 static int
178 sesled(int argc, char **argv, bool setfault)
179 {
180         encioc_elm_devnames_t objdn;
181         encioc_element_t *objp;
182         glob_t g;
183         char *disk, *endptr;
184         size_t len, i, ndisks;
185         int fd;
186         unsigned int nobj, j, sesid;
187         bool all, isses, onoff;
188
189         isses = false;
190         all = false;
191         onoff = false;
192
193         if (argc != 3) {
194                 usage(stderr, (setfault ? "fault" : "locate"));
195         }
196
197         disk = argv[1];
198
199         sesid = strtoul(disk, &endptr, 10);
200         if (*endptr == '\0') {
201                 endptr = strrchr(uflag, '*');
202                 if (endptr != NULL && *endptr == '*') {
203                         xo_warnx("Must specifying a SES device (-u) to use a SES "
204                             "id# to identify a disk");
205                         usage(stderr, (setfault ? "fault" : "locate"));
206                 }
207                 isses = true;
208         }
209
210         if (strcmp(argv[2], "on") == 0) {
211                 onoff = true;
212         } else if (strcmp(argv[2], "off") == 0) {
213                 onoff = false;
214         } else {
215                 usage(stderr, (setfault ? "fault" : "locate"));
216         }
217
218         if (strcmp(disk, "all") == 0) {
219                 all = true;
220         }
221         len = strlen(disk);
222
223         /* Get the list of ses devices */
224         if (glob((uflag != NULL ? uflag : "/dev/ses[0-9]*"), 0, NULL, &g) ==
225             GLOB_NOMATCH) {
226                 globfree(&g);
227                 xo_errx(EXIT_FAILURE, "No SES devices found");
228         }
229
230         ndisks = 0;
231         for (i = 0; i < g.gl_pathc; i++) {
232                 /* ensure we only got numbers after ses */
233                 if (strspn(g.gl_pathv[i] + 8, "0123456789") !=
234                     strlen(g.gl_pathv[i] + 8)) {
235                         continue;
236                 }
237                 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) {
238                         /*
239                          * Don't treat non-access errors as critical if we are
240                          * accessing all devices
241                          */
242                         if (errno == EACCES && g.gl_pathc > 1) {
243                                 xo_err(EXIT_FAILURE, "unable to access SES device");
244                         }
245                         xo_warn("unable to access SES device: %s", g.gl_pathv[i]);
246                         continue;
247                 }
248
249                 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) {
250                         close(fd);
251                         xo_err(EXIT_FAILURE, "ENCIOC_GETNELM");
252                 }
253
254                 objp = calloc(nobj, sizeof(encioc_element_t));
255                 if (objp == NULL) {
256                         close(fd);
257                         xo_err(EXIT_FAILURE, "calloc()");
258                 }
259
260                 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) objp) < 0) {
261                         free(objp);
262                         close(fd);
263                         xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP");
264                 }
265
266                 if (isses) {
267                         if (sesid >= nobj) {
268                                 free(objp);
269                                 close(fd);
270                                 xo_errx(EXIT_FAILURE,
271                                      "Requested SES ID does not exist");
272                         }
273                         do_led(fd, sesid, objp[sesid].elm_type, onoff, setfault);
274                         ndisks++;
275                         free(objp);
276                         close(fd);
277                         break;
278                 }
279                 for (j = 0; j < nobj; j++) {
280                         const int devnames_size = 128;
281                         char devnames[devnames_size];
282
283                         if (all) {
284                                 encioc_elm_status_t es;
285                                 memset(&es, 0, sizeof(es));
286                                 es.elm_idx = objp[j].elm_idx;
287                                 if (ioctl(fd, ENCIOC_GETELMSTAT, &es) < 0) {
288                                         close(fd);
289                                         xo_err(EXIT_FAILURE,
290                                                 "ENCIOC_GETELMSTAT");
291                                 }
292                                 if ((es.cstat[0] & 0xf) == SES_OBJSTAT_NOACCESS)
293                                         continue;
294                                 do_led(fd, objp[j].elm_idx, objp[j].elm_type,
295                                     onoff, setfault);
296                                 continue;
297                         }
298                         memset(&objdn, 0, sizeof(objdn));
299                         memset(devnames, 0, devnames_size);
300                         objdn.elm_idx = objp[j].elm_idx;
301                         objdn.elm_names_size = devnames_size;
302                         objdn.elm_devnames = devnames;
303                         if (ioctl(fd, ENCIOC_GETELMDEVNAMES,
304                             (caddr_t) &objdn) <0) {
305                                 continue;
306                         }
307                         if (objdn.elm_names_len > 0) {
308                                 if (disk_match(objdn.elm_devnames, disk, len)) {
309                                         do_led(fd, objdn.elm_idx, objp[j].elm_type,
310                                             onoff, setfault);
311                                         ndisks++;
312                                         break;
313                                 }
314                         }
315                 }
316                 free(objp);
317                 close(fd);
318         }
319         globfree(&g);
320         if (ndisks == 0 && all == false) {
321                 xo_errx(EXIT_FAILURE, "Count not find the SES id of device '%s'",
322                     disk);
323         }
324
325         return (EXIT_SUCCESS);
326 }
327
328 static int
329 locate(int argc, char **argv)
330 {
331
332         return (sesled(argc, argv, false));
333 }
334
335 static int
336 fault(int argc, char **argv)
337 {
338
339         return (sesled(argc, argv, true));
340 }
341
342 static void
343 sesutil_print(int *style, const char *fmt, ...)
344 {
345         va_list args;
346
347         if (*style == PRINT_STYLE_DASHED) {
348                 xo_open_container("extra_status");
349                 xo_emit("\t\tExtra status:\n");
350                 *style = PRINT_STYLE_DASHED_2;
351         } else if (*style == PRINT_STYLE_CSV) {
352                 xo_open_container("extra_status");
353                 *style = PRINT_STYLE_CSV_2;
354         }
355
356         if (*style == PRINT_STYLE_DASHED_2)
357                 xo_emit("\t\t- ");
358         else if (*style == PRINT_STYLE_CSV_2)
359                 xo_emit(", ");
360         va_start(args, fmt);
361         xo_emit_hv(NULL, fmt, args);
362         va_end(args);
363         if (*style == PRINT_STYLE_DASHED_2)
364                 xo_emit("\n");
365 }
366
367 static void
368 print_extra_status(int eletype, u_char *cstat, int style)
369 {
370
371         if (cstat[0] & 0x40) {
372                 sesutil_print(&style, "{e:predicted_failure/true} Predicted Failure");
373         }
374         if (cstat[0] & 0x20) {
375                 sesutil_print(&style, "{e:disabled/true} Disabled");
376         }
377         if (cstat[0] & 0x10) {
378                 sesutil_print(&style, "{e:swapped/true} Swapped");
379         }
380         switch (eletype) {
381         case ELMTYP_DEVICE:
382         case ELMTYP_ARRAY_DEV:
383                 if (cstat[2] & 0x02) {
384                         sesutil_print(&style, "LED={q:led/locate}");
385                 }
386                 if (cstat[2] & 0x20) {
387                         sesutil_print(&style, "LED={q:led/fault}");
388                 }
389                 break;
390         case ELMTYP_FAN:
391                 sesutil_print(&style, "Speed: {:speed/%d}{Uw:rpm}",
392                     (((0x7 & cstat[1]) << 8) + cstat[2]) * 10);
393                 break;
394         case ELMTYP_THERM:
395                 if (cstat[2]) {
396                         sesutil_print(&style, "Temperature: {:temperature/%d}{Uw:C}",
397                             cstat[2] - TEMPERATURE_OFFSET);
398                 } else {
399                         sesutil_print(&style, "Temperature: -{q:temperature/reserved}");
400                 }
401                 break;
402         case ELMTYP_VOM:
403                 sesutil_print(&style, "Voltage: {:voltage/%.2f}{Uw:V}",
404                     be16dec(cstat + 2) / 100.0);
405                 break;
406         }
407         if (style) {
408                 xo_close_container("extra_status");
409         }
410 }
411
412 static int
413 objmap(int argc, char **argv __unused)
414 {
415         encioc_string_t stri;
416         encioc_elm_devnames_t e_devname;
417         encioc_elm_status_t e_status;
418         encioc_elm_desc_t e_desc;
419         encioc_element_t *e_ptr;
420         glob_t g;
421         int fd;
422         unsigned int j, nobj;
423         size_t i;
424         char str[32];
425
426         if (argc != 1) {
427                 usage(stderr, "map");
428         }
429
430         memset(&e_desc, 0, sizeof(e_desc));
431         /* SES4r02 allows element descriptors of up to 65536 characters */
432         e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char));
433         if (e_desc.elm_desc_str == NULL)
434                 xo_err(EXIT_FAILURE, "calloc()");
435
436         e_devname.elm_devnames = calloc(128, sizeof(char));
437         if (e_devname.elm_devnames == NULL)
438                 xo_err(EXIT_FAILURE, "calloc()");
439         e_devname.elm_names_size = 128;
440
441         /* Get the list of ses devices */
442         if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) {
443                 globfree(&g);
444                 xo_errx(EXIT_FAILURE, "No SES devices found");
445         }
446         xo_set_version(SESUTIL_XO_VERSION);
447         xo_open_container("sesutil");
448         xo_open_list("enclosures");
449         for (i = 0; i < g.gl_pathc; i++) {
450                 /* ensure we only got numbers after ses */
451                 if (strspn(g.gl_pathv[i] + 8, "0123456789") !=
452                     strlen(g.gl_pathv[i] + 8)) {
453                         continue;
454                 }
455                 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) {
456                         /*
457                          * Don't treat non-access errors as critical if we are
458                          * accessing all devices
459                          */
460                         if (errno == EACCES && g.gl_pathc > 1) {
461                                 xo_err(EXIT_FAILURE, "unable to access SES device");
462                         }
463                         xo_warn("unable to access SES device: %s", g.gl_pathv[i]);
464                         continue;
465                 }
466
467                 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) {
468                         close(fd);
469                         xo_err(EXIT_FAILURE, "ENCIOC_GETNELM");
470                 }
471
472                 e_ptr = calloc(nobj, sizeof(encioc_element_t));
473                 if (e_ptr == NULL) {
474                         close(fd);
475                         xo_err(EXIT_FAILURE, "calloc()");
476                 }
477
478                 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) {
479                         close(fd);
480                         xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP");
481                 }
482
483                 xo_open_instance("enclosures");
484                 xo_emit("{t:enc/%s}:\n", g.gl_pathv[i] + 5);
485                 stri.bufsiz = sizeof(str);
486                 stri.buf = &str[0];
487                 if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0)
488                         xo_emit("\tEnclosure Name: {t:name/%s}\n", stri.buf);
489                 stri.bufsiz = sizeof(str);
490                 stri.buf = &str[0];
491                 if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0)
492                         xo_emit("\tEnclosure ID: {t:id/%s}\n", stri.buf);
493
494                 xo_open_list("elements");
495                 for (j = 0; j < nobj; j++) {
496                         /* Get the status of the element */
497                         memset(&e_status, 0, sizeof(e_status));
498                         e_status.elm_idx = e_ptr[j].elm_idx;
499                         if (ioctl(fd, ENCIOC_GETELMSTAT,
500                             (caddr_t) &e_status) < 0) {
501                                 close(fd);
502                                 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT");
503                         }
504                         /* Get the description of the element */
505                         e_desc.elm_idx = e_ptr[j].elm_idx;
506                         e_desc.elm_desc_len = UINT16_MAX;
507                         if (ioctl(fd, ENCIOC_GETELMDESC,
508                             (caddr_t) &e_desc) < 0) {
509                                 close(fd);
510                                 xo_err(EXIT_FAILURE, "ENCIOC_GETELMDESC");
511                         }
512                         e_desc.elm_desc_str[e_desc.elm_desc_len] = '\0';
513                         /* Get the device name(s) of the element */
514                         e_devname.elm_idx = e_ptr[j].elm_idx;
515                         if (ioctl(fd, ENCIOC_GETELMDEVNAMES,
516                             (caddr_t) &e_devname) <0) {
517                                 /* Continue even if we can't look up devnames */
518                                 e_devname.elm_devnames[0] = '\0';
519                         }
520                         xo_open_instance("elements");
521                         xo_emit("\tElement {:id/%u}, Type: {:type/%s}\n", e_ptr[j].elm_idx,
522                             geteltnm(e_ptr[j].elm_type));
523                         xo_emit("\t\tStatus: {:status/%s} ({q:status_code/0x%02x 0x%02x 0x%02x 0x%02x})\n",
524                             scode2ascii(e_status.cstat[0]), e_status.cstat[0],
525                             e_status.cstat[1], e_status.cstat[2],
526                             e_status.cstat[3]);
527                         if (e_desc.elm_desc_len > 0) {
528                                 xo_emit("\t\tDescription: {:description/%s}\n",
529                                     e_desc.elm_desc_str);
530                         }
531                         if (e_devname.elm_names_len > 0) {
532                                 xo_emit("\t\tDevice Names: {:device_names/%s}\n",
533                                     e_devname.elm_devnames);
534                         }
535                         print_extra_status(e_ptr[j].elm_type, e_status.cstat, PRINT_STYLE_DASHED);
536                         xo_close_instance("elements");
537                 }
538                 xo_close_list("elements");
539                 free(e_ptr);
540                 close(fd);
541         }
542         globfree(&g);
543         free(e_devname.elm_devnames);
544         free(e_desc.elm_desc_str);
545         xo_close_list("enclosures");
546         xo_close_container("sesutil");
547         xo_finish();
548
549         return (EXIT_SUCCESS);
550 }
551
552 /*
553  * Get rid of the 'passN' devices, unless there's nothing else to show.
554  */
555 static void
556 skip_pass_devices(char *devnames, size_t devnameslen)
557 {
558         char *dev, devs[128], passes[128], *tmp;
559
560         devs[0] = passes[0] = '\0';
561         tmp = devnames;
562
563         while ((dev = strsep(&tmp, ",")) != NULL) {
564                 if (strncmp(dev, "pass", 4) == 0) {
565                         if (passes[0] != '\0')
566                                 strlcat(passes, ",", sizeof(passes));
567                         strlcat(passes, dev, sizeof(passes));
568                 } else {
569                         if (devs[0] != '\0')
570                                 strlcat(devs, ",", sizeof(devs));
571                         strlcat(devs, dev, sizeof(devs));
572                 }
573         }
574         strlcpy(devnames, devs, devnameslen);
575         if (devnames[0] == '\0')
576                 strlcpy(devnames, passes, devnameslen);
577 }
578
579 static void
580 fetch_device_details(char *devnames, char **model, char **serial, off_t *size)
581 {
582         char ident[DISK_IDENT_SIZE];
583         struct diocgattr_arg arg;
584         char *tmp;
585         off_t mediasize;
586         int comma;
587         int fd;
588
589         comma = (int)strcspn(devnames, ",");
590         asprintf(&tmp, "/dev/%.*s", comma, devnames);
591         if (tmp == NULL)
592                 err(1, "asprintf");
593         fd = open(tmp, O_RDONLY);
594         free(tmp);
595         if (fd < 0) {
596                 /*
597                  * This can happen with a disk so broken it cannot
598                  * be probed by GEOM.
599                  */
600                 *model = strdup("?");
601                 *serial = strdup("?");
602                 *size = -1;
603                 close(fd);
604                 return;
605         }
606
607         strlcpy(arg.name, "GEOM::descr", sizeof(arg.name));
608         arg.len = sizeof(arg.value.str);
609         if (ioctl(fd, DIOCGATTR, &arg) == 0)
610                 *model = strdup(arg.value.str);
611         else
612                 *model = NULL;
613
614         if (ioctl(fd, DIOCGIDENT, ident) == 0)
615                 *serial = strdup(ident);
616         else
617                 *serial = NULL;
618
619         if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0)
620                 *size = mediasize;
621         else
622                 *size = -1;
623         close(fd);
624 }
625
626 static void
627 show_device(int fd, int elm_idx, encioc_elm_status_t e_status, encioc_elm_desc_t e_desc)
628 {
629         encioc_elm_devnames_t e_devname;
630         char *model = NULL, *serial = NULL;
631         off_t size;
632
633         /* Get the device name(s) of the element */
634         memset(&e_devname, 0, sizeof(e_devname));
635         e_devname.elm_idx = elm_idx;
636         e_devname.elm_names_size = 128;
637         e_devname.elm_devnames = calloc(128, sizeof(char));
638         if (e_devname.elm_devnames == NULL) {
639                 close(fd);
640                 xo_err(EXIT_FAILURE, "calloc()");
641         }
642
643         if (ioctl(fd, ENCIOC_GETELMDEVNAMES,
644             (caddr_t) &e_devname) < 0) {
645                 /* We don't care if this fails */
646                 e_devname.elm_devnames[0] = '\0';
647                 size = -1;
648         } else {
649                 skip_pass_devices(e_devname.elm_devnames, 128);
650                 fetch_device_details(e_devname.elm_devnames, &model, &serial, &size);
651         }
652         xo_open_instance("elements");
653         xo_emit("{e:type/device_slot}");
654         xo_emit("{d:description/%-15s} ", e_desc.elm_desc_len > 0 ? e_desc.elm_desc_str : "-");
655         xo_emit("{e:description/%-15s}", e_desc.elm_desc_len > 0 ? e_desc.elm_desc_str : "");
656         xo_emit("{d:device_names/%-7s} ", e_devname.elm_names_len > 0 ? e_devname.elm_devnames : "-");
657         xo_emit("{e:device_names/%s}", e_devname.elm_names_len > 0 ? e_devname.elm_devnames : "");
658         xo_emit("{d:model/%-25s} ", model ? model : "-");
659         xo_emit("{e:model/%s}", model ? model : "");
660         xo_emit("{d:serial/%-20s} ", serial != NULL ? serial : "-");
661         xo_emit("{e:serial/%s}", serial != NULL ? serial : "");
662         if ((e_status.cstat[0] & 0xf) == SES_OBJSTAT_OK && size >= 0) {
663                 xo_emit("{h,hn-1000:size/%ld}{e:status/%s}",
664                     size, scode2ascii(e_status.cstat[0]));
665         } else {
666                 xo_emit("{:status/%s}", scode2ascii(e_status.cstat[0]));
667         }
668         print_extra_status(ELMTYP_ARRAY_DEV, e_status.cstat, PRINT_STYLE_CSV);
669         xo_emit("\n");
670         xo_close_instance("elements");
671         free(serial);
672         free(model);
673         free(e_devname.elm_devnames);
674 }
675
676 static void
677 show_therm(encioc_elm_status_t e_status, encioc_elm_desc_t e_desc)
678 {
679
680         if (e_desc.elm_desc_len <= 0) {
681                 /* We don't have a label to display; might as well skip it. */
682                 return;
683         }
684
685         if (e_status.cstat[2] == 0) {
686                 /* No temperature to show. */
687                 return;
688         }
689
690         xo_open_instance("elements");
691         xo_emit("{e:type/temperature_sensor}");
692         xo_emit("{:description/%s}: {:temperature/%d}{Uw:C}",
693             e_desc.elm_desc_str, e_status.cstat[2] - TEMPERATURE_OFFSET);
694         xo_close_instance("elements");
695 }
696
697 static void
698 show_vom(encioc_elm_status_t e_status, encioc_elm_desc_t e_desc)
699 {
700
701         if (e_desc.elm_desc_len <= 0) {
702                 /* We don't have a label to display; might as well skip it. */
703                 return;
704         }
705
706         if (e_status.cstat[2] == 0) {
707                 /* No voltage to show. */
708                 return;
709         }
710
711         xo_open_instance("elements");
712         xo_emit("{e:type/voltage_sensor}");
713         xo_emit("{:description/%s}: {:voltage/%.2f}{Uw:V}",
714             e_desc.elm_desc_str, be16dec(e_status.cstat + 2) / 100.0);
715         xo_close_instance("elements");
716 }
717
718 static int
719 show(int argc, char **argv __unused)
720 {
721         encioc_string_t stri;
722         encioc_elm_status_t e_status;
723         encioc_elm_desc_t e_desc;
724         encioc_element_t *e_ptr;
725         glob_t g;
726         elm_type_t prev_type;
727         int fd;
728         unsigned int j, nobj;
729         size_t i;
730         bool first_ses;
731         char str[32];
732
733         if (argc != 1) {
734                 usage(stderr, "map");
735         }
736
737         first_ses = true;
738
739         e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char));
740         if (e_desc.elm_desc_str == NULL)
741                 xo_err(EXIT_FAILURE, "calloc()");
742
743         /* Get the list of ses devices */
744         if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) {
745                 globfree(&g);
746                 xo_errx(EXIT_FAILURE, "No SES devices found");
747         }
748         xo_set_version(SESUTIL_XO_VERSION);
749         xo_open_container("sesutil");
750         xo_open_list("enclosures");
751         for (i = 0; i < g.gl_pathc; i++) {
752                 /* ensure we only got numbers after ses */
753                 if (strspn(g.gl_pathv[i] + 8, "0123456789") !=
754                     strlen(g.gl_pathv[i] + 8)) {
755                         continue;
756                 }
757                 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) {
758                         /*
759                          * Don't treat non-access errors as critical if we are
760                          * accessing all devices
761                          */
762                         if (errno == EACCES && g.gl_pathc > 1) {
763                                 xo_err(EXIT_FAILURE, "unable to access SES device");
764                         }
765                         xo_warn("unable to access SES device: %s", g.gl_pathv[i]);
766                         continue;
767                 }
768
769                 if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) {
770                         close(fd);
771                         xo_err(EXIT_FAILURE, "ENCIOC_GETNELM");
772                 }
773
774                 e_ptr = calloc(nobj, sizeof(encioc_element_t));
775                 if (e_ptr == NULL) {
776                         close(fd);
777                         xo_err(EXIT_FAILURE, "calloc()");
778                 }
779
780                 if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) {
781                         close(fd);
782                         xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP");
783                 }
784
785                 xo_open_instance("enclosures");
786
787                 if (first_ses)
788                         first_ses = false;
789                 else
790                         xo_emit("\n");
791
792                 xo_emit("{t:enc/%s}: ", g.gl_pathv[i] + 5);
793                 stri.bufsiz = sizeof(str);
794                 stri.buf = &str[0];
795                 if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0)
796                         xo_emit("<{t:name/%s}>; ", stri.buf);
797                 stri.bufsiz = sizeof(str);
798                 stri.buf = &str[0];
799                 if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0)
800                         xo_emit("ID: {t:id/%s}", stri.buf);
801                 xo_emit("\n");
802
803                 xo_open_list("elements");
804                 prev_type = -1;
805                 for (j = 0; j < nobj; j++) {
806                         /* Get the status of the element */
807                         memset(&e_status, 0, sizeof(e_status));
808                         e_status.elm_idx = e_ptr[j].elm_idx;
809                         if (ioctl(fd, ENCIOC_GETELMSTAT,
810                             (caddr_t) &e_status) < 0) {
811                                 close(fd);
812                                 xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT");
813                         }
814
815                         /*
816                          * Skip "Unsupported" elements; those usually precede
817                          * the actual device entries and are not particularly
818                          * interesting.
819                          */
820                         if (e_status.cstat[0] == SES_OBJSTAT_UNSUPPORTED)
821                                 continue;
822
823                         /* Get the description of the element */
824                         e_desc.elm_idx = e_ptr[j].elm_idx;
825                         e_desc.elm_desc_len = UINT16_MAX;
826                         if (ioctl(fd, ENCIOC_GETELMDESC,
827                             (caddr_t) &e_desc) < 0) {
828                                 close(fd);
829                                 xo_err(EXIT_FAILURE, "ENCIOC_GETELMDESC");
830                         }
831                         e_desc.elm_desc_str[e_desc.elm_desc_len] = '\0';
832
833                         switch (e_ptr[j].elm_type) {
834                         case ELMTYP_DEVICE:
835                         case ELMTYP_ARRAY_DEV:
836                                 if (e_ptr[j].elm_type != prev_type)
837                                         xo_emit("Desc            Dev     Model                     Ident                Size/Status\n");
838
839                                 show_device(fd, e_ptr[j].elm_idx, e_status, e_desc);
840                                 prev_type = e_ptr[j].elm_type;
841                                 break;
842                         case ELMTYP_THERM:
843                                 if (e_ptr[j].elm_type != prev_type)
844                                         xo_emit("\nTemperatures: ");
845                                 else
846                                         xo_emit(", ");
847                                 prev_type = e_ptr[j].elm_type;
848                                 show_therm(e_status, e_desc);
849                                 break;
850                         case ELMTYP_VOM:
851                                 if (e_ptr[j].elm_type != prev_type)
852                                         xo_emit("\nVoltages: ");
853                                 else
854                                         xo_emit(", ");
855                                 prev_type = e_ptr[j].elm_type;
856                                 show_vom(e_status, e_desc);
857                                 break;
858                         default:
859                                 /*
860                                  * Ignore stuff not interesting to the user.
861                                  */
862                                 break;
863                         }
864                 }
865                 if (prev_type != (elm_type_t)-1 &&
866                     prev_type != ELMTYP_DEVICE && prev_type != ELMTYP_ARRAY_DEV)
867                         xo_emit("\n");
868                 xo_close_list("elements");
869                 free(e_ptr);
870                 close(fd);
871         }
872         globfree(&g);
873         free(e_desc.elm_desc_str);
874         xo_close_list("enclosures");
875         xo_close_container("sesutil");
876         xo_finish();
877
878         return (EXIT_SUCCESS);
879 }
880
881 static int
882 encstatus(int argc, char **argv __unused)
883 {
884         glob_t g;
885         int fd, status;
886         size_t i, e;
887         u_char estat;
888
889         status = 0;
890         if (argc != 1) {
891                 usage(stderr, "status");
892         }
893
894         /* Get the list of ses devices */
895         if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) {
896                 globfree(&g);
897                 xo_errx(EXIT_FAILURE, "No SES devices found");
898         }
899
900         xo_set_version(SESUTIL_XO_VERSION);
901         xo_open_container("sesutil");
902         xo_open_list("enclosures");
903         for (i = 0; i < g.gl_pathc; i++) {
904                 /* ensure we only got numbers after ses */
905                 if (strspn(g.gl_pathv[i] + 8, "0123456789") !=
906                     strlen(g.gl_pathv[i] + 8)) {
907                         continue;
908                 }
909                 if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) {
910                         /*
911                          * Don't treat non-access errors as critical if we are
912                          * accessing all devices
913                          */
914                         if (errno == EACCES && g.gl_pathc > 1) {
915                                 xo_err(EXIT_FAILURE, "unable to access SES device");
916                         }
917                         xo_warn("unable to access SES device: %s", g.gl_pathv[i]);
918                         continue;
919                 }
920
921                 if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat) < 0) {
922                         xo_err(EXIT_FAILURE, "ENCIOC_GETENCSTAT");
923                         close(fd);
924                 }
925
926                 xo_open_instance("enclosures");
927                 xo_emit("{:enc/%s}: ", g.gl_pathv[i] + 5);
928                 e = 0;
929                 if (estat == 0) {
930                         if (status == 0) {
931                                 status = 1;
932                         }
933                         xo_emit("{q:status/OK}");
934                 } else {
935                         if (estat & SES_ENCSTAT_INFO) {
936                                 xo_emit("{lq:status/INFO}");
937                                 e++;
938                         }
939                         if (estat & SES_ENCSTAT_NONCRITICAL) {
940                                 if (e)
941                                         xo_emit(",");
942                                 xo_emit("{lq:status/NONCRITICAL}");
943                                 e++;
944                         }
945                         if (estat & SES_ENCSTAT_CRITICAL) {
946                                 if (e)
947                                         xo_emit(",");
948                                 xo_emit("{lq:status/CRITICAL}");
949                                 e++;
950                                 status = -1;
951                         }
952                         if (estat & SES_ENCSTAT_UNRECOV) {
953                                 if (e)
954                                         xo_emit(",");
955                                 xo_emit("{lq:status/UNRECOV}");
956                                 e++;
957                                 status = -1;
958                         }
959                 }
960                 xo_close_instance("enclosures");
961                 xo_emit("\n");
962                 close(fd);
963         }
964         globfree(&g);
965
966         xo_close_list("enclosures");
967         xo_close_container("sesutil");
968         xo_finish();
969
970         if (status == 1) {
971                 return (EXIT_SUCCESS);
972         } else {
973                 return (EXIT_FAILURE);
974         }
975 }
976
977 int
978 main(int argc, char **argv)
979 {
980         int i, ch;
981         struct command *cmd = NULL;
982
983         argc = xo_parse_args(argc, argv);
984         if (argc < 0)
985                 exit(1);
986
987         uflag = "/dev/ses[0-9]*";
988         while ((ch = getopt_long(argc, argv, "u:", NULL, NULL)) != -1) {
989                 switch (ch) {
990                 case 'u':
991                         uflag = optarg;
992                         break;
993                 case '?':
994                 default:
995                         usage(stderr, NULL);
996                 }
997         }
998         argc -= optind;
999         argv += optind;
1000
1001         if (argc < 1) {
1002                 warnx("Missing command");
1003                 usage(stderr, NULL);
1004         }
1005
1006         for (i = 0; i < nbcmds; i++) {
1007                 if (strcmp(argv[0], cmds[i].name) == 0) {
1008                         cmd = &cmds[i];
1009                         break;
1010                 }
1011         }
1012
1013         if (cmd == NULL) {
1014                 warnx("unknown command %s", argv[0]);
1015                 usage(stderr, NULL);
1016         }
1017
1018         return (cmd->exec(argc, argv));
1019 }