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