1287473Sbapt/*-
2287473Sbapt * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
3287988Sallanjude * Copyright (c) 2015 Allan Jude <allanjude@FreeBSD.org>
4287988Sallanjude * Copyright (c) 2000 by Matthew Jacob
5287473Sbapt * All rights reserved.
6287473Sbapt *
7287473Sbapt * Redistribution and use in source and binary forms, with or without
8287473Sbapt * modification, are permitted provided that the following conditions
9287473Sbapt * are met:
10287473Sbapt * 1. Redistributions of source code must retain the above copyright
11287473Sbapt *    notice, this list of conditions and the following disclaimer
12287473Sbapt *    in this position and unchanged.
13287473Sbapt * 2. Redistributions in binary form must reproduce the above copyright
14287473Sbapt *    notice, this list of conditions and the following disclaimer in the
15287473Sbapt *    documentation and/or other materials provided with the distribution.
16287473Sbapt *
17287473Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18287473Sbapt * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19287473Sbapt * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20287473Sbapt * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21287473Sbapt * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22287473Sbapt * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23287473Sbapt * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24287473Sbapt * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25287473Sbapt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26287473Sbapt * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27287473Sbapt */
28287473Sbapt
29287473Sbapt#include <sys/cdefs.h>
30287473Sbapt__FBSDID("$FreeBSD: stable/11/usr.sbin/sesutil/sesutil.c 359198 2020-03-22 01:01:47Z asomers $");
31287473Sbapt
32321287Sbapt#include <sys/endian.h>
33287473Sbapt#include <sys/param.h>
34287473Sbapt#include <sys/ioctl.h>
35292121Sbapt#include <sys/types.h>
36287473Sbapt
37287473Sbapt#include <err.h>
38287473Sbapt#include <errno.h>
39287473Sbapt#include <fcntl.h>
40287988Sallanjude#include <getopt.h>
41287473Sbapt#include <glob.h>
42287473Sbapt#include <stdbool.h>
43287473Sbapt#include <stddef.h>
44287473Sbapt#include <stdint.h>
45287473Sbapt#include <stdio.h>
46287473Sbapt#include <stdlib.h>
47287473Sbapt#include <string.h>
48287473Sbapt#include <unistd.h>
49321287Sbapt#include <libxo/xo.h>
50287473Sbapt
51287473Sbapt#include <cam/scsi/scsi_enc.h>
52287473Sbapt
53287988Sallanjude#include "eltsub.h"
54287988Sallanjude
55321287Sbapt#define SESUTIL_XO_VERSION	"1"
56321287Sbapt
57287988Sallanjudestatic int encstatus(int argc, char **argv);
58287988Sallanjudestatic int fault(int argc, char **argv);
59287473Sbaptstatic int locate(int argc, char **argv);
60287988Sallanjudestatic int objmap(int argc, char **argv);
61287988Sallanjudestatic int sesled(int argc, char **argv, bool fault);
62321287Sbaptstatic void sesutil_print(bool *title, const char *fmt, ...) __printflike(2,3);
63287473Sbapt
64287473Sbaptstatic struct command {
65287473Sbapt	const char *name;
66287988Sallanjude	const char *param;
67287473Sbapt	const char *desc;
68287473Sbapt	int (*exec)(int argc, char **argv);
69287473Sbapt} cmds[] = {
70287988Sallanjude	{ "fault",
71287988Sallanjude	    "(<disk>|<sesid>|all) (on|off)",
72287988Sallanjude	    "Change the state of the fault LED associated with a disk",
73287988Sallanjude	    fault },
74287988Sallanjude	{ "locate",
75287988Sallanjude	    "(<disk>|<sesid>|all) (on|off)",
76287988Sallanjude	    "Change the state of the locate LED associated with a disk",
77287988Sallanjude	    locate },
78287988Sallanjude	{ "map", "",
79287988Sallanjude	    "Print a map of the devices managed by the enclosure", objmap } ,
80287988Sallanjude	{ "status", "", "Print the status of the enclosure",
81287988Sallanjude	    encstatus },
82287473Sbapt};
83287473Sbapt
84287473Sbaptstatic const int nbcmds = nitems(cmds);
85287988Sallanjudestatic const char *uflag;
86287473Sbapt
87287473Sbaptstatic void
88287988Sallanjudeusage(FILE *out, const char *subcmd)
89287473Sbapt{
90287988Sallanjude	int i;
91287988Sallanjude
92287988Sallanjude	if (subcmd == NULL) {
93287988Sallanjude		fprintf(out, "Usage: %s [-u /dev/ses<N>] <command> [options]\n",
94287988Sallanjude		    getprogname());
95287988Sallanjude		fprintf(out, "Commands supported:\n");
96287988Sallanjude	}
97287988Sallanjude	for (i = 0; i < nbcmds; i++) {
98287988Sallanjude		if (subcmd != NULL) {
99287988Sallanjude			if (strcmp(subcmd, cmds[i].name) == 0) {
100287988Sallanjude				fprintf(out, "Usage: %s %s [-u /dev/ses<N>] "
101287988Sallanjude				    "%s\n\t%s\n", getprogname(), subcmd,
102287988Sallanjude				    cmds[i].param, cmds[i].desc);
103287988Sallanjude				break;
104287988Sallanjude			}
105287988Sallanjude			continue;
106287988Sallanjude		}
107287988Sallanjude		fprintf(out, "    %-12s%s\n\t\t%s\n\n", cmds[i].name,
108287988Sallanjude		    cmds[i].param, cmds[i].desc);
109287988Sallanjude	}
110287988Sallanjude
111287988Sallanjude	exit(EXIT_FAILURE);
112287988Sallanjude}
113287988Sallanjude
114287988Sallanjudestatic void
115344007Smavdo_led(int fd, unsigned int idx, elm_type_t type, bool onoff, bool setfault)
116287988Sallanjude{
117344007Smav	int state = onoff ? 1 : 0;
118287473Sbapt	encioc_elm_status_t o;
119344007Smav	struct ses_ctrl_dev_slot *slot;
120287473Sbapt
121287473Sbapt	o.elm_idx = idx;
122287473Sbapt	if (ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t) &o) < 0) {
123287473Sbapt		close(fd);
124321287Sbapt		xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT");
125287473Sbapt	}
126344007Smav	slot = (struct ses_ctrl_dev_slot *) &o.cstat[0];
127344007Smav	switch (type) {
128344007Smav	case ELMTYP_DEVICE:
129344007Smav	case ELMTYP_ARRAY_DEV:
130344007Smav		ses_ctrl_common_set_select(&slot->common, 1);
131344007Smav		if (setfault)
132344007Smav			ses_ctrl_dev_slot_set_rqst_fault(slot, state);
133306796Smav		else
134344007Smav			ses_ctrl_dev_slot_set_rqst_ident(slot, state);
135344007Smav		break;
136344007Smav	default:
137344007Smav		return;
138287988Sallanjude	}
139287473Sbapt	if (ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t) &o) < 0) {
140287473Sbapt		close(fd);
141321287Sbapt		xo_err(EXIT_FAILURE, "ENCIOC_SETELMSTAT");
142287473Sbapt	}
143287473Sbapt}
144287473Sbapt
145287473Sbaptstatic bool
146287473Sbaptdisk_match(const char *devnames, const char *disk, size_t len)
147287473Sbapt{
148287494Sbapt	const char *dname;
149287473Sbapt
150287494Sbapt	dname = devnames;
151287494Sbapt	while ((dname = strstr(dname, disk)) != NULL) {
152287988Sallanjude		if (dname[len] == '\0' || dname[len] == ',') {
153287473Sbapt			return (true);
154287988Sallanjude		}
155287494Sbapt		dname++;
156287473Sbapt	}
157287988Sallanjude
158287473Sbapt	return (false);
159287473Sbapt}
160287473Sbapt
161287473Sbaptstatic int
162287992Sallanjudesesled(int argc, char **argv, bool setfault)
163287473Sbapt{
164287473Sbapt	encioc_elm_devnames_t objdn;
165287473Sbapt	encioc_element_t *objp;
166287473Sbapt	glob_t g;
167287988Sallanjude	char *disk, *endptr;
168287988Sallanjude	size_t len, i, ndisks;
169287988Sallanjude	int fd;
170287988Sallanjude	unsigned int nobj, j, sesid;
171287988Sallanjude	bool all, isses, onoff;
172287473Sbapt
173287988Sallanjude	isses = false;
174287988Sallanjude	all = false;
175287988Sallanjude	onoff = false;
176287988Sallanjude
177287988Sallanjude	if (argc != 3) {
178287992Sallanjude		usage(stderr, (setfault ? "fault" : "locate"));
179287473Sbapt	}
180287473Sbapt
181287988Sallanjude	disk = argv[1];
182287473Sbapt
183287988Sallanjude	sesid = strtoul(disk, &endptr, 10);
184287988Sallanjude	if (*endptr == '\0') {
185287988Sallanjude		endptr = strrchr(uflag, '*');
186292122Sbapt		if (endptr != NULL && *endptr == '*') {
187321287Sbapt			xo_warnx("Must specifying a SES device (-u) to use a SES "
188287988Sallanjude			    "id# to identify a disk");
189287992Sallanjude			usage(stderr, (setfault ? "fault" : "locate"));
190287988Sallanjude		}
191287988Sallanjude		isses = true;
192287988Sallanjude	}
193287988Sallanjude
194287988Sallanjude	if (strcmp(argv[2], "on") == 0) {
195287485Sbapt		onoff = true;
196287988Sallanjude	} else if (strcmp(argv[2], "off") == 0) {
197287485Sbapt		onoff = false;
198287473Sbapt	} else {
199287992Sallanjude		usage(stderr, (setfault ? "fault" : "locate"));
200287473Sbapt	}
201287473Sbapt
202287473Sbapt	if (strcmp(disk, "all") == 0) {
203287473Sbapt		all = true;
204287473Sbapt	}
205287473Sbapt	len = strlen(disk);
206287473Sbapt
207287473Sbapt	/* Get the list of ses devices */
208287988Sallanjude	if (glob((uflag != NULL ? uflag : "/dev/ses[0-9]*"), 0, NULL, &g) ==
209287988Sallanjude	    GLOB_NOMATCH) {
210287473Sbapt		globfree(&g);
211321287Sbapt		xo_errx(EXIT_FAILURE, "No SES devices found");
212287473Sbapt	}
213287988Sallanjude
214287988Sallanjude	ndisks = 0;
215287473Sbapt	for (i = 0; i < g.gl_pathc; i++) {
216287473Sbapt		/* ensure we only got numbers after ses */
217287473Sbapt		if (strspn(g.gl_pathv[i] + 8, "0123456789") !=
218287988Sallanjude		    strlen(g.gl_pathv[i] + 8)) {
219287473Sbapt			continue;
220287988Sallanjude		}
221287473Sbapt		if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) {
222287988Sallanjude			/*
223287988Sallanjude			 * Don't treat non-access errors as critical if we are
224287988Sallanjude			 * accessing all devices
225287988Sallanjude			 */
226287988Sallanjude			if (errno == EACCES && g.gl_pathc > 1) {
227321287Sbapt				xo_err(EXIT_FAILURE, "unable to access SES device");
228287988Sallanjude			}
229321287Sbapt			xo_warn("unable to access SES device: %s", g.gl_pathv[i]);
230287988Sallanjude			continue;
231287473Sbapt		}
232287473Sbapt
233287988Sallanjude		if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) {
234287988Sallanjude			close(fd);
235321287Sbapt			xo_err(EXIT_FAILURE, "ENCIOC_GETNELM");
236287988Sallanjude		}
237287473Sbapt
238287473Sbapt		objp = calloc(nobj, sizeof(encioc_element_t));
239287988Sallanjude		if (objp == NULL) {
240287988Sallanjude			close(fd);
241321287Sbapt			xo_err(EXIT_FAILURE, "calloc()");
242287988Sallanjude		}
243287473Sbapt
244287988Sallanjude		if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) objp) < 0) {
245359198Sasomers			free(objp);
246287988Sallanjude			close(fd);
247321287Sbapt			xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP");
248287988Sallanjude		}
249287473Sbapt
250287988Sallanjude		if (isses) {
251359198Sasomers			if (sesid >= nobj) {
252359198Sasomers				free(objp);
253287988Sallanjude				close(fd);
254321287Sbapt				xo_errx(EXIT_FAILURE,
255287988Sallanjude				     "Requested SES ID does not exist");
256287988Sallanjude			}
257344007Smav			do_led(fd, sesid, objp[sesid].elm_type, onoff, setfault);
258287988Sallanjude			ndisks++;
259359198Sasomers			free(objp);
260287988Sallanjude			close(fd);
261287988Sallanjude			break;
262287988Sallanjude		}
263287473Sbapt		for (j = 0; j < nobj; j++) {
264359198Sasomers			const int devnames_size = 128;
265359198Sasomers			char devnames[devnames_size];
266359198Sasomers
267319901Sallanjude			if (all) {
268344007Smav				do_led(fd, objp[j].elm_idx, objp[j].elm_type,
269344007Smav				    onoff, setfault);
270319901Sallanjude				continue;
271319901Sallanjude			}
272287473Sbapt			memset(&objdn, 0, sizeof(objdn));
273359198Sasomers			memset(devnames, 0, devnames_size);
274287473Sbapt			objdn.elm_idx = objp[j].elm_idx;
275359198Sasomers			objdn.elm_names_size = devnames_size;
276359198Sasomers			objdn.elm_devnames = devnames;
277287473Sbapt			if (ioctl(fd, ENCIOC_GETELMDEVNAMES,
278287988Sallanjude			    (caddr_t) &objdn) <0) {
279287473Sbapt				continue;
280287988Sallanjude			}
281287473Sbapt			if (objdn.elm_names_len > 0) {
282287473Sbapt				if (disk_match(objdn.elm_devnames, disk, len)) {
283344007Smav					do_led(fd, objdn.elm_idx, objp[j].elm_type,
284287992Sallanjude					    onoff, setfault);
285287988Sallanjude					ndisks++;
286287473Sbapt					break;
287287473Sbapt				}
288287473Sbapt			}
289287988Sallanjude		}
290298382Sbapt		free(objp);
291287473Sbapt		close(fd);
292287473Sbapt	}
293287473Sbapt	globfree(&g);
294287988Sallanjude	if (ndisks == 0 && all == false) {
295321287Sbapt		xo_errx(EXIT_FAILURE, "Count not find the SES id of device '%s'",
296287988Sallanjude		    disk);
297287988Sallanjude	}
298287473Sbapt
299287473Sbapt	return (EXIT_SUCCESS);
300287473Sbapt}
301287473Sbapt
302287988Sallanjudestatic int
303287988Sallanjudelocate(int argc, char **argv)
304287473Sbapt{
305287473Sbapt
306287988Sallanjude	return (sesled(argc, argv, false));
307287473Sbapt}
308287473Sbapt
309287988Sallanjudestatic int
310287988Sallanjudefault(int argc, char **argv)
311287988Sallanjude{
312287988Sallanjude
313287988Sallanjude	return (sesled(argc, argv, true));
314287988Sallanjude}
315287988Sallanjude
316321287Sbapt#define TEMPERATURE_OFFSET 20
317321287Sbaptstatic void
318321287Sbaptsesutil_print(bool *title, const char *fmt, ...)
319321287Sbapt{
320321287Sbapt	va_list args;
321321287Sbapt
322321287Sbapt	if (!*title) {
323321287Sbapt		xo_open_container("extra_status");
324321287Sbapt		xo_emit("\t\tExtra status:\n");
325321287Sbapt		*title = true;
326321287Sbapt	}
327321287Sbapt	va_start(args, fmt);
328321287Sbapt	xo_emit_hv(NULL, fmt, args);
329321287Sbapt	va_end(args);
330321287Sbapt}
331321287Sbapt
332321287Sbaptstatic void
333321287Sbaptprint_extra_status(int eletype, u_char *cstat)
334321287Sbapt{
335321287Sbapt	bool title = false;
336321287Sbapt
337321287Sbapt	if (cstat[0] & 0x40) {
338321287Sbapt		sesutil_print(&title, "\t\t-{e:predicted_failure/true} Predicted Failure\n");
339321287Sbapt	}
340321287Sbapt	if (cstat[0] & 0x20) {
341321287Sbapt		sesutil_print(&title, "\t\t-{e:disabled/true} Disabled\n");
342321287Sbapt	}
343321287Sbapt	if (cstat[0] & 0x10) {
344321287Sbapt		sesutil_print(&title, "\t\t-{e:swapped/true} Swapped\n");
345321287Sbapt	}
346321287Sbapt	switch (eletype) {
347321287Sbapt	case ELMTYP_DEVICE:
348321287Sbapt	case ELMTYP_ARRAY_DEV:
349321287Sbapt		if (cstat[2] & 0x02) {
350321287Sbapt			sesutil_print(&title, "\t\t- LED={q:led/locate}\n");
351321287Sbapt		}
352321287Sbapt		if (cstat[2] & 0x20) {
353321287Sbapt			sesutil_print(&title, "\t\t- LED={q:led/fault}\n");
354321287Sbapt		}
355321287Sbapt		break;
356321287Sbapt	case ELMTYP_FAN:
357321287Sbapt		sesutil_print(&title, "\t\t- Speed: {:speed/%d}{Uw:rpm}\n",
358321287Sbapt		    (((0x7 & cstat[1]) << 8) + cstat[2]) * 10);
359321287Sbapt		break;
360321287Sbapt	case ELMTYP_THERM:
361321287Sbapt		if (cstat[2]) {
362321287Sbapt			sesutil_print(&title, "\t\t- Temperature: {:temperature/%d}{Uw:C}\n",
363321287Sbapt			    cstat[2] - TEMPERATURE_OFFSET);
364321287Sbapt		} else {
365321287Sbapt			sesutil_print(&title, "\t\t- Temperature: -{q:temperature/reserved}-\n");
366321287Sbapt		}
367321287Sbapt		break;
368321287Sbapt	case ELMTYP_VOM:
369321287Sbapt		sesutil_print(&title, "\t\t- Voltage: {:voltage/%.2f}{Uw:V}\n",
370321287Sbapt		    be16dec(cstat + 2) / 100.0);
371321287Sbapt		break;
372321287Sbapt	}
373321287Sbapt	if (title) {
374321287Sbapt		xo_close_container("extra_status");
375321287Sbapt	}
376321287Sbapt}
377321287Sbapt
378287988Sallanjudestatic int
379287988Sallanjudeobjmap(int argc, char **argv __unused)
380287988Sallanjude{
381292262Sbapt	encioc_string_t stri;
382287988Sallanjude	encioc_elm_devnames_t e_devname;
383287988Sallanjude	encioc_elm_status_t e_status;
384287988Sallanjude	encioc_elm_desc_t e_desc;
385287988Sallanjude	encioc_element_t *e_ptr;
386287988Sallanjude	glob_t g;
387287988Sallanjude	int fd;
388287988Sallanjude	unsigned int j, nobj;
389287988Sallanjude	size_t i;
390292262Sbapt	char str[32];
391287988Sallanjude
392287988Sallanjude	if (argc != 1) {
393287988Sallanjude		usage(stderr, "map");
394287988Sallanjude	}
395287988Sallanjude
396287988Sallanjude	/* Get the list of ses devices */
397287988Sallanjude	if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) {
398287988Sallanjude		globfree(&g);
399321287Sbapt		xo_errx(EXIT_FAILURE, "No SES devices found");
400287988Sallanjude	}
401321287Sbapt	xo_set_version(SESUTIL_XO_VERSION);
402321287Sbapt	xo_open_container("sesutil");
403321287Sbapt	xo_open_list("enclosures");
404287988Sallanjude	for (i = 0; i < g.gl_pathc; i++) {
405287988Sallanjude		/* ensure we only got numbers after ses */
406287988Sallanjude		if (strspn(g.gl_pathv[i] + 8, "0123456789") !=
407287988Sallanjude		    strlen(g.gl_pathv[i] + 8)) {
408287988Sallanjude			continue;
409287988Sallanjude		}
410287988Sallanjude		if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) {
411287988Sallanjude			/*
412287988Sallanjude			 * Don't treat non-access errors as critical if we are
413287988Sallanjude			 * accessing all devices
414287988Sallanjude			 */
415287988Sallanjude			if (errno == EACCES && g.gl_pathc > 1) {
416321287Sbapt				xo_err(EXIT_FAILURE, "unable to access SES device");
417287988Sallanjude			}
418321287Sbapt			xo_warn("unable to access SES device: %s", g.gl_pathv[i]);
419287988Sallanjude			continue;
420287988Sallanjude		}
421287988Sallanjude
422287988Sallanjude		if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) {
423287988Sallanjude			close(fd);
424321287Sbapt			xo_err(EXIT_FAILURE, "ENCIOC_GETNELM");
425287988Sallanjude		}
426287988Sallanjude
427287988Sallanjude		e_ptr = calloc(nobj, sizeof(encioc_element_t));
428287988Sallanjude		if (e_ptr == NULL) {
429287988Sallanjude			close(fd);
430321287Sbapt			xo_err(EXIT_FAILURE, "calloc()");
431287988Sallanjude		}
432287988Sallanjude
433287988Sallanjude		if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) e_ptr) < 0) {
434287988Sallanjude			close(fd);
435321287Sbapt			xo_err(EXIT_FAILURE, "ENCIOC_GETELMMAP");
436287988Sallanjude		}
437287988Sallanjude
438321287Sbapt		xo_open_instance("enclosures");
439321287Sbapt		xo_emit("{t:enc/%s}:\n", g.gl_pathv[i] + 5);
440292262Sbapt		stri.bufsiz = sizeof(str);
441292262Sbapt		stri.buf = &str[0];
442292262Sbapt		if (ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri) == 0)
443321287Sbapt			xo_emit("\tEnclosure Name: {t:name/%s}\n", stri.buf);
444292262Sbapt		stri.bufsiz = sizeof(str);
445292262Sbapt		stri.buf = &str[0];
446292262Sbapt		if (ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri) == 0)
447321287Sbapt			xo_emit("\tEnclosure ID: {t:id/%s}\n", stri.buf);
448292262Sbapt
449321287Sbapt		xo_open_list("elements");
450287988Sallanjude		for (j = 0; j < nobj; j++) {
451287988Sallanjude			/* Get the status of the element */
452287988Sallanjude			memset(&e_status, 0, sizeof(e_status));
453287988Sallanjude			e_status.elm_idx = e_ptr[j].elm_idx;
454287988Sallanjude			if (ioctl(fd, ENCIOC_GETELMSTAT,
455287988Sallanjude			    (caddr_t) &e_status) < 0) {
456287988Sallanjude				close(fd);
457321287Sbapt				xo_err(EXIT_FAILURE, "ENCIOC_GETELMSTAT");
458287988Sallanjude			}
459287988Sallanjude			/* Get the description of the element */
460287988Sallanjude			memset(&e_desc, 0, sizeof(e_desc));
461287988Sallanjude			e_desc.elm_idx = e_ptr[j].elm_idx;
462287988Sallanjude			e_desc.elm_desc_len = UINT16_MAX;
463287988Sallanjude			e_desc.elm_desc_str = calloc(UINT16_MAX, sizeof(char));
464287988Sallanjude			if (e_desc.elm_desc_str == NULL) {
465287988Sallanjude				close(fd);
466321287Sbapt				xo_err(EXIT_FAILURE, "calloc()");
467287988Sallanjude			}
468287988Sallanjude			if (ioctl(fd, ENCIOC_GETELMDESC,
469287988Sallanjude			    (caddr_t) &e_desc) < 0) {
470287988Sallanjude				close(fd);
471321287Sbapt				xo_err(EXIT_FAILURE, "ENCIOC_GETELMDESC");
472287988Sallanjude			}
473287988Sallanjude			/* Get the device name(s) of the element */
474287988Sallanjude			memset(&e_devname, 0, sizeof(e_devname));
475287988Sallanjude			e_devname.elm_idx = e_ptr[j].elm_idx;
476287988Sallanjude			e_devname.elm_names_size = 128;
477287988Sallanjude			e_devname.elm_devnames = calloc(128, sizeof(char));
478287988Sallanjude			if (e_devname.elm_devnames == NULL) {
479287988Sallanjude				close(fd);
480321287Sbapt				xo_err(EXIT_FAILURE, "calloc()");
481287988Sallanjude			}
482287988Sallanjude			if (ioctl(fd, ENCIOC_GETELMDEVNAMES,
483287988Sallanjude			    (caddr_t) &e_devname) <0) {
484287988Sallanjude				/* We don't care if this fails */
485287988Sallanjude				e_devname.elm_devnames[0] = '\0';
486287988Sallanjude			}
487321287Sbapt			xo_open_instance("elements");
488321287Sbapt			xo_emit("\tElement {:id/%u}, Type: {:type/%s}\n", e_ptr[j].elm_idx,
489287988Sallanjude			    geteltnm(e_ptr[j].elm_type));
490321287Sbapt			xo_emit("\t\tStatus: {:status/%s} ({q:status_code/0x%02x 0x%02x 0x%02x 0x%02x})\n",
491292121Sbapt			    scode2ascii(e_status.cstat[0]), e_status.cstat[0],
492292121Sbapt			    e_status.cstat[1], e_status.cstat[2],
493292121Sbapt			    e_status.cstat[3]);
494287988Sallanjude			if (e_desc.elm_desc_len > 0) {
495321287Sbapt				xo_emit("\t\tDescription: {:description/%s}\n",
496287988Sallanjude				    e_desc.elm_desc_str);
497287988Sallanjude			}
498287988Sallanjude			if (e_devname.elm_names_len > 0) {
499321287Sbapt				xo_emit("\t\tDevice Names: {:device_names/%s}\n",
500287988Sallanjude				    e_devname.elm_devnames);
501287988Sallanjude			}
502321287Sbapt			print_extra_status(e_ptr[j].elm_type, e_status.cstat);
503321287Sbapt			xo_close_instance("elements");
504287988Sallanjude			free(e_devname.elm_devnames);
505287988Sallanjude		}
506321287Sbapt		xo_close_list("elements");
507298382Sbapt		free(e_ptr);
508287988Sallanjude		close(fd);
509287988Sallanjude	}
510287988Sallanjude	globfree(&g);
511321287Sbapt	xo_close_list("enclosures");
512321287Sbapt	xo_close_container("sesutil");
513321287Sbapt	xo_finish();
514287988Sallanjude
515287988Sallanjude	return (EXIT_SUCCESS);
516287988Sallanjude}
517287988Sallanjude
518287988Sallanjudestatic int
519287988Sallanjudeencstatus(int argc, char **argv __unused)
520287988Sallanjude{
521287988Sallanjude	glob_t g;
522287988Sallanjude	int fd, status;
523287988Sallanjude	size_t i, e;
524287988Sallanjude	u_char estat;
525287988Sallanjude
526287988Sallanjude	status = 0;
527287988Sallanjude	if (argc != 1) {
528287988Sallanjude		usage(stderr, "status");
529287988Sallanjude	}
530287988Sallanjude
531287988Sallanjude	/* Get the list of ses devices */
532287988Sallanjude	if (glob(uflag, 0, NULL, &g) == GLOB_NOMATCH) {
533287988Sallanjude		globfree(&g);
534321287Sbapt		xo_errx(EXIT_FAILURE, "No SES devices found");
535287988Sallanjude	}
536321287Sbapt
537321287Sbapt	xo_set_version(SESUTIL_XO_VERSION);
538321287Sbapt	xo_open_container("sesutil");
539321287Sbapt	xo_open_list("enclosures");
540287988Sallanjude	for (i = 0; i < g.gl_pathc; i++) {
541287988Sallanjude		/* ensure we only got numbers after ses */
542287988Sallanjude		if (strspn(g.gl_pathv[i] + 8, "0123456789") !=
543287988Sallanjude		    strlen(g.gl_pathv[i] + 8)) {
544287988Sallanjude			continue;
545287988Sallanjude		}
546287988Sallanjude		if ((fd = open(g.gl_pathv[i], O_RDWR)) < 0) {
547287988Sallanjude			/*
548287988Sallanjude			 * Don't treat non-access errors as critical if we are
549287988Sallanjude			 * accessing all devices
550287988Sallanjude			 */
551287988Sallanjude			if (errno == EACCES && g.gl_pathc > 1) {
552321287Sbapt				xo_err(EXIT_FAILURE, "unable to access SES device");
553287988Sallanjude			}
554321287Sbapt			xo_warn("unable to access SES device: %s", g.gl_pathv[i]);
555287988Sallanjude			continue;
556287988Sallanjude		}
557287988Sallanjude
558287988Sallanjude		if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat) < 0) {
559321287Sbapt			xo_err(EXIT_FAILURE, "ENCIOC_GETENCSTAT");
560287988Sallanjude			close(fd);
561287988Sallanjude		}
562287988Sallanjude
563321287Sbapt		xo_open_instance("enclosures");
564321287Sbapt		xo_emit("{:enc/%s}: ", g.gl_pathv[i] + 5);
565287988Sallanjude		e = 0;
566287988Sallanjude		if (estat == 0) {
567287988Sallanjude			if (status == 0) {
568287988Sallanjude				status = 1;
569287988Sallanjude			}
570321287Sbapt			xo_emit("{q:status/OK}");
571287988Sallanjude		} else {
572287988Sallanjude			if (estat & SES_ENCSTAT_INFO) {
573321287Sbapt				xo_emit("{lq:status/INFO}");
574287988Sallanjude				e++;
575287988Sallanjude			}
576287988Sallanjude			if (estat & SES_ENCSTAT_NONCRITICAL) {
577287988Sallanjude				if (e)
578321287Sbapt					xo_emit(",");
579321287Sbapt				xo_emit("{lq:status/NONCRITICAL}");
580287988Sallanjude				e++;
581287988Sallanjude			}
582287988Sallanjude			if (estat & SES_ENCSTAT_CRITICAL) {
583287988Sallanjude				if (e)
584321287Sbapt					xo_emit(",");
585321287Sbapt				xo_emit("{lq:status/CRITICAL}");
586287988Sallanjude				e++;
587287988Sallanjude				status = -1;
588287988Sallanjude			}
589287988Sallanjude			if (estat & SES_ENCSTAT_UNRECOV) {
590287988Sallanjude				if (e)
591321287Sbapt					xo_emit(",");
592321287Sbapt				xo_emit("{lq:status/UNRECOV}");
593287988Sallanjude				e++;
594287988Sallanjude				status = -1;
595287988Sallanjude			}
596287988Sallanjude		}
597321287Sbapt		xo_close_instance("enclosures");
598321287Sbapt		xo_emit("\n");
599287988Sallanjude		close(fd);
600287988Sallanjude	}
601287988Sallanjude	globfree(&g);
602287988Sallanjude
603321287Sbapt	xo_close_list("enclosures");
604321287Sbapt	xo_close_container("sesutil");
605321287Sbapt	xo_finish();
606321287Sbapt
607287988Sallanjude	if (status == 1) {
608287988Sallanjude		return (EXIT_SUCCESS);
609287988Sallanjude	} else {
610287988Sallanjude		return (EXIT_FAILURE);
611287988Sallanjude	}
612287988Sallanjude}
613287988Sallanjude
614287473Sbaptint
615287473Sbaptmain(int argc, char **argv)
616287473Sbapt{
617287988Sallanjude	int i, ch;
618287473Sbapt	struct command *cmd = NULL;
619287473Sbapt
620321287Sbapt	argc = xo_parse_args(argc, argv);
621321287Sbapt	if (argc < 0)
622321287Sbapt		exit(1);
623321287Sbapt
624287988Sallanjude	uflag = "/dev/ses[0-9]*";
625287988Sallanjude	while ((ch = getopt_long(argc, argv, "u:", NULL, NULL)) != -1) {
626287988Sallanjude		switch (ch) {
627287988Sallanjude		case 'u':
628287988Sallanjude			uflag = optarg;
629287988Sallanjude			break;
630287988Sallanjude		case '?':
631287988Sallanjude		default:
632287988Sallanjude			usage(stderr, NULL);
633287988Sallanjude		}
634287988Sallanjude	}
635287988Sallanjude	argc -= optind;
636287988Sallanjude	argv += optind;
637287988Sallanjude
638287988Sallanjude	if (argc < 1) {
639287473Sbapt		warnx("Missing command");
640287988Sallanjude		usage(stderr, NULL);
641287473Sbapt	}
642287473Sbapt
643287473Sbapt	for (i = 0; i < nbcmds; i++) {
644287988Sallanjude		if (strcmp(argv[0], cmds[i].name) == 0) {
645287473Sbapt			cmd = &cmds[i];
646287473Sbapt			break;
647287473Sbapt		}
648287473Sbapt	}
649287473Sbapt
650287473Sbapt	if (cmd == NULL) {
651287988Sallanjude		warnx("unknown command %s", argv[0]);
652287988Sallanjude		usage(stderr, NULL);
653287473Sbapt	}
654287473Sbapt
655287473Sbapt	return (cmd->exec(argc, argv));
656287473Sbapt}
657