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