xref: /freebsd/tests/sys/ses/nondestructive.c (revision 2c449a4c5a338d2dd39a1949524e57c06ce7eff6)
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  * $FreeBSD$
26eea7c615SAlan Somers  */
27eea7c615SAlan Somers 
28eea7c615SAlan Somers /* Basic smoke test of the ioctl interface */
29eea7c615SAlan Somers 
30eea7c615SAlan Somers #include <sys/types.h>
31eea7c615SAlan Somers #include <sys/ioctl.h>
32eea7c615SAlan Somers 
33eea7c615SAlan Somers #include <atf-c.h>
34eea7c615SAlan Somers #include <fcntl.h>
35eea7c615SAlan Somers #include <glob.h>
36eea7c615SAlan Somers #include <regex.h>
37eea7c615SAlan Somers #include <stdint.h>
38eea7c615SAlan Somers #include <stdio.h>
39eea7c615SAlan Somers #include <stdlib.h>
40eea7c615SAlan Somers 
41eea7c615SAlan Somers #include <cam/scsi/scsi_enc.h>
42eea7c615SAlan Somers 
43eea7c615SAlan Somers #include "common.h"
44eea7c615SAlan Somers 
45eea7c615SAlan Somers static bool do_getelmdesc(const char *devname, int fd) {
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);
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 }
121eea7c615SAlan Somers ATF_TC_BODY(getelmdesc, tc)
122eea7c615SAlan Somers {
123*2c449a4cSLi-Wen Hsu 	if (!has_ses())
124*2c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
125eea7c615SAlan Somers 	for_each_ses_dev(do_getelmdesc, O_RDONLY);
126eea7c615SAlan Somers }
127eea7c615SAlan Somers 
128eea7c615SAlan Somers static bool do_getelmdevnames(const char *devname __unused, int fd) {
129eea7c615SAlan Somers 	encioc_element_t *map;
130eea7c615SAlan Somers 	unsigned nobj;
131eea7c615SAlan Somers 	const size_t namesize = 128;
132eea7c615SAlan Somers 	int r;
133eea7c615SAlan Somers 	char *namebuf;
134eea7c615SAlan Somers 	unsigned elm_idx;
135eea7c615SAlan Somers 
136eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
137eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
138eea7c615SAlan Somers 
139eea7c615SAlan Somers 	namebuf = calloc(namesize, sizeof(char));
140eea7c615SAlan Somers 	ATF_REQUIRE(namebuf != NULL);
141eea7c615SAlan Somers 	map = calloc(nobj, sizeof(encioc_element_t));
142eea7c615SAlan Somers 	ATF_REQUIRE(map != NULL);
143eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
144eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
145eea7c615SAlan Somers 
146eea7c615SAlan Somers 	for (elm_idx = 0; elm_idx < nobj; elm_idx++) {
147eea7c615SAlan Somers 		/*
148eea7c615SAlan Somers 		 * devnames should be present if:
149eea7c615SAlan Somers 		 * * The element is of type Device Slot or Array Device Slot
150eea7c615SAlan Somers 		 * * It isn't an Overall Element
151eea7c615SAlan Somers 		 * * The element's status is not "Not Installed"
152eea7c615SAlan Somers 		 */
153eea7c615SAlan Somers 		encioc_elm_status_t e_status;
154eea7c615SAlan Somers 		encioc_elm_devnames_t elmdn;
155eea7c615SAlan Somers 
156eea7c615SAlan Somers 		memset(&e_status, 0, sizeof(e_status));
157eea7c615SAlan Somers 		e_status.elm_idx = elm_idx;
158eea7c615SAlan Somers 		r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&e_status);
159eea7c615SAlan Somers 		ATF_REQUIRE_EQ(r, 0);
160eea7c615SAlan Somers 
161eea7c615SAlan Somers 		memset(&elmdn, 0, sizeof(elmdn));
162eea7c615SAlan Somers 		elmdn.elm_idx = elm_idx;
163eea7c615SAlan Somers 		elmdn.elm_names_size = namesize;
164eea7c615SAlan Somers 		elmdn.elm_devnames = namebuf;
165eea7c615SAlan Somers 		namebuf[0] = '\0';
166eea7c615SAlan Somers 		r = ioctl(fd, ENCIOC_GETELMDEVNAMES, (caddr_t) &elmdn);
167eea7c615SAlan Somers 		if (e_status.cstat[0] != SES_OBJSTAT_UNSUPPORTED &&
168eea7c615SAlan Somers 		    e_status.cstat[0] != SES_OBJSTAT_NOTINSTALLED &&
169eea7c615SAlan Somers 		    (map[elm_idx].elm_type == ELMTYP_DEVICE ||
170eea7c615SAlan Somers 		     map[elm_idx].elm_type == ELMTYP_ARRAY_DEV))
171eea7c615SAlan Somers 		{
172eea7c615SAlan 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");
173eea7c615SAlan Somers 		} else {
174eea7c615SAlan Somers 			ATF_CHECK(r != 0);
175eea7c615SAlan Somers 		}
176eea7c615SAlan Somers 
177eea7c615SAlan Somers 		if (r == 0) {
178eea7c615SAlan Somers 			size_t z = 0;
179eea7c615SAlan Somers 			int da = 0, ada = 0, pass = 0, nvd = 0;
180eea7c615SAlan Somers 			int nvme = 0, unknown = 0;
181eea7c615SAlan Somers 
182eea7c615SAlan Somers 			while(elmdn.elm_devnames[z] != '\0') {
183eea7c615SAlan Somers 				size_t e;
184eea7c615SAlan Somers 				char *s;
185eea7c615SAlan Somers 
186eea7c615SAlan Somers 				if (elmdn.elm_devnames[z] == ',')
187eea7c615SAlan Somers 					z++;	/* Skip the comma */
188eea7c615SAlan Somers 				s = elmdn.elm_devnames + z;
189eea7c615SAlan Somers 				e = strcspn(s, "0123456789");
190eea7c615SAlan Somers 				if (0 == strncmp("da", s, e))
191eea7c615SAlan Somers 					da++;
192eea7c615SAlan Somers 				else if (0 == strncmp("ada", s, e))
193eea7c615SAlan Somers 					ada++;
194eea7c615SAlan Somers 				else if (0 == strncmp("pass", s, e))
195eea7c615SAlan Somers 					pass++;
196eea7c615SAlan Somers 				else if (0 == strncmp("nvd", s, e))
197eea7c615SAlan Somers 					nvd++;
198eea7c615SAlan Somers 				else if (0 == strncmp("nvme", s, e))
199eea7c615SAlan Somers 					nvme++;
200eea7c615SAlan Somers 				else
201eea7c615SAlan Somers 					unknown++;
202eea7c615SAlan Somers 				z += strcspn(elmdn.elm_devnames + z, ",");
203eea7c615SAlan Somers 			}
204eea7c615SAlan Somers 			/* There should be one pass dev for each non-pass dev */
205eea7c615SAlan Somers 			ATF_CHECK_EQ(pass, da + ada + nvd + nvme);
206eea7c615SAlan Somers 			ATF_CHECK_EQ_MSG(0, unknown,
207eea7c615SAlan Somers 			    "Unknown device names %s", elmdn.elm_devnames);
208eea7c615SAlan Somers 		}
209eea7c615SAlan Somers 	}
210eea7c615SAlan Somers 	free(map);
211eea7c615SAlan Somers 	free(namebuf);
212eea7c615SAlan Somers 
213eea7c615SAlan Somers 	return (true);
214eea7c615SAlan Somers }
215eea7c615SAlan Somers 
216eea7c615SAlan Somers ATF_TC(getelmdevnames);
217eea7c615SAlan Somers ATF_TC_HEAD(getelmdevnames, tc)
218eea7c615SAlan Somers {
219eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
220eea7c615SAlan Somers 	    "Compare ENCIOC_GETELMDEVNAMES's output to sg3_utils'");
221eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
222eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
223eea7c615SAlan Somers }
224eea7c615SAlan Somers ATF_TC_BODY(getelmdevnames, tc)
225eea7c615SAlan Somers {
226*2c449a4cSLi-Wen Hsu 	if (!has_ses())
227*2c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
228eea7c615SAlan Somers 	for_each_ses_dev(do_getelmdevnames, O_RDONLY);
229eea7c615SAlan Somers }
230eea7c615SAlan Somers 
231eea7c615SAlan Somers static int
232eea7c615SAlan Somers elm_type_name2int(const char *name) {
233eea7c615SAlan Somers 	const char *elm_type_names[] = ELM_TYPE_NAMES;
234eea7c615SAlan Somers 	int i;
235eea7c615SAlan Somers 
236eea7c615SAlan Somers 	for (i = 0; i <= ELMTYP_LAST; i++) {
237eea7c615SAlan Somers 		/* sg_ses uses different case than ses(4) */
238eea7c615SAlan Somers 		if (0 == strcasecmp(name, elm_type_names[i]))
239eea7c615SAlan Somers 			return i;
240eea7c615SAlan Somers 	}
241eea7c615SAlan Somers 	return (-1);
242eea7c615SAlan Somers }
243eea7c615SAlan Somers 
244eea7c615SAlan Somers static bool do_getelmmap(const char *devname, int fd) {
245eea7c615SAlan Somers 	encioc_element_t *map;
246eea7c615SAlan Somers 	FILE *pipe;
247eea7c615SAlan Somers 	char cmd[256];
248eea7c615SAlan Somers 	char line[256];
249eea7c615SAlan Somers 	unsigned elm_idx = 0;
250eea7c615SAlan Somers 	unsigned nobj, subenc_id;
251eea7c615SAlan Somers 	int r, elm_type;
252eea7c615SAlan Somers 
253eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
254eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
255eea7c615SAlan Somers 
256eea7c615SAlan Somers 	map = calloc(nobj, sizeof(encioc_element_t));
257eea7c615SAlan Somers 	ATF_REQUIRE(map != NULL);
258eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
259eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
260eea7c615SAlan Somers 
261eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
262eea7c615SAlan Somers 	pipe = popen(cmd, "r");
263eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
264eea7c615SAlan Somers 	while(NULL != fgets(line, sizeof(line), pipe)) {
265eea7c615SAlan Somers 		char elm_type_name[80];
266eea7c615SAlan Somers 		int i, num_elm;
267eea7c615SAlan Somers 
268eea7c615SAlan Somers 		r = sscanf(line,
269eea7c615SAlan Somers 		    "    Element type: %[a-zA-Z0-9_ /], subenclosure id: %d",
270eea7c615SAlan Somers 		    elm_type_name, &subenc_id);
271eea7c615SAlan Somers 		if (r == 2) {
272eea7c615SAlan Somers 			elm_type = elm_type_name2int(elm_type_name);
273eea7c615SAlan Somers 			continue;
274eea7c615SAlan Somers 		} else {
275eea7c615SAlan Somers 			r = sscanf(line,
276eea7c615SAlan Somers 			    "    Element type: vendor specific [0x%x], subenclosure id: %d",
277eea7c615SAlan Somers 			    &elm_type, &subenc_id);
278eea7c615SAlan Somers 			if (r == 2)
279eea7c615SAlan Somers 				continue;
280eea7c615SAlan Somers 		}
281eea7c615SAlan Somers 		r = sscanf(line, "      number of possible elements: %d",
282eea7c615SAlan Somers 		    &num_elm);
283eea7c615SAlan Somers 		if (r != 1)
284eea7c615SAlan Somers 			continue;
285eea7c615SAlan Somers 
286eea7c615SAlan Somers 		/* Skip the Overall elements */
287eea7c615SAlan Somers 		elm_idx++;
288eea7c615SAlan Somers 		for (i = 0; i < num_elm; i++, elm_idx++) {
289eea7c615SAlan Somers 			ATF_CHECK_EQ(map[elm_idx].elm_idx, elm_idx);
290eea7c615SAlan Somers 			ATF_CHECK_EQ(map[elm_idx].elm_subenc_id, subenc_id);
291eea7c615SAlan Somers 			ATF_CHECK_EQ((int)map[elm_idx].elm_type, elm_type);
292eea7c615SAlan Somers 		}
293eea7c615SAlan Somers 	}
294eea7c615SAlan Somers 
295eea7c615SAlan Somers 	free(map);
296eea7c615SAlan Somers 	r = pclose(pipe);
297eea7c615SAlan Somers 	if (r != 0) {
298eea7c615SAlan Somers 		/* Probably an SGPIO device */
299eea7c615SAlan Somers 		return (false);
300eea7c615SAlan Somers 	} else {
301eea7c615SAlan Somers 		ATF_CHECK_EQ_MSG(nobj, elm_idx,
302eea7c615SAlan Somers 				"Did not find the expected number of element "
303eea7c615SAlan Somers 				"descriptors in sg_ses's output");
304eea7c615SAlan Somers 		return (true);
305eea7c615SAlan Somers 	}
306eea7c615SAlan Somers }
307eea7c615SAlan Somers 
308eea7c615SAlan Somers ATF_TC(getelmmap);
309eea7c615SAlan Somers ATF_TC_HEAD(getelmmap, tc)
310eea7c615SAlan Somers {
311eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
312eea7c615SAlan Somers 	    "Compare ENCIOC_GETELMMAP's output to sg3_utils'");
313eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
314eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
315eea7c615SAlan Somers }
316eea7c615SAlan Somers ATF_TC_BODY(getelmmap, tc)
317eea7c615SAlan Somers {
318*2c449a4cSLi-Wen Hsu 	if (!has_ses())
319*2c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
320eea7c615SAlan Somers 	for_each_ses_dev(do_getelmmap, O_RDONLY);
321eea7c615SAlan Somers }
322eea7c615SAlan Somers 
323eea7c615SAlan Somers static bool do_getelmstat(const char *devname, int fd) {
324eea7c615SAlan Somers 	encioc_element_t *map;
325eea7c615SAlan Somers 	unsigned elm_idx;
326eea7c615SAlan Somers 	unsigned nobj;
327eea7c615SAlan Somers 	int r, elm_subidx;
328eea7c615SAlan Somers 	elm_type_t last_elm_type = -1;
329eea7c615SAlan Somers 
330eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
331eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
332eea7c615SAlan Somers 
333eea7c615SAlan Somers 	map = calloc(nobj, sizeof(encioc_element_t));
334eea7c615SAlan Somers 	ATF_REQUIRE(map != NULL);
335eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) map);
336eea7c615SAlan Somers 
337eea7c615SAlan Somers 	for (elm_idx = 0; elm_idx < nobj; elm_subidx++, elm_idx++) {
338eea7c615SAlan Somers 		encioc_elm_status_t e_status;
339eea7c615SAlan Somers 		FILE *pipe;
340eea7c615SAlan Somers 		char cmd[256];
341eea7c615SAlan Somers 		uint32_t status;
342eea7c615SAlan Somers 		int pr;
343eea7c615SAlan Somers 
344eea7c615SAlan Somers 		if (last_elm_type != map[elm_idx].elm_type)
345eea7c615SAlan Somers 			elm_subidx = -1;
346eea7c615SAlan Somers 		last_elm_type = map[elm_idx].elm_type;
347eea7c615SAlan Somers 
348eea7c615SAlan Somers 		snprintf(cmd, sizeof(cmd),
349eea7c615SAlan Somers 		    "sg_ses -Hp2 --index=_%d,%d --get=0:7:32 %s",
350eea7c615SAlan Somers 		    map[elm_idx].elm_type, elm_subidx, devname);
351eea7c615SAlan Somers 		pipe = popen(cmd, "r");
352eea7c615SAlan Somers 		ATF_REQUIRE(pipe != NULL);
353eea7c615SAlan Somers 		r = fscanf(pipe, "0x%x", &status);
354eea7c615SAlan Somers 		pr = pclose(pipe);
355eea7c615SAlan Somers 		if (pr != 0) {
356eea7c615SAlan Somers 			/* Probably an SGPIO device */
357eea7c615SAlan Somers 			free(map);
358eea7c615SAlan Somers 			return (false);
359eea7c615SAlan Somers 		}
360eea7c615SAlan Somers 		ATF_REQUIRE_EQ(r, 1);
361eea7c615SAlan Somers 
362eea7c615SAlan Somers 		memset(&e_status, 0, sizeof(e_status));
363eea7c615SAlan Somers 		e_status.elm_idx = map[elm_idx].elm_idx;
364eea7c615SAlan Somers 		r = ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t)&e_status);
365eea7c615SAlan Somers 		ATF_REQUIRE_EQ(r, 0);
366eea7c615SAlan Somers 
367eea7c615SAlan Somers 		// Compare the common status field
368eea7c615SAlan Somers 		ATF_CHECK_EQ(e_status.cstat[0], status >> 24);
369eea7c615SAlan Somers 		/*
370eea7c615SAlan Somers 		 * Ignore the other fields, because some have values that can
371eea7c615SAlan Somers 		 * change frequently (voltage, temperature, etc)
372eea7c615SAlan Somers 		 */
373eea7c615SAlan Somers 	}
374eea7c615SAlan Somers 	free(map);
375eea7c615SAlan Somers 
376eea7c615SAlan Somers 	return (true);
377eea7c615SAlan Somers }
378eea7c615SAlan Somers 
379eea7c615SAlan Somers ATF_TC(getelmstat);
380eea7c615SAlan Somers ATF_TC_HEAD(getelmstat, tc)
381eea7c615SAlan Somers {
382eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
383eea7c615SAlan Somers 	    "Compare ENCIOC_GETELMSTAT's output to sg3_utils'");
384eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
385eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
386eea7c615SAlan Somers }
387eea7c615SAlan Somers ATF_TC_BODY(getelmstat, tc)
388eea7c615SAlan Somers {
389*2c449a4cSLi-Wen Hsu 	if (!has_ses())
390*2c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
391eea7c615SAlan Somers 	for_each_ses_dev(do_getelmstat, O_RDONLY);
392eea7c615SAlan Somers }
393eea7c615SAlan Somers 
394eea7c615SAlan Somers static bool do_getencid(const char *devname, int fd) {
395eea7c615SAlan Somers 	encioc_string_t stri;
396eea7c615SAlan Somers 	FILE *pipe;
397eea7c615SAlan Somers 	char cmd[256];
398eea7c615SAlan Somers 	char encid[32];
399eea7c615SAlan Somers 	char line[256];
400eea7c615SAlan Somers 	char sg_encid[32];
401eea7c615SAlan Somers 	int r, sg_ses_r;
402eea7c615SAlan Somers 
403eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
404eea7c615SAlan Somers 	pipe = popen(cmd, "r");
405eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
406eea7c615SAlan Somers 	sg_encid[0] = '\0';
407eea7c615SAlan Somers 	while(NULL != fgets(line, sizeof(line), pipe)) {
408eea7c615SAlan Somers 		const char *f = "      enclosure logical identifier (hex): %s";
409eea7c615SAlan Somers 
410eea7c615SAlan Somers 		if (1 == fscanf(pipe, f, sg_encid))
411eea7c615SAlan Somers 			break;
412eea7c615SAlan Somers 	}
413eea7c615SAlan Somers 	sg_ses_r = pclose(pipe);
414eea7c615SAlan Somers 
415eea7c615SAlan Somers 	stri.bufsiz = sizeof(encid);
416eea7c615SAlan Somers 	stri.buf = &encid[0];
417eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETENCID, (caddr_t) &stri);
418eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
419eea7c615SAlan Somers 	if (sg_ses_r == 0) {
420eea7c615SAlan Somers 		ATF_REQUIRE(sg_encid[0] != '\0');
421eea7c615SAlan Somers 		ATF_CHECK_STREQ(sg_encid, (char*)stri.buf);
422eea7c615SAlan Somers 		return (true);
423eea7c615SAlan Somers 	} else {
424eea7c615SAlan Somers 		/* Probably SGPIO; sg_ses unsupported */
425eea7c615SAlan Somers 		return (false);
426eea7c615SAlan Somers 	}
427eea7c615SAlan Somers }
428eea7c615SAlan Somers 
429eea7c615SAlan Somers ATF_TC(getencid);
430eea7c615SAlan Somers ATF_TC_HEAD(getencid, tc)
431eea7c615SAlan Somers {
432eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
433eea7c615SAlan Somers 	    "Compare ENCIOC_GETENCID's output to sg3_utils'");
434eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
435eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
436eea7c615SAlan Somers }
437eea7c615SAlan Somers ATF_TC_BODY(getencid, tc)
438eea7c615SAlan Somers {
439*2c449a4cSLi-Wen Hsu 	if (!has_ses())
440*2c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
441eea7c615SAlan Somers 	for_each_ses_dev(do_getencid, O_RDONLY);
442eea7c615SAlan Somers }
443eea7c615SAlan Somers 
444eea7c615SAlan Somers static bool do_getencname(const char *devname, int fd) {
445eea7c615SAlan Somers 	encioc_string_t stri;
446eea7c615SAlan Somers 	FILE *pipe;
447eea7c615SAlan Somers 	char cmd[256];
448eea7c615SAlan Somers 	char encname[32];
449eea7c615SAlan Somers 	char line[256];
450eea7c615SAlan Somers 	int r;
451eea7c615SAlan Somers 
452eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_inq -o %s | awk '"
453eea7c615SAlan Somers 		"/Vendor identification/ {vi=$NF} "
454eea7c615SAlan Somers 		"/Product identification/ {pi=$NF} "
455eea7c615SAlan Somers 		"/Product revision level/ {prl=$NF} "
456eea7c615SAlan Somers 		"END {printf(vi \" \" pi \" \" prl)}'", devname);
457eea7c615SAlan Somers 	pipe = popen(cmd, "r");
458eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
459eea7c615SAlan Somers 	ATF_REQUIRE(NULL != fgets(line, sizeof(line), pipe));
460eea7c615SAlan Somers 	pclose(pipe);
461eea7c615SAlan Somers 
462eea7c615SAlan Somers 	stri.bufsiz = sizeof(encname);
463eea7c615SAlan Somers 	stri.buf = &encname[0];
464eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETENCNAME, (caddr_t) &stri);
465eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
466eea7c615SAlan Somers 	if (strlen(line) < 3) {
467eea7c615SAlan Somers 		// Probably an SGPIO device, INQUIRY unsupported
468eea7c615SAlan Somers 		return (false);
469eea7c615SAlan Somers 	} else {
470eea7c615SAlan Somers 		ATF_CHECK_STREQ(line, (char*)stri.buf);
471eea7c615SAlan Somers 		return (true);
472eea7c615SAlan Somers 	}
473eea7c615SAlan Somers }
474eea7c615SAlan Somers 
475eea7c615SAlan Somers ATF_TC(getencname);
476eea7c615SAlan Somers ATF_TC_HEAD(getencname, tc)
477eea7c615SAlan Somers {
478eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
479eea7c615SAlan Somers 	    "Compare ENCIOC_GETENCNAME's output to sg3_utils'");
480eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
481eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_inq");
482eea7c615SAlan Somers }
483eea7c615SAlan Somers ATF_TC_BODY(getencname, tc)
484eea7c615SAlan Somers {
485*2c449a4cSLi-Wen Hsu 	if (!has_ses())
486*2c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
487eea7c615SAlan Somers 	for_each_ses_dev(do_getencname, O_RDONLY);
488eea7c615SAlan Somers }
489eea7c615SAlan Somers 
490eea7c615SAlan Somers static bool do_getencstat(const char *devname, int fd) {
491eea7c615SAlan Somers 	FILE *pipe;
492eea7c615SAlan Somers 	char cmd[256];
493eea7c615SAlan Somers 	unsigned char e, estat, invop, info, noncrit, crit, unrecov;
494eea7c615SAlan Somers 	int r;
495eea7c615SAlan Somers 
496eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p2 %s "
497eea7c615SAlan Somers 		"| grep 'INVOP='",
498eea7c615SAlan Somers 		devname);
499eea7c615SAlan Somers 	pipe = popen(cmd, "r");
500eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
501eea7c615SAlan Somers 	r = fscanf(pipe,
502eea7c615SAlan Somers 	    "  INVOP=%hhu, INFO=%hhu, NON-CRIT=%hhu, CRIT=%hhu, UNRECOV=%hhu",
503eea7c615SAlan Somers 	    &invop, &info, &noncrit, &crit, &unrecov);
504eea7c615SAlan Somers 	pclose(pipe);
505eea7c615SAlan Somers 	if (r != 5) {
506eea7c615SAlan Somers 		/* Probably on SGPIO device */
507eea7c615SAlan Somers 		return (false);
508eea7c615SAlan Somers 	} else {
509eea7c615SAlan Somers 		r = ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat);
510eea7c615SAlan Somers 		ATF_REQUIRE_EQ(r, 0);
511eea7c615SAlan Somers 		/* Exclude the info bit because it changes frequently */
512eea7c615SAlan Somers 		e = (invop << 4) | (noncrit << 2) | (crit << 1) | unrecov;
513eea7c615SAlan Somers 		ATF_CHECK_EQ(estat & ~0x08, e);
514eea7c615SAlan Somers 		return (true);
515eea7c615SAlan Somers 	}
516eea7c615SAlan Somers }
517eea7c615SAlan Somers 
518eea7c615SAlan Somers ATF_TC(getencstat);
519eea7c615SAlan Somers ATF_TC_HEAD(getencstat, tc)
520eea7c615SAlan Somers {
521eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
522eea7c615SAlan Somers 	    "Compare ENCIOC_GETENCSTAT's output to sg3_utils'");
523eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
524eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
525eea7c615SAlan Somers }
526eea7c615SAlan Somers ATF_TC_BODY(getencstat, tc)
527eea7c615SAlan Somers {
528*2c449a4cSLi-Wen Hsu 	if (!has_ses())
529*2c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
530eea7c615SAlan Somers 	for_each_ses_dev(do_getencstat, O_RDONLY);
531eea7c615SAlan Somers }
532eea7c615SAlan Somers 
533eea7c615SAlan Somers static bool do_getnelm(const char *devname, int fd) {
534eea7c615SAlan Somers 	FILE *pipe;
535eea7c615SAlan Somers 	char cmd[256];
536eea7c615SAlan Somers 	char line[256];
537eea7c615SAlan Somers 	unsigned nobj, expected = 0;
538eea7c615SAlan Somers 	int r, sg_ses_r;
539eea7c615SAlan Somers 
540eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p1 %s", devname);
541eea7c615SAlan Somers 	pipe = popen(cmd, "r");
542eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
543eea7c615SAlan Somers 
544eea7c615SAlan Somers 	while(NULL != fgets(line, sizeof(line), pipe)) {
545eea7c615SAlan Somers 		unsigned nelm;
546eea7c615SAlan Somers 
547eea7c615SAlan Somers 		if (1 == fscanf(pipe, "      number of possible elements: %u",
548eea7c615SAlan Somers 		    &nelm))
549eea7c615SAlan Somers 		{
550eea7c615SAlan Somers 			expected += 1 + nelm;	// +1 for the Overall element
551eea7c615SAlan Somers 		}
552eea7c615SAlan Somers 	}
553eea7c615SAlan Somers 	sg_ses_r = pclose(pipe);
554eea7c615SAlan Somers 
555eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj);
556eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
557eea7c615SAlan Somers 	if (sg_ses_r == 0) {
558eea7c615SAlan Somers 		ATF_CHECK_EQ(expected, nobj);
559eea7c615SAlan Somers 		return (true);
560eea7c615SAlan Somers 	} else {
561eea7c615SAlan Somers 		/* Probably SGPIO, sg_ses unsupported */
562eea7c615SAlan Somers 		return (false);
563eea7c615SAlan Somers 	}
564eea7c615SAlan Somers }
565eea7c615SAlan Somers 
566eea7c615SAlan Somers ATF_TC(getnelm);
567eea7c615SAlan Somers ATF_TC_HEAD(getnelm, tc)
568eea7c615SAlan Somers {
569eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
570eea7c615SAlan Somers 	    "Compare ENCIOC_GETNELM's output to sg3_utils'");
571eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
572eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
573eea7c615SAlan Somers }
574eea7c615SAlan Somers ATF_TC_BODY(getnelm, tc)
575eea7c615SAlan Somers {
576*2c449a4cSLi-Wen Hsu 	if (!has_ses())
577*2c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
578eea7c615SAlan Somers 	for_each_ses_dev(do_getnelm, O_RDONLY);
579eea7c615SAlan Somers }
580eea7c615SAlan Somers 
581eea7c615SAlan Somers static bool do_getstring(const char *devname, int fd) {
582eea7c615SAlan Somers 	FILE *pipe;
583eea7c615SAlan Somers 	char cmd[256];
584eea7c615SAlan Somers 	char *sg_ses_buf, *ses_buf;
585eea7c615SAlan Somers 	ssize_t sg_ses_count;
586eea7c615SAlan Somers 	encioc_string_t str_in;
587eea7c615SAlan Somers 	int r;
588eea7c615SAlan Somers 
589eea7c615SAlan Somers 	sg_ses_buf = malloc(65535);
590eea7c615SAlan Somers 	ATF_REQUIRE(sg_ses_buf != NULL);
591eea7c615SAlan Somers 	ses_buf = malloc(65535);
592eea7c615SAlan Somers 	ATF_REQUIRE(ses_buf != NULL);
593eea7c615SAlan Somers 
594eea7c615SAlan Somers 	snprintf(cmd, sizeof(cmd), "sg_ses -p4 -rr %s", devname);
595eea7c615SAlan Somers 	pipe = popen(cmd, "r");
596eea7c615SAlan Somers 	ATF_REQUIRE(pipe != NULL);
597eea7c615SAlan Somers 	sg_ses_count = fread(sg_ses_buf, 1, 65535, pipe);
598eea7c615SAlan Somers 	r = pclose(pipe);
599eea7c615SAlan Somers 	if (r != 0) {
600eea7c615SAlan Somers 		// This SES device does not support the STRINGIN diagnostic page
601eea7c615SAlan Somers 		return (false);
602eea7c615SAlan Somers 	}
603eea7c615SAlan Somers 	ATF_REQUIRE(sg_ses_count > 0);
604eea7c615SAlan Somers 
605eea7c615SAlan Somers 	str_in.bufsiz = 65535;
606eea7c615SAlan Somers 	str_in.buf = ses_buf;
607eea7c615SAlan Somers 	r = ioctl(fd, ENCIOC_GETSTRING, (caddr_t) &str_in);
608eea7c615SAlan Somers 	ATF_REQUIRE_EQ(r, 0);
609eea7c615SAlan Somers 	ATF_CHECK_EQ(sg_ses_count, (ssize_t)str_in.bufsiz);
610eea7c615SAlan Somers 	ATF_CHECK_EQ(0, memcmp(sg_ses_buf, ses_buf, str_in.bufsiz));
611eea7c615SAlan Somers 
612eea7c615SAlan Somers 	free(ses_buf);
613eea7c615SAlan Somers 	free(sg_ses_buf);
614eea7c615SAlan Somers 
615eea7c615SAlan Somers 	return (true);
616eea7c615SAlan Somers }
617eea7c615SAlan Somers 
618eea7c615SAlan Somers ATF_TC(getstring);
619eea7c615SAlan Somers ATF_TC_HEAD(getstring, tc)
620eea7c615SAlan Somers {
621eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "descr",
622eea7c615SAlan Somers 	    "Compare ENCIOC_GETSTRING's output to sg3_utils'");
623eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.user", "root");
624eea7c615SAlan Somers 	atf_tc_set_md_var(tc, "require.progs", "sg_ses");
625eea7c615SAlan Somers }
626eea7c615SAlan Somers ATF_TC_BODY(getstring, tc)
627eea7c615SAlan Somers {
628*2c449a4cSLi-Wen Hsu 	if (!has_ses())
629*2c449a4cSLi-Wen Hsu 		atf_tc_skip("No ses devices found");
630eea7c615SAlan Somers 	atf_tc_expect_fail("Bug 258188 ENCIO_GETSTRING does not set the string's returned size");
631eea7c615SAlan Somers 	for_each_ses_dev(do_getstring, O_RDWR);
632eea7c615SAlan Somers }
633eea7c615SAlan Somers 
634eea7c615SAlan Somers ATF_TP_ADD_TCS(tp)
635eea7c615SAlan Somers {
636eea7c615SAlan Somers 
637eea7c615SAlan Somers 	/*
638eea7c615SAlan Somers 	 * Untested ioctls:
639eea7c615SAlan Somers 	 *
640eea7c615SAlan Somers 	 * * ENCIOC_GETTEXT because it was never implemented
641eea7c615SAlan Somers 	 *
642eea7c615SAlan Somers 	 */
643eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getelmdesc);
644eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getelmdevnames);
645eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getelmmap);
646eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getelmstat);
647eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getencid);
648eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getencname);
649eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getencstat);
650eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getnelm);
651eea7c615SAlan Somers 	ATF_TP_ADD_TC(tp, getstring);
652eea7c615SAlan Somers 
653eea7c615SAlan Somers 	return (atf_no_error());
654eea7c615SAlan Somers }
655