xref: /freebsd/tests/sys/ses/nondestructive.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1eea7c615SAlan Somers /*-
2eea7c615SAlan Somers  * Copyright (C) 2021 Axcient, Inc. All rights reserved.
3eea7c615SAlan Somers  *
4eea7c615SAlan Somers  * Redistribution and use in source and binary forms, with or without
5eea7c615SAlan Somers  * modification, are permitted provided that the following conditions
6eea7c615SAlan Somers  * are met:
7eea7c615SAlan Somers  * 1. Redistributions of source code must retain the above copyright
8eea7c615SAlan Somers  *    notice, this list of conditions and the following disclaimer.
9eea7c615SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
10eea7c615SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
11eea7c615SAlan Somers  *    documentation and/or other materials provided with the distribution.
12eea7c615SAlan Somers  *
13eea7c615SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14eea7c615SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15eea7c615SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16eea7c615SAlan Somers  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17eea7c615SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18eea7c615SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19eea7c615SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20eea7c615SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21eea7c615SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22eea7c615SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23eea7c615SAlan Somers  * SUCH DAMAGE.
24eea7c615SAlan Somers  */
25eea7c615SAlan Somers 
26eea7c615SAlan Somers /* Basic smoke test of the ioctl interface */
27eea7c615SAlan Somers 
28eea7c615SAlan Somers #include <sys/types.h>
29eea7c615SAlan Somers #include <sys/ioctl.h>
30eea7c615SAlan Somers 
31eea7c615SAlan Somers #include <atf-c.h>
32eea7c615SAlan Somers #include <fcntl.h>
33eea7c615SAlan Somers #include <glob.h>
34eea7c615SAlan Somers #include <regex.h>
35eea7c615SAlan Somers #include <stdint.h>
36eea7c615SAlan Somers #include <stdio.h>
37eea7c615SAlan Somers #include <stdlib.h>
38eea7c615SAlan Somers 
39eea7c615SAlan Somers #include <cam/scsi/scsi_enc.h>
40eea7c615SAlan Somers 
41eea7c615SAlan Somers #include "common.h"
42eea7c615SAlan Somers 
43*b37d1debSAlan Somers static bool
do_getelmdesc(const char * devname,int fd)44*b37d1debSAlan Somers do_getelmdesc(const char *devname, int fd)
45*b37d1debSAlan Somers {
46eea7c615SAlan Somers 	regex_t re;
47eea7c615SAlan Somers 	FILE *pipe;
48eea7c615SAlan Somers 	char cmd[256];
49eea7c615SAlan Somers 	char line[256];
50eea7c615SAlan Somers 	char *actual;
51eea7c615SAlan Somers 	unsigned nobj;
52eea7c615SAlan Somers 	unsigned elm_idx = 0;
53eea7c615SAlan Somers 	int r;
54eea7c615SAlan Somers 
55eea7c615SAlan Somers 	actual = calloc(UINT16_MAX, sizeof(char));
56eea7c615SAlan Somers 	ATF_REQUIRE(actual != NULL);
57eea7c615SAlan Somers 	r = regcomp(&re, "(Overall|Element [0-9]+) descriptor: ", REG_EXTENDED);
58eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
59eea7c615SAlan Somers 
60eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
61eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
62eea7c615SAlan Somers 
63eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p7 %s", devname);
64eea7c615SAlan Somers 	pipe = popen(cmd, "r");
65eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
66eea7c615SAlan Somers 	while(NULL != fgets(line, sizeof(line), pipe)) {
67eea7c615SAlan Somers 		regmatch_t matches[1];
68eea7c615SAlan Somers 		encioc_elm_desc_t e_desc;
69eea7c615SAlan Somers 		char *expected;
70eea7c615SAlan Somers 		size_t elen;
71eea7c615SAlan Somers 
72eea7c615SAlan Somers 		if (regexec(&re, line, 1, matches, 0) == REG_NOMATCH) {
73eea7c615SAlan Somers 			continue;
74eea7c615SAlan Somers 		}
75eea7c615SAlan Somers 
76eea7c615SAlan Somers 		expected = &line[matches[0].rm_eo];
77eea7c615SAlan Somers 		/* Remove trailing newline */
78eea7c615SAlan Somers 		elen = strnlen(expected, sizeof(line) - matches[0].rm_eo);
79eea7c615SAlan Somers 		expected[elen - 1] = '\0';
80eea7c615SAlan Somers 		/*
81eea7c615SAlan Somers 		 * Zero the result string.  XXX we wouldn't have to do this if
82eea7c615SAlan Somers 		 * the kernel would nul-terminate the result.
83eea7c615SAlan Somers 		 */
84eea7c615SAlan Somers 		memset(actual, 0, UINT16_MAX);
85eea7c615SAlan Somers 		e_desc.elm_idx = elm_idx;
86eea7c615SAlan Somers 		e_desc.elm_desc_len = UINT16_MAX;
87eea7c615SAlan Somers 		e_desc.elm_desc_str = actual;
88eea7c615SAlan Somers 		r = ioctl(fd, ENCIOC_GETELMDESC, (caddr_t) &e_desc);
89eea7c615SAlan Somers 		ATF_REQUIRE_EQ(r, 0);
90eea7c615SAlan Somers 		if (0 == strcmp("<empty>", expected)) {
91eea7c615SAlan Somers 			/* sg_ses replaces "" with "<empty>" */
92eea7c615SAlan Somers 			ATF_CHECK_STREQ("", actual);
93eea7c615SAlan Somers 		} else
94eea7c615SAlan Somers 			ATF_CHECK_STREQ(expected, actual);
95eea7c615SAlan Somers 		elm_idx++;
96eea7c615SAlan Somers 	}
97eea7c615SAlan Somers 
98eea7c615SAlan Somers 	r = pclose(pipe);
99eea7c615SAlan Somers 	regfree(&re);
100eea7c615SAlan Somers 	free(actual);
101eea7c615SAlan Somers 	if (r != 0) {
102eea7c615SAlan Somers 		/* Probably an SGPIO device */
103eea7c615SAlan Somers 
104eea7c615SAlan Somers 		return (false);
105eea7c615SAlan Somers 	} else {
106eea7c615SAlan Somers 		ATF_CHECK_EQ_MSG(nobj, elm_idx,
107eea7c615SAlan Somers 				"Did not find the expected number of element "
108eea7c615SAlan Somers 				"descriptors in sg_ses's output");
109eea7c615SAlan Somers 		return (true);
110eea7c615SAlan Somers 	}
111eea7c615SAlan Somers }
112eea7c615SAlan Somers 
113eea7c615SAlan Somers ATF_TC(getelmdesc);
ATF_TC_HEAD(getelmdesc,tc)114eea7c615SAlan Somers ATF_TC_HEAD(getelmdesc, tc)
115eea7c615SAlan Somers {
116eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
117eea7c615SAlan Somers 	    "Compare ENCIOC_GETELMDESC's output to sg3_utils'");
118eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
119eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
120eea7c615SAlan Somers }
ATF_TC_BODY(getelmdesc,tc)121eea7c615SAlan Somers ATF_TC_BODY(getelmdesc, tc)
122eea7c615SAlan Somers {
1232c449a4cSLi-Wen Hsu 	if (!has_ses())
1242c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
125eea7c615SAlan Somers 	for_each_ses_dev(do_getelmdesc, O_RDONLY);
126eea7c615SAlan Somers }
127eea7c615SAlan Somers 
128*b37d1debSAlan Somers static bool
do_getelmdevnames(const char * devname __unused,int fd)129*b37d1debSAlan Somers do_getelmdevnames(const char *devname __unused, int fd)
130*b37d1debSAlan Somers {
131eea7c615SAlan Somers 	encioc_element_t *map;
132eea7c615SAlan Somers 	unsigned nobj;
133eea7c615SAlan Somers 	const size_t namesize = 128;
134*b37d1debSAlan Somers 	int r, status;
135eea7c615SAlan Somers 	char *namebuf;
136eea7c615SAlan Somers 	unsigned elm_idx;
137eea7c615SAlan Somers 
138eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
139eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
140eea7c615SAlan Somers 
141eea7c615SAlan Somers 	namebuf = calloc(namesize, sizeof(char));
142eea7c615SAlan Somers 	ATF_REQUIRE(namebuf != NULL);
143eea7c615SAlan Somers 	map = calloc(nobj, sizeof(encioc_element_t));
144eea7c615SAlan Somers 	ATF_REQUIRE(map != NULL);
145eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
146eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
147eea7c615SAlan Somers 
148eea7c615SAlan Somers 	for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
149eea7c615SAlan Somers 		/*
150eea7c615SAlan Somers 		 * devnames should be present if:
151eea7c615SAlan Somers 		 * * The element is of type Device Slot or Array Device Slot
152eea7c615SAlan Somers 		 * * It isn't an Overall Element
153eea7c615SAlan Somers 		 * * The element's status is not "Not Installed"
154eea7c615SAlan Somers 		 */
155eea7c615SAlan Somers 		encioc_elm_status_t e_status;
156eea7c615SAlan Somers 		encioc_elm_devnames_t elmdn;
157eea7c615SAlan Somers 
158eea7c615SAlan Somers 		memset(&e_status, 0, sizeof(e_status));
159eea7c615SAlan Somers 		e_status.elm_idx = elm_idx;
160eea7c615SAlan Somers 		r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&e_status);
161eea7c615SAlan Somers 		ATF_REQUIRE_EQ(r, 0);
162eea7c615SAlan Somers 
163eea7c615SAlan Somers 		memset(&elmdn, 0, sizeof(elmdn));
164eea7c615SAlan Somers 		elmdn.elm_idx = elm_idx;
165eea7c615SAlan Somers 		elmdn.elm_names_size = namesize;
166eea7c615SAlan Somers 		elmdn.elm_devnames = namebuf;
167eea7c615SAlan Somers 		namebuf[0] = '\0';
168eea7c615SAlan Somers 		r = ioctl(fd, ENCIOC_GETELMDEVNAMES, (caddr_t) &elmdn);
169*b37d1debSAlan Somers 		status = ses_status_common_get_element_status_code(
170*b37d1debSAlan Somers 			(struct ses_status_common*)&e_status.cstat[0]);
171*b37d1debSAlan Somers 		if (status != SES_OBJSTAT_UNSUPPORTED &&
172*b37d1debSAlan Somers 		    status != SES_OBJSTAT_NOTINSTALLED &&
173eea7c615SAlan Somers 		    (map[elm_idx].elm_type == ELMTYP_DEVICE ||
174eea7c615SAlan Somers 		     map[elm_idx].elm_type == ELMTYP_ARRAY_DEV))
175eea7c615SAlan Somers 		{
176eea7c615SAlan Somers 			ATF_CHECK_EQ_MSG(r, 0, "devnames not found.  This could be due to a buggy ses driver, buggy ses controller, dead HDD, or an ATA HDD in a SAS slot");
177eea7c615SAlan Somers 		} else {
178eea7c615SAlan Somers 			ATF_CHECK(r != 0);
179eea7c615SAlan Somers 		}
180eea7c615SAlan Somers 
181eea7c615SAlan Somers 		if (r == 0) {
182eea7c615SAlan Somers 			size_t z = 0;
183*b37d1debSAlan Somers 			int da = 0, ada = 0, pass = 0, nda = 0, unknown = 0;
184eea7c615SAlan Somers 
185eea7c615SAlan Somers 			while(elmdn.elm_devnames[z] != '\0') {
186eea7c615SAlan Somers 				size_t e;
187eea7c615SAlan Somers 				char *s;
188eea7c615SAlan Somers 
189eea7c615SAlan Somers 				if (elmdn.elm_devnames[z] == ',')
190eea7c615SAlan Somers 					z++;	/* Skip the comma */
191eea7c615SAlan Somers 				s = elmdn.elm_devnames + z;
192eea7c615SAlan Somers 				e = strcspn(s, "0123456789");
193eea7c615SAlan Somers 				if (0 == strncmp("da", s, e))
194eea7c615SAlan Somers 					da++;
195eea7c615SAlan Somers 				else if (0 == strncmp("ada", s, e))
196eea7c615SAlan Somers 					ada++;
197eea7c615SAlan Somers 				else if (0 == strncmp("pass", s, e))
198eea7c615SAlan Somers 					pass++;
199*b37d1debSAlan Somers 				else if (0 == strncmp("nda", s, e))
200*b37d1debSAlan Somers 					nda++;
201eea7c615SAlan Somers 				else
202eea7c615SAlan Somers 					unknown++;
203eea7c615SAlan Somers 				z += strcspn(elmdn.elm_devnames + z, ",");
204eea7c615SAlan Somers 			}
205eea7c615SAlan Somers 			/* There should be one pass dev for each non-pass dev */
206*b37d1debSAlan Somers 			ATF_CHECK_EQ(pass, da + ada + nda);
207eea7c615SAlan Somers 			ATF_CHECK_EQ_MSG(0, unknown,
208eea7c615SAlan Somers 			    "Unknown device names %s", elmdn.elm_devnames);
209eea7c615SAlan Somers 		}
210eea7c615SAlan Somers 	}
211eea7c615SAlan Somers 	free(map);
212eea7c615SAlan Somers 	free(namebuf);
213eea7c615SAlan Somers 
214eea7c615SAlan Somers 	return (true);
215eea7c615SAlan Somers }
216eea7c615SAlan Somers 
217eea7c615SAlan Somers ATF_TC(getelmdevnames);
ATF_TC_HEAD(getelmdevnames,tc)218eea7c615SAlan Somers ATF_TC_HEAD(getelmdevnames, tc)
219eea7c615SAlan Somers {
220eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
221eea7c615SAlan Somers 	    "Compare ENCIOC_GETELMDEVNAMES's output to sg3_utils'");
222eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
223eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
224eea7c615SAlan Somers }
ATF_TC_BODY(getelmdevnames,tc)225eea7c615SAlan Somers ATF_TC_BODY(getelmdevnames, tc)
226eea7c615SAlan Somers {
2272c449a4cSLi-Wen Hsu 	if (!has_ses())
2282c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
229eea7c615SAlan Somers 	for_each_ses_dev(do_getelmdevnames, O_RDONLY);
230eea7c615SAlan Somers }
231eea7c615SAlan Somers 
232eea7c615SAlan Somers static int
elm_type_name2int(const char * name)233*b37d1debSAlan Somers elm_type_name2int(const char *name)
234*b37d1debSAlan Somers {
235eea7c615SAlan Somers 	const char *elm_type_names[] = ELM_TYPE_NAMES;
236eea7c615SAlan Somers 	int i;
237eea7c615SAlan Somers 
238eea7c615SAlan Somers 	for (i = 0; i <= ELMTYP_LAST; i++) {
239eea7c615SAlan Somers 		/* sg_ses uses different case than ses(4) */
240eea7c615SAlan Somers 		if (0 == strcasecmp(name, elm_type_names[i]))
241eea7c615SAlan Somers 			return i;
242eea7c615SAlan Somers 	}
243eea7c615SAlan Somers 	return (-1);
244eea7c615SAlan Somers }
245eea7c615SAlan Somers 
246*b37d1debSAlan Somers static bool
do_getelmmap(const char * devname,int fd)247*b37d1debSAlan Somers do_getelmmap(const char *devname, int fd)
248*b37d1debSAlan Somers {
249eea7c615SAlan Somers 	encioc_element_t *map;
250eea7c615SAlan Somers 	FILE *pipe;
251eea7c615SAlan Somers 	char cmd[256];
252eea7c615SAlan Somers 	char line[256];
253eea7c615SAlan Somers 	unsigned elm_idx = 0;
254eea7c615SAlan Somers 	unsigned nobj, subenc_id;
255eea7c615SAlan Somers 	int r, elm_type;
256eea7c615SAlan Somers 
257eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
258eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
259eea7c615SAlan Somers 
260eea7c615SAlan Somers 	map = calloc(nobj, sizeof(encioc_element_t));
261eea7c615SAlan Somers 	ATF_REQUIRE(map != NULL);
262eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
263eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
264eea7c615SAlan Somers 
265eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
266eea7c615SAlan Somers 	pipe = popen(cmd, "r");
267eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
268eea7c615SAlan Somers 	while(NULL != fgets(line, sizeof(line), pipe)) {
269eea7c615SAlan Somers 		char elm_type_name[80];
270eea7c615SAlan Somers 		int i, num_elm;
271eea7c615SAlan Somers 
272eea7c615SAlan Somers 		r = sscanf(line,
273eea7c615SAlan Somers 		    "    Element type: %[a-zA-Z0-9_ /], subenclosure id: %d",
274eea7c615SAlan Somers 		    elm_type_name, &subenc_id);
275eea7c615SAlan Somers 		if (r == 2) {
276eea7c615SAlan Somers 			elm_type = elm_type_name2int(elm_type_name);
277eea7c615SAlan Somers 			continue;
278eea7c615SAlan Somers 		} else {
279eea7c615SAlan Somers 			r = sscanf(line,
280eea7c615SAlan Somers 			    "    Element type: vendor specific [0x%x], subenclosure id: %d",
281eea7c615SAlan Somers 			    &elm_type, &subenc_id);
282eea7c615SAlan Somers 			if (r == 2)
283eea7c615SAlan Somers 				continue;
284eea7c615SAlan Somers 		}
285eea7c615SAlan Somers 		r = sscanf(line, "      number of possible elements: %d",
286eea7c615SAlan Somers 		    &num_elm);
287eea7c615SAlan Somers 		if (r != 1)
288eea7c615SAlan Somers 			continue;
289eea7c615SAlan Somers 
290eea7c615SAlan Somers 		/* Skip the Overall elements */
291eea7c615SAlan Somers 		elm_idx++;
292eea7c615SAlan Somers 		for (i = 0; i < num_elm; i++, elm_idx++) {
293eea7c615SAlan Somers 			ATF_CHECK_EQ(map[elm_idx].elm_idx, elm_idx);
294eea7c615SAlan Somers 			ATF_CHECK_EQ(map[elm_idx].elm_subenc_id, subenc_id);
295eea7c615SAlan Somers 			ATF_CHECK_EQ((int)map[elm_idx].elm_type, elm_type);
296eea7c615SAlan Somers 		}
297eea7c615SAlan Somers 	}
298eea7c615SAlan Somers 
299eea7c615SAlan Somers 	free(map);
300eea7c615SAlan Somers 	r = pclose(pipe);
301eea7c615SAlan Somers 	if (r != 0) {
302eea7c615SAlan Somers 		/* Probably an SGPIO device */
303eea7c615SAlan Somers 		return (false);
304eea7c615SAlan Somers 	} else {
305eea7c615SAlan Somers 		ATF_CHECK_EQ_MSG(nobj, elm_idx,
306eea7c615SAlan Somers 				"Did not find the expected number of element "
307eea7c615SAlan Somers 				"descriptors in sg_ses's output");
308eea7c615SAlan Somers 		return (true);
309eea7c615SAlan Somers 	}
310eea7c615SAlan Somers }
311eea7c615SAlan Somers 
312eea7c615SAlan Somers ATF_TC(getelmmap);
ATF_TC_HEAD(getelmmap,tc)313eea7c615SAlan Somers ATF_TC_HEAD(getelmmap, tc)
314eea7c615SAlan Somers {
315eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
316eea7c615SAlan Somers 	    "Compare ENCIOC_GETELMMAP's output to sg3_utils'");
317eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
318eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
319eea7c615SAlan Somers }
ATF_TC_BODY(getelmmap,tc)320eea7c615SAlan Somers ATF_TC_BODY(getelmmap, tc)
321eea7c615SAlan Somers {
3222c449a4cSLi-Wen Hsu 	if (!has_ses())
3232c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
324eea7c615SAlan Somers 	for_each_ses_dev(do_getelmmap, O_RDONLY);
325eea7c615SAlan Somers }
326eea7c615SAlan Somers 
327*b37d1debSAlan Somers static bool
do_getelmstat(const char * devname,int fd)328*b37d1debSAlan Somers do_getelmstat(const char *devname, int fd)
329*b37d1debSAlan Somers {
330eea7c615SAlan Somers 	encioc_element_t *map;
331eea7c615SAlan Somers 	unsigned elm_idx;
332eea7c615SAlan Somers 	unsigned nobj;
333eea7c615SAlan Somers 	int r, elm_subidx;
334eea7c615SAlan Somers 	elm_type_t last_elm_type = -1;
335eea7c615SAlan Somers 
336eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
337eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
338eea7c615SAlan Somers 
339eea7c615SAlan Somers 	map = calloc(nobj, sizeof(encioc_element_t));
340eea7c615SAlan Somers 	ATF_REQUIRE(map != NULL);
341eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
342eea7c615SAlan Somers 
343eea7c615SAlan Somers 	for (elm_idx = 0; elm_idx < nobj; elm_subidx++, elm_idx++) {
344eea7c615SAlan Somers 		encioc_elm_status_t e_status;
345eea7c615SAlan Somers 		FILE *pipe;
346eea7c615SAlan Somers 		char cmd[256];
347eea7c615SAlan Somers 		uint32_t status;
348eea7c615SAlan Somers 		int pr;
349eea7c615SAlan Somers 
350eea7c615SAlan Somers 		if (last_elm_type != map[elm_idx].elm_type)
351eea7c615SAlan Somers 			elm_subidx = -1;
352eea7c615SAlan Somers 		last_elm_type = map[elm_idx].elm_type;
353eea7c615SAlan Somers 
354eea7c615SAlan Somers 		snprintf(cmd, sizeof(cmd),
355eea7c615SAlan Somers 		    "sg_ses -Hp2 --index=_%d,%d --get=0:7:32 %s",
356eea7c615SAlan Somers 		    map[elm_idx].elm_type, elm_subidx, devname);
357eea7c615SAlan Somers 		pipe = popen(cmd, "r");
358eea7c615SAlan Somers 		ATF_REQUIRE(pipe != NULL);
359eea7c615SAlan Somers 		r = fscanf(pipe, "0x%x", &status);
360eea7c615SAlan Somers 		pr = pclose(pipe);
361eea7c615SAlan Somers 		if (pr != 0) {
362eea7c615SAlan Somers 			/* Probably an SGPIO device */
363eea7c615SAlan Somers 			free(map);
364eea7c615SAlan Somers 			return (false);
365eea7c615SAlan Somers 		}
366eea7c615SAlan Somers 		ATF_REQUIRE_EQ(r, 1);
367eea7c615SAlan Somers 
368eea7c615SAlan Somers 		memset(&e_status, 0, sizeof(e_status));
369eea7c615SAlan Somers 		e_status.elm_idx = map[elm_idx].elm_idx;
370eea7c615SAlan Somers 		r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&e_status);
371eea7c615SAlan Somers 		ATF_REQUIRE_EQ(r, 0);
372eea7c615SAlan Somers 
373eea7c615SAlan Somers 		// Compare the common status field
374eea7c615SAlan Somers 		ATF_CHECK_EQ(e_status.cstat[0], status >> 24);
375eea7c615SAlan Somers 		/*
376eea7c615SAlan Somers 		 * Ignore the other fields, because some have values that can
377eea7c615SAlan Somers 		 * change frequently (voltage, temperature, etc)
378eea7c615SAlan Somers 		 */
379eea7c615SAlan Somers 	}
380eea7c615SAlan Somers 	free(map);
381eea7c615SAlan Somers 
382eea7c615SAlan Somers 	return (true);
383eea7c615SAlan Somers }
384eea7c615SAlan Somers 
385eea7c615SAlan Somers ATF_TC(getelmstat);
ATF_TC_HEAD(getelmstat,tc)386eea7c615SAlan Somers ATF_TC_HEAD(getelmstat, tc)
387eea7c615SAlan Somers {
388eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
389eea7c615SAlan Somers 	    "Compare ENCIOC_GETELMSTAT's output to sg3_utils'");
390eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
391eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
392eea7c615SAlan Somers }
ATF_TC_BODY(getelmstat,tc)393eea7c615SAlan Somers ATF_TC_BODY(getelmstat, tc)
394eea7c615SAlan Somers {
3952c449a4cSLi-Wen Hsu 	if (!has_ses())
3962c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
397eea7c615SAlan Somers 	for_each_ses_dev(do_getelmstat, O_RDONLY);
398eea7c615SAlan Somers }
399eea7c615SAlan Somers 
400*b37d1debSAlan Somers static bool
do_getencid(const char * devname,int fd)401*b37d1debSAlan Somers do_getencid(const char *devname, int fd)
402*b37d1debSAlan Somers {
403eea7c615SAlan Somers 	encioc_string_t stri;
404eea7c615SAlan Somers 	FILE *pipe;
405eea7c615SAlan Somers 	char cmd[256];
406eea7c615SAlan Somers 	char encid[32];
407eea7c615SAlan Somers 	char line[256];
408eea7c615SAlan Somers 	char sg_encid[32];
409eea7c615SAlan Somers 	int r, sg_ses_r;
410eea7c615SAlan Somers 
411eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
412eea7c615SAlan Somers 	pipe = popen(cmd, "r");
413eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
414eea7c615SAlan Somers 	sg_encid[0] = '\0';
415eea7c615SAlan Somers 	while(NULL != fgets(line, sizeof(line), pipe)) {
416eea7c615SAlan Somers 		const char *f = "      enclosure logical identifier (hex): %s";
417eea7c615SAlan Somers 
418eea7c615SAlan Somers 		if (1 == fscanf(pipe, f, sg_encid))
419eea7c615SAlan Somers 			break;
420eea7c615SAlan Somers 	}
421eea7c615SAlan Somers 	sg_ses_r = pclose(pipe);
422eea7c615SAlan Somers 
423eea7c615SAlan Somers 	stri.bufsiz = sizeof(encid);
424eea7c615SAlan Somers 	stri.buf = &encid[0];
425eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri);
426eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
427eea7c615SAlan Somers 	if (sg_ses_r == 0) {
428eea7c615SAlan Somers 		ATF_REQUIRE(sg_encid[0] != '\0');
429eea7c615SAlan Somers 		ATF_CHECK_STREQ(sg_encid, (char*)stri.buf);
430eea7c615SAlan Somers 		return (true);
431eea7c615SAlan Somers 	} else {
432eea7c615SAlan Somers 		/* Probably SGPIO; sg_ses unsupported */
433eea7c615SAlan Somers 		return (false);
434eea7c615SAlan Somers 	}
435eea7c615SAlan Somers }
436eea7c615SAlan Somers 
437eea7c615SAlan Somers ATF_TC(getencid);
ATF_TC_HEAD(getencid,tc)438eea7c615SAlan Somers ATF_TC_HEAD(getencid, tc)
439eea7c615SAlan Somers {
440eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
441eea7c615SAlan Somers 	    "Compare ENCIOC_GETENCID's output to sg3_utils'");
442eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
443eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
444eea7c615SAlan Somers }
ATF_TC_BODY(getencid,tc)445eea7c615SAlan Somers ATF_TC_BODY(getencid, tc)
446eea7c615SAlan Somers {
4472c449a4cSLi-Wen Hsu 	if (!has_ses())
4482c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
449eea7c615SAlan Somers 	for_each_ses_dev(do_getencid, O_RDONLY);
450eea7c615SAlan Somers }
451eea7c615SAlan Somers 
452*b37d1debSAlan Somers static bool
do_getencname(const char * devname,int fd)453*b37d1debSAlan Somers do_getencname(const char *devname, int fd)
454*b37d1debSAlan Somers {
455eea7c615SAlan Somers 	encioc_string_t stri;
456eea7c615SAlan Somers 	FILE *pipe;
457eea7c615SAlan Somers 	char cmd[256];
458eea7c615SAlan Somers 	char encname[32];
459eea7c615SAlan Somers 	char line[256];
460eea7c615SAlan Somers 	int r;
461eea7c615SAlan Somers 
462eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_inq -o %s | awk '"
463eea7c615SAlan Somers 		"/Vendor identification/ {vi=$NF} "
464eea7c615SAlan Somers 		"/Product identification/ {pi=$NF} "
465eea7c615SAlan Somers 		"/Product revision level/ {prl=$NF} "
466eea7c615SAlan Somers 		"END {printf(vi \" \" pi \" \" prl)}'", devname);
467eea7c615SAlan Somers 	pipe = popen(cmd, "r");
468eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
469eea7c615SAlan Somers 	ATF_REQUIRE(NULL != fgets(line, sizeof(line), pipe));
470eea7c615SAlan Somers 	pclose(pipe);
471eea7c615SAlan Somers 
472eea7c615SAlan Somers 	stri.bufsiz = sizeof(encname);
473eea7c615SAlan Somers 	stri.buf = &encname[0];
474eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri);
475eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
476eea7c615SAlan Somers 	if (strlen(line) < 3) {
477eea7c615SAlan Somers 		// Probably an SGPIO device, INQUIRY unsupported
478eea7c615SAlan Somers 		return (false);
479eea7c615SAlan Somers 	} else {
480eea7c615SAlan Somers 		ATF_CHECK_STREQ(line, (char*)stri.buf);
481eea7c615SAlan Somers 		return (true);
482eea7c615SAlan Somers 	}
483eea7c615SAlan Somers }
484eea7c615SAlan Somers 
485eea7c615SAlan Somers ATF_TC(getencname);
ATF_TC_HEAD(getencname,tc)486eea7c615SAlan Somers ATF_TC_HEAD(getencname, tc)
487eea7c615SAlan Somers {
488eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
489eea7c615SAlan Somers 	    "Compare ENCIOC_GETENCNAME's output to sg3_utils'");
490eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
491eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_inq");
492eea7c615SAlan Somers }
ATF_TC_BODY(getencname,tc)493eea7c615SAlan Somers ATF_TC_BODY(getencname, tc)
494eea7c615SAlan Somers {
4952c449a4cSLi-Wen Hsu 	if (!has_ses())
4962c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
497eea7c615SAlan Somers 	for_each_ses_dev(do_getencname, O_RDONLY);
498eea7c615SAlan Somers }
499eea7c615SAlan Somers 
500*b37d1debSAlan Somers static bool
do_getencstat(const char * devname,int fd)501*b37d1debSAlan Somers do_getencstat(const char *devname, int fd)
502*b37d1debSAlan Somers {
503eea7c615SAlan Somers 	FILE *pipe;
504eea7c615SAlan Somers 	char cmd[256];
505eea7c615SAlan Somers 	unsigned char e, estat, invop, info, noncrit, crit, unrecov;
506eea7c615SAlan Somers 	int r;
507eea7c615SAlan Somers 
508eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p2 %s "
509eea7c615SAlan Somers 		"| grep 'INVOP='",
510eea7c615SAlan Somers 		devname);
511eea7c615SAlan Somers 	pipe = popen(cmd, "r");
512eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
513eea7c615SAlan Somers 	r = fscanf(pipe,
514eea7c615SAlan Somers 	    "  INVOP=%hhu, INFO=%hhu, NON-CRIT=%hhu, CRIT=%hhu, UNRECOV=%hhu",
515eea7c615SAlan Somers 	    &invop, &info, &noncrit, &crit, &unrecov);
516eea7c615SAlan Somers 	pclose(pipe);
517eea7c615SAlan Somers 	if (r != 5) {
518eea7c615SAlan Somers 		/* Probably on SGPIO device */
519eea7c615SAlan Somers 		return (false);
520eea7c615SAlan Somers 	} else {
521eea7c615SAlan Somers 		r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat);
522eea7c615SAlan Somers 		ATF_REQUIRE_EQ(r, 0);
523eea7c615SAlan Somers 		/* Exclude the info bit because it changes frequently */
524eea7c615SAlan Somers 		e = (invop << 4) | (noncrit << 2) | (crit << 1) | unrecov;
525eea7c615SAlan Somers 		ATF_CHECK_EQ(estat & ~0x08, e);
526eea7c615SAlan Somers 		return (true);
527eea7c615SAlan Somers 	}
528eea7c615SAlan Somers }
529eea7c615SAlan Somers 
530eea7c615SAlan Somers ATF_TC(getencstat);
ATF_TC_HEAD(getencstat,tc)531eea7c615SAlan Somers ATF_TC_HEAD(getencstat, tc)
532eea7c615SAlan Somers {
533eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
534eea7c615SAlan Somers 	    "Compare ENCIOC_GETENCSTAT's output to sg3_utils'");
535eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
536eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
537eea7c615SAlan Somers }
ATF_TC_BODY(getencstat,tc)538eea7c615SAlan Somers ATF_TC_BODY(getencstat, tc)
539eea7c615SAlan Somers {
5402c449a4cSLi-Wen Hsu 	if (!has_ses())
5412c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
542eea7c615SAlan Somers 	for_each_ses_dev(do_getencstat, O_RDONLY);
543eea7c615SAlan Somers }
544eea7c615SAlan Somers 
545*b37d1debSAlan Somers static bool
do_getnelm(const char * devname,int fd)546*b37d1debSAlan Somers do_getnelm(const char *devname, int fd)
547*b37d1debSAlan Somers {
548eea7c615SAlan Somers 	FILE *pipe;
549eea7c615SAlan Somers 	char cmd[256];
550eea7c615SAlan Somers 	char line[256];
551eea7c615SAlan Somers 	unsigned nobj, expected = 0;
552eea7c615SAlan Somers 	int r, sg_ses_r;
553eea7c615SAlan Somers 
554eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
555eea7c615SAlan Somers 	pipe = popen(cmd, "r");
556eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
557eea7c615SAlan Somers 
558eea7c615SAlan Somers 	while(NULL != fgets(line, sizeof(line), pipe)) {
559eea7c615SAlan Somers 		unsigned nelm;
560eea7c615SAlan Somers 
561eea7c615SAlan Somers 		if (1 == fscanf(pipe, "      number of possible elements: %u",
562eea7c615SAlan Somers 		    &nelm))
563eea7c615SAlan Somers 		{
564eea7c615SAlan Somers 			expected += 1 + nelm;	// +1 for the Overall element
565eea7c615SAlan Somers 		}
566eea7c615SAlan Somers 	}
567eea7c615SAlan Somers 	sg_ses_r = pclose(pipe);
568eea7c615SAlan Somers 
569eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
570eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
571eea7c615SAlan Somers 	if (sg_ses_r == 0) {
572eea7c615SAlan Somers 		ATF_CHECK_EQ(expected, nobj);
573eea7c615SAlan Somers 		return (true);
574eea7c615SAlan Somers 	} else {
575eea7c615SAlan Somers 		/* Probably SGPIO, sg_ses unsupported */
576eea7c615SAlan Somers 		return (false);
577eea7c615SAlan Somers 	}
578eea7c615SAlan Somers }
579eea7c615SAlan Somers 
580eea7c615SAlan Somers ATF_TC(getnelm);
ATF_TC_HEAD(getnelm,tc)581eea7c615SAlan Somers ATF_TC_HEAD(getnelm, tc)
582eea7c615SAlan Somers {
583eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
584eea7c615SAlan Somers 	    "Compare ENCIOC_GETNELM's output to sg3_utils'");
585eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
586eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
587eea7c615SAlan Somers }
ATF_TC_BODY(getnelm,tc)588eea7c615SAlan Somers ATF_TC_BODY(getnelm, tc)
589eea7c615SAlan Somers {
5902c449a4cSLi-Wen Hsu 	if (!has_ses())
5912c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
592eea7c615SAlan Somers 	for_each_ses_dev(do_getnelm, O_RDONLY);
593eea7c615SAlan Somers }
594eea7c615SAlan Somers 
595*b37d1debSAlan Somers static bool
do_getstring(const char * devname,int fd)596*b37d1debSAlan Somers do_getstring(const char *devname, int fd)
597*b37d1debSAlan Somers {
598eea7c615SAlan Somers 	FILE *pipe;
599eea7c615SAlan Somers 	char cmd[256];
600eea7c615SAlan Somers 	char *sg_ses_buf, *ses_buf;
601eea7c615SAlan Somers 	ssize_t sg_ses_count;
602eea7c615SAlan Somers 	encioc_string_t str_in;
603eea7c615SAlan Somers 	int r;
604eea7c615SAlan Somers 
605eea7c615SAlan Somers 	sg_ses_buf = malloc(65535);
606eea7c615SAlan Somers 	ATF_REQUIRE(sg_ses_buf != NULL);
607eea7c615SAlan Somers 	ses_buf = malloc(65535);
608eea7c615SAlan Somers 	ATF_REQUIRE(ses_buf != NULL);
609eea7c615SAlan Somers 
610eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p4 -rr %s", devname);
611eea7c615SAlan Somers 	pipe = popen(cmd, "r");
612eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
613eea7c615SAlan Somers 	sg_ses_count = fread(sg_ses_buf, 1, 65535, pipe);
614eea7c615SAlan Somers 	r = pclose(pipe);
615eea7c615SAlan Somers 	if (r != 0) {
616eea7c615SAlan Somers 		// This SES device does not support the STRINGIN diagnostic page
617eea7c615SAlan Somers 		return (false);
618eea7c615SAlan Somers 	}
619eea7c615SAlan Somers 	ATF_REQUIRE(sg_ses_count > 0);
620eea7c615SAlan Somers 
621eea7c615SAlan Somers 	str_in.bufsiz = 65535;
622eea7c615SAlan Somers 	str_in.buf = ses_buf;
623eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETSTRING, (caddr_t) &str_in);
624eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
625eea7c615SAlan Somers 	ATF_CHECK_EQ(sg_ses_count, (ssize_t)str_in.bufsiz);
626eea7c615SAlan Somers 	ATF_CHECK_EQ(0, memcmp(sg_ses_buf, ses_buf, str_in.bufsiz));
627eea7c615SAlan Somers 
628eea7c615SAlan Somers 	free(ses_buf);
629eea7c615SAlan Somers 	free(sg_ses_buf);
630eea7c615SAlan Somers 
631eea7c615SAlan Somers 	return (true);
632eea7c615SAlan Somers }
633eea7c615SAlan Somers 
634eea7c615SAlan Somers ATF_TC(getstring);
ATF_TC_HEAD(getstring,tc)635eea7c615SAlan Somers ATF_TC_HEAD(getstring, tc)
636eea7c615SAlan Somers {
637eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
638eea7c615SAlan Somers 	    "Compare ENCIOC_GETSTRING's output to sg3_utils'");
639eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
640eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
641eea7c615SAlan Somers }
ATF_TC_BODY(getstring,tc)642eea7c615SAlan Somers ATF_TC_BODY(getstring, tc)
643eea7c615SAlan Somers {
6442c449a4cSLi-Wen Hsu 	if (!has_ses())
6452c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
646eea7c615SAlan Somers 	atf_tc_expect_fail("Bug 258188 ENCIO_GETSTRING does not set the string's returned size");
647eea7c615SAlan Somers 	for_each_ses_dev(do_getstring, O_RDWR);
648eea7c615SAlan Somers }
649eea7c615SAlan Somers 
ATF_TP_ADD_TCS(tp)650eea7c615SAlan Somers ATF_TP_ADD_TCS(tp)
651eea7c615SAlan Somers {
652eea7c615SAlan Somers 
653eea7c615SAlan Somers 	/*
654eea7c615SAlan Somers 	 * Untested ioctls:
655eea7c615SAlan Somers 	 *
656eea7c615SAlan Somers 	 * * ENCIOC_GETTEXT because it was never implemented
657eea7c615SAlan Somers 	 *
658eea7c615SAlan Somers 	 */
659eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getelmdesc);
660eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getelmdevnames);
661eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getelmmap);
662eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getelmstat);
663eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getencid);
664eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getencname);
665eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getencstat);
666eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getnelm);
667eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getstring);
668eea7c615SAlan Somers 
669eea7c615SAlan Somers 	return (atf_no_error());
670eea7c615SAlan Somers }
671