xref: /aosp_15_r20/external/sg3_utils/src/sg_safte.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2004-2018 Hannes Reinecke and Douglas Gilbert.
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  */
9 
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <stdint.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <getopt.h>
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "sg_lib.h"
26 #include "sg_cmds_basic.h"
27 #include "sg_cmds_extra.h"
28 #include "sg_unaligned.h"
29 #include "sg_pr2serr.h"
30 
31 /* A utility program for the Linux OS SCSI subsystem.
32  *
33  *  This program accesses a processor device which operates according
34  *  to the 'SCSI Accessed Fault-Tolerant Enclosures' (SAF-TE) spec.
35  */
36 
37 static const char * version_str = "0.33 20180628";
38 
39 
40 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
41 #define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
42 #define EBUFF_SZ 256
43 
44 #define RB_MODE_DESC 3
45 #define RWB_MODE_DATA 2
46 #define RWB_MODE_VENDOR 1
47 #define RB_DESC_LEN 4
48 
49 #define SAFTE_CFG_FLAG_DOORLOCK 1
50 #define SAFTE_CFG_FLAG_ALARM 2
51 #define SAFTE_CFG_FLAG_CELSIUS 3
52 
53 struct safte_cfg_t {
54     int fans;
55     int psupplies;
56     int slots;
57     int temps;
58     int thermostats;
59     int vendor_specific;
60     int flags;
61 };
62 
63 struct safte_cfg_t safte_cfg;
64 
65 static unsigned int buf_capacity = 64;
66 
67 static void
dStrRaw(const uint8_t * str,int len)68 dStrRaw(const uint8_t * str, int len)
69 {
70     int k;
71 
72     for (k = 0; k < len; ++k)
73         printf("%c", str[k]);
74 }
75 
76 /* Buffer ID 0x0: Read Enclosure Configuration (mandatory) */
77 static int
read_safte_configuration(int sg_fd,uint8_t * rb_buff,unsigned int rb_len,int verbose)78 read_safte_configuration(int sg_fd, uint8_t *rb_buff,
79                          unsigned int rb_len, int verbose)
80 {
81     int res;
82 
83     if (rb_len < buf_capacity) {
84         pr2serr("SCSI BUFFER size too small (%d/%d bytes)\n", rb_len,
85                 buf_capacity);
86         return SG_LIB_CAT_ILLEGAL_REQ;
87     }
88 
89     if (verbose > 1)
90         pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=0 to fetch "
91                 "configuration\n");
92     res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 0, 0,
93                             rb_buff, rb_len, true, verbose);
94     if (res && res != SG_LIB_CAT_RECOVERED)
95         return res;
96 
97     safte_cfg.fans = rb_buff[0];
98     safte_cfg.psupplies = rb_buff[1];
99     safte_cfg.slots = rb_buff[2];
100     safte_cfg.temps = rb_buff[4];
101     if (rb_buff[3])
102         safte_cfg.flags |= SAFTE_CFG_FLAG_DOORLOCK;
103     if (rb_buff[5])
104         safte_cfg.flags |= SAFTE_CFG_FLAG_ALARM;
105     if (rb_buff[6] & 0x80)
106         safte_cfg.flags |= SAFTE_CFG_FLAG_CELSIUS;
107 
108     safte_cfg.thermostats = rb_buff[6] & 0x0f;
109     safte_cfg.vendor_specific = rb_buff[63];
110 
111     return 0;
112 }
113 
114 static int
print_safte_configuration(void)115 print_safte_configuration(void)
116 {
117     printf("Enclosure Configuration:\n");
118     printf("\tNumber of Fans: %d\n", safte_cfg.fans);
119     printf("\tNumber of Power Supplies: %d\n", safte_cfg.psupplies);
120     printf("\tNumber of Device Slots: %d\n", safte_cfg.slots);
121     printf("\tNumber of Temperature Sensors: %d\n", safte_cfg.temps);
122     printf("\tNumber of Thermostats: %d\n", safte_cfg.thermostats);
123     printf("\tVendor unique bytes: %d\n", safte_cfg.vendor_specific);
124 
125     return 0;
126 }
127 
128 /* Buffer ID 0x01: Read Enclosure Status (mandatory) */
129 static int
do_safte_encl_status(int sg_fd,int do_hex,int do_raw,int verbose)130 do_safte_encl_status(int sg_fd, int do_hex, int do_raw, int verbose)
131 {
132     int res, i, offset;
133     unsigned int rb_len;
134     uint8_t *rb_buff;
135 
136     rb_len = safte_cfg.fans + safte_cfg.psupplies + safte_cfg.slots +
137         safte_cfg.temps + 5 + safte_cfg.vendor_specific;
138     rb_buff = (uint8_t *)malloc(rb_len);
139 
140 
141     if (verbose > 1)
142         pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=1 to read "
143                 "enclosure status\n");
144     res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 1, 0,
145                             rb_buff, rb_len, false, verbose);
146     if (res && res != SG_LIB_CAT_RECOVERED)
147         return res;
148 
149     if (do_raw > 1) {
150         dStrRaw(rb_buff, buf_capacity);
151         return 0;
152     }
153     if (do_hex > 1) {
154         hex2stdout(rb_buff, buf_capacity, 1);
155         return 0;
156     }
157     printf("Enclosure Status:\n");
158     offset = 0;
159     for (i = 0; i < safte_cfg.fans; i++) {
160         printf("\tFan %d status: ", i);
161         switch(rb_buff[i]) {
162             case 0:
163                 printf("operational\n");
164                 break;
165             case 1:
166                 printf("malfunctioning\n");
167                 break;
168             case 2:
169                 printf("not installed\n");
170                 break;
171             case 80:
172                 printf("not reportable\n");
173                 break;
174             default:
175                 printf("unknown\n");
176                 break;
177         }
178     }
179 
180     offset += safte_cfg.fans;
181     for (i = 0; i < safte_cfg.psupplies; i++) {
182         printf("\tPower supply %d status: ", i);
183         switch(rb_buff[i + offset]) {
184             case 0:
185                 printf("operational / on\n");
186                 break;
187             case 1:
188                 printf("operational / off\n");
189                 break;
190             case 0x10:
191                 printf("malfunctioning / on\n");
192                 break;
193             case 0x11:
194                 printf("malfunctioning / off\n");
195                 break;
196             case 0x20:
197                 printf("not present\n");
198                 break;
199             case 0x21:
200                 printf("present\n");
201                 break;
202             case 0x80:
203                 printf("not reportable\n");
204                 break;
205             default:
206                 printf("unknown\n");
207                 break;
208         }
209     }
210 
211     offset += safte_cfg.psupplies;
212     for (i = 0; i < safte_cfg.slots; i++) {
213         printf("\tDevice Slot %d: SCSI ID %d\n", i, rb_buff[i + offset]);
214     }
215 
216     offset += safte_cfg.slots;
217     if (safte_cfg.flags & SAFTE_CFG_FLAG_DOORLOCK) {
218         switch(rb_buff[offset]) {
219             case 0x0:
220                 printf("\tDoor lock status: locked\n");
221                 break;
222             case 0x01:
223                 printf("\tDoor lock status: unlocked\n");
224                 break;
225             case 0x80:
226                 printf("\tDoor lock status: not reportable\n");
227                 break;
228         }
229     } else {
230         printf("\tDoor lock status: not installed\n");
231     }
232 
233     offset++;
234     if (!(safte_cfg.flags & SAFTE_CFG_FLAG_ALARM)) {
235         printf("\tSpeaker status: not installed\n");
236     } else {
237         switch(rb_buff[offset]) {
238             case 0x0:
239                 printf("\tSpeaker status: off\n");
240                 break;
241             case 0x01:
242                 printf("\tSpeaker status: on\n");
243                 break;
244         }
245     }
246 
247     offset++;
248     for (i = 0; i < safte_cfg.temps; i++) {
249         int temp = rb_buff[i + offset];
250         int is_celsius = !!(safte_cfg.flags & SAFTE_CFG_FLAG_CELSIUS);
251 
252         if (! is_celsius)
253             temp -= 10;
254 
255         printf("\tTemperature sensor %d: %d deg %c\n", i, temp,
256                is_celsius ? 'C' : 'F');
257     }
258 
259     offset += safte_cfg.temps;
260     if (safte_cfg.thermostats) {
261         if (rb_buff[offset] & 0x80) {
262             printf("\tEnclosure Temperature alert status: abnormal\n");
263         } else {
264             printf("\tEnclosure Temperature alert status: normal\n");
265         }
266     }
267     return 0;
268 }
269 
270 /* Buffer ID 0x02: Read Usage Statistics (optional) */
271 static int
do_safte_usage_statistics(int sg_fd,int do_hex,int do_raw,int verbose)272 do_safte_usage_statistics(int sg_fd, int do_hex, int do_raw, int verbose)
273 {
274     int res;
275     unsigned int rb_len;
276     uint8_t *rb_buff;
277     unsigned int minutes;
278 
279     rb_len = 16 + safte_cfg.vendor_specific;
280     rb_buff = (uint8_t *)malloc(rb_len);
281 
282     if (verbose > 1)
283         pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=2 to read "
284                 "usage statistics\n");
285     res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 2, 0,
286                             rb_buff, rb_len, false, verbose);
287     if (res) {
288         if (res == SG_LIB_CAT_ILLEGAL_REQ) {
289             printf("Usage Statistics:\n\tNot implemented\n");
290             return 0;
291         }
292         if (res != SG_LIB_CAT_RECOVERED) {
293             free(rb_buff);
294             return res;
295         }
296     }
297 
298     if (do_raw > 1) {
299         dStrRaw(rb_buff, buf_capacity);
300         return 0;
301     }
302     if (do_hex > 1) {
303         hex2stdout(rb_buff, buf_capacity, 1);
304         return 0;
305     }
306     printf("Usage Statistics:\n");
307     minutes = sg_get_unaligned_be32(rb_buff + 0);
308     printf("\tPower on Minutes: %u\n", minutes);
309     minutes = sg_get_unaligned_be32(rb_buff + 4);
310     printf("\tPower on Cycles: %u\n", minutes);
311 
312     free(rb_buff);
313     return 0;
314 }
315 
316 /* Buffer ID 0x03: Read Device Insertions (optional) */
317 static int
do_safte_slot_insertions(int sg_fd,int do_hex,int do_raw,int verbose)318 do_safte_slot_insertions(int sg_fd, int do_hex, int do_raw, int verbose)
319 {
320     int res, i;
321     unsigned int rb_len;
322     uint8_t *rb_buff, slot_status;
323 
324     rb_len = safte_cfg.slots * 2;
325     rb_buff = (uint8_t *)malloc(rb_len);
326 
327     if (verbose > 1)
328         pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=3 to read "
329                 "device insertions\n");
330     res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 3, 0,
331                             rb_buff, rb_len, false, verbose);
332     if (res ) {
333         if (res == SG_LIB_CAT_ILLEGAL_REQ) {
334                 printf("Slot insertions:\n\tNot implemented\n");
335                 return 0;
336         }
337         if (res != SG_LIB_CAT_RECOVERED) {
338                 free(rb_buff);
339                 return res;
340         }
341     }
342 
343     if (do_raw > 1) {
344         dStrRaw(rb_buff, buf_capacity);
345         return 0;
346     }
347     if (do_hex > 1) {
348         hex2stdout(rb_buff, buf_capacity, 1);
349         return 0;
350     }
351     printf("Slot insertions:\n");
352     for (i = 0; i < safte_cfg.slots; i++) {
353         slot_status = sg_get_unaligned_be16(rb_buff + (i * 2));
354         printf("\tSlot %d: %d insertions", i, slot_status);
355     }
356     free(rb_buff);
357     return 0;
358 }
359 
360 /* Buffer ID 0x04: Read Device Slot Status (mandatory) */
361 static int
do_safte_slot_status(int sg_fd,int do_hex,int do_raw,int verbose)362 do_safte_slot_status(int sg_fd, int do_hex, int do_raw, int verbose)
363 {
364     int res, i;
365     unsigned int rb_len;
366     uint8_t *rb_buff, slot_status;
367 
368     rb_len = safte_cfg.slots * 4;
369     rb_buff = (uint8_t *)malloc(rb_len);
370 
371     if (verbose > 1)
372         pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=4 to read "
373                 "device slot status\n");
374     res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 4, 0,
375                             rb_buff, rb_len, false, verbose);
376     if (res && res != SG_LIB_CAT_RECOVERED) {
377         free(rb_buff);
378         return res;
379     }
380 
381     if (do_raw > 1) {
382         dStrRaw(rb_buff, buf_capacity);
383         return 0;
384     }
385     if (do_hex > 1) {
386         hex2stdout(rb_buff, buf_capacity, 1);
387         return 0;
388     }
389     printf("Slot status:\n");
390     for (i = 0; i < safte_cfg.slots; i++) {
391         slot_status = rb_buff[i * 4 + 3];
392         printf("\tSlot %d: ", i);
393         if (slot_status & 0x7) {
394             if (slot_status & 0x1)
395                 printf("inserted ");
396             if (slot_status & 0x2)
397                 printf("ready ");
398             if (slot_status & 0x4)
399                 printf("activated ");
400             printf("\n");
401         } else {
402             printf("empty\n");
403         }
404     }
405     free(rb_buff);
406     return 0;
407 }
408 
409 /* Buffer ID 0x05: Read Global Flags (optional) */
410 static int
do_safte_global_flags(int sg_fd,int do_hex,int do_raw,int verbose)411 do_safte_global_flags(int sg_fd, int do_hex, int do_raw, int verbose)
412 {
413     int res;
414     unsigned int rb_len;
415     uint8_t *rb_buff;
416 
417     rb_len = 16;
418     rb_buff = (uint8_t *)malloc(rb_len);
419 
420     if (verbose > 1)
421         pr2serr("Use READ BUFFER,mode=vendor_specific,buff_id=5 to read "
422                 "global flags\n");
423     res = sg_ll_read_buffer(sg_fd, RWB_MODE_VENDOR, 5, 0,
424                             rb_buff, rb_len, false, verbose);
425     if (res ) {
426         if (res == SG_LIB_CAT_ILLEGAL_REQ) {
427                 printf("Global Flags:\n\tNot implemented\n");
428                 return 0;
429         }
430         if (res != SG_LIB_CAT_RECOVERED) {
431                 free(rb_buff);
432                 return res;
433         }
434     }
435 
436     if (do_raw > 1) {
437         dStrRaw(rb_buff, buf_capacity);
438         return 0;
439     }
440     if (do_hex > 1) {
441         hex2stdout(rb_buff, buf_capacity, 1);
442         return 0;
443     }
444     printf("Global Flags:\n");
445     printf("\tAudible Alarm Control: %s\n",
446            rb_buff[0] & 0x1?"on":"off");
447     printf("\tGlobal Failure Indicator: %s\n",
448            rb_buff[0] & 0x2?"on":"off");
449     printf("\tGlobal Warning Indicator: %s\n",
450            rb_buff[0] & 0x4?"on":"off");
451     printf("\tEnclosure Power: %s\n",
452            rb_buff[0] & 0x8?"on":"off");
453     printf("\tCooling Failure: %s\n",
454            rb_buff[0] & 0x10?"yes":"no");
455     printf("\tPower Failure: %s\n",
456            rb_buff[0] & 0x20?"yes":"no");
457     printf("\tDrive Failure: %s\n",
458            rb_buff[0] & 0x40?"yes":"no");
459     printf("\tDrive Warning: %s\n",
460            rb_buff[0] & 0x80?"yes":"no");
461     printf("\tArray Failure: %s\n",
462            rb_buff[1] & 0x1?"yes":"no");
463     printf("\tArray Warning: %s\n",
464            rb_buff[0] & 0x2?"yes":"no");
465     printf("\tEnclosure Lock: %s\n",
466            rb_buff[0] & 0x4?"on":"off");
467     printf("\tEnclosure Identify: %s\n",
468            rb_buff[0] & 0x8?"on":"off");
469 
470     free(rb_buff);
471     return 0;
472 }
473 
474 static void
usage()475 usage()
476 {
477     pr2serr("Usage:  sg_safte [--config] [--devstatus] [--encstatus] "
478             "[--flags] [--help]\n"
479             "                 [--hex] [--insertions] [--raw] [--usage] "
480             "[--verbose]\n"
481             "                 [--version] DEVICE\n"
482             "  where:\n"
483             "    --config|-c         output enclosure configuration\n"
484             "    --devstatus|-d      output device slot status\n"
485             "    --encstatus|-s      output enclosure status\n"
486             "    --flags|-f          output global flags\n"
487             "    --help|-h           output command usage message then "
488             "exit\n"
489             "    --hex|-H            output enclosure config in hex\n"
490             "    --insertions|-i     output insertion statistics\n"
491             "    --raw|-r            output enclosure config in binary "
492             "to stdout\n"
493             "    --usage|-u          output usage statistics\n"
494             "    --verbose|-v        increase verbosity\n"
495             "    --version|-v        output version then exit\n\n"
496             "Queries a SAF-TE processor device\n");
497 }
498 
499 static struct option long_options[] = {
500     {"config", 0, 0, 'c'},
501     {"devstatus", 0, 0, 'd'},
502     {"encstatus", 0, 0, 's'},
503     {"flags", 0, 0, 'f'},
504     {"help", 0, 0, 'h'},
505     {"hex", 0, 0, 'H'},
506     {"insertions", 0, 0, 'i'},
507     {"raw", 0, 0, 'r'},
508     {"usage", 0, 0, 'u'},
509     {"verbose", 0, 0, 'v'},
510     {"version", 0, 0, 'V'},
511     {0, 0, 0, 0},
512 };
513 
514 int
main(int argc,char * argv[])515 main(int argc, char * argv[])
516 {
517     bool do_insertions = false;
518     bool no_hex_raw;
519     bool verbose_given = false;
520     bool version_given = false;
521     int c, ret, peri_type;
522     int sg_fd = -1;
523     int res = SG_LIB_CAT_OTHER;
524     const char * device_name = NULL;
525     char ebuff[EBUFF_SZ];
526     uint8_t *rb_buff;
527     bool do_config = false;
528     bool do_status = false;
529     bool do_slots = false;
530     bool do_flags = false;
531     bool do_usage = false;
532     int do_hex = 0;
533     int do_raw = 0;
534     int verbose = 0;
535     const char * cp;
536     char buff[48];
537     char b[80];
538     struct sg_simple_inquiry_resp inq_resp;
539     const char op_name[] = "READ BUFFER";
540 
541     while (1) {
542         int option_index = 0;
543 
544         c = getopt_long(argc, argv, "cdfhHirsuvV?", long_options,
545                         &option_index);
546 
547         if (c == -1)
548             break;
549 
550         switch (c) {
551             case 'c':
552                 do_config = true;
553                 break;
554             case 'd':
555                 do_slots = true;
556                 break;
557             case 'f':
558                 do_flags = true;
559                 break;
560             case 'h':
561             case '?':
562                 usage();
563                 return 0;
564             case 'H':
565                 ++do_hex;
566                 break;
567             case 'i':
568                 do_insertions = true;
569                 break;
570             case 'r':
571                 ++do_raw;
572                 break;
573             case 's':
574                 do_status = true;
575                 break;
576             case 'u':
577                 do_usage = true;
578                 break;
579             case 'v':
580                 verbose_given = true;
581                 ++verbose;
582                 break;
583             case 'V':
584                 version_given = true;
585                 break;
586             default:
587                 pr2serr("unrecognised option code 0x%x ??\n", c);
588                 usage();
589                 return SG_LIB_SYNTAX_ERROR;
590         }
591     }
592     if (optind < argc) {
593         if (NULL == device_name) {
594             device_name = argv[optind];
595             ++optind;
596         }
597         if (optind < argc) {
598             for (; optind < argc; ++optind)
599                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
600             usage();
601             return SG_LIB_SYNTAX_ERROR;
602         }
603     }
604 
605 #ifdef DEBUG
606     pr2serr("In DEBUG mode, ");
607     if (verbose_given && version_given) {
608         pr2serr("but override: '-vV' given, zero verbose and continue\n");
609         verbose_given = false;
610         version_given = false;
611         verbose = 0;
612     } else if (! verbose_given) {
613         pr2serr("set '-vv'\n");
614         verbose = 2;
615     } else
616         pr2serr("keep verbose=%d\n", verbose);
617 #else
618     if (verbose_given && version_given)
619         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
620 #endif
621     if (version_given) {
622         pr2serr("Version string: %s\n", version_str);
623         return 0;
624     }
625 
626     if (NULL == device_name) {
627         pr2serr("Missing device name!\n\n");
628         usage();
629         return SG_LIB_SYNTAX_ERROR;
630     }
631     if (do_raw) {
632         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
633             perror("sg_set_binary_mode");
634             return SG_LIB_FILE_ERROR;
635         }
636     }
637 
638     if ((sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose))
639         < 0) {
640         if (verbose) {
641             snprintf(ebuff, EBUFF_SZ, "sg_safte: error opening file: %s (rw)",
642                      device_name);
643             perror(ebuff);
644         }
645         ret = sg_convert_errno(-sg_fd);
646         goto fini;
647     }
648     no_hex_raw = ((0 == do_hex) && (0 == do_raw));
649 
650     if (no_hex_raw) {
651         if (0 == sg_simple_inquiry(sg_fd, &inq_resp, true, verbose)) {
652             printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor,
653                    inq_resp.product, inq_resp.revision);
654             peri_type = inq_resp.peripheral_type;
655             cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
656             if (strlen(cp) > 0)
657                 printf("  Peripheral device type: %s\n", cp);
658             else
659                 printf("  Peripheral device type: 0x%x\n", peri_type);
660         } else {
661             pr2serr("sg_safte: %s doesn't respond to a SCSI INQUIRY\n",
662                     device_name);
663             return SG_LIB_CAT_OTHER;
664         }
665     }
666 
667     rb_buff = (uint8_t *)malloc(buf_capacity);
668     if (!rb_buff)
669         goto err_out;
670 
671     memset(rb_buff, 0, buf_capacity);
672 
673     res = read_safte_configuration(sg_fd, rb_buff, buf_capacity, verbose);
674     switch (res) {
675     case 0:
676     case SG_LIB_CAT_RECOVERED:
677         break;
678     default:
679         goto err_out;
680     }
681     if (1 == do_raw) {
682         dStrRaw(rb_buff, buf_capacity);
683         goto finish;
684     }
685     if (1 == do_hex) {
686         hex2stdout(rb_buff, buf_capacity, 1);
687         goto finish;
688     }
689 
690     if (do_config && no_hex_raw)
691         print_safte_configuration();
692 
693     if (do_status) {
694         res = do_safte_encl_status(sg_fd, do_hex, do_raw, verbose);
695         switch (res) {
696             case 0:
697             case SG_LIB_CAT_RECOVERED:
698                 break;
699             default:
700                 goto err_out;
701         }
702     }
703 
704     if (do_usage) {
705         res = do_safte_usage_statistics(sg_fd, do_hex, do_raw, verbose);
706         switch (res) {
707             case 0:
708             case SG_LIB_CAT_RECOVERED:
709                 break;
710             default:
711                 goto err_out;
712         }
713     }
714 
715     if (do_insertions) {
716         res = do_safte_slot_insertions(sg_fd, do_hex, do_raw, verbose);
717         switch (res) {
718             case 0:
719             case SG_LIB_CAT_RECOVERED:
720                 break;
721             default:
722                 goto err_out;
723         }
724     }
725 
726     if (do_slots) {
727         res = do_safte_slot_status(sg_fd, do_hex, do_raw, verbose);
728         switch (res) {
729             case 0:
730             case SG_LIB_CAT_RECOVERED:
731                 break;
732             default:
733                 goto err_out;
734         }
735     }
736 
737     if (do_flags) {
738         res = do_safte_global_flags(sg_fd, do_hex, do_raw, verbose);
739         switch (res) {
740             case 0:
741             case SG_LIB_CAT_RECOVERED:
742                 break;
743             default:
744                 goto err_out;
745         }
746     }
747 finish:
748     res = 0;
749 
750 err_out:
751     switch (res) {
752     case 0:
753     case SG_LIB_CAT_RECOVERED:
754         break;
755     default:
756         sg_get_category_sense_str(res, sizeof(b), b, verbose);
757         pr2serr("%s failed: %s\n", op_name, b);
758         break;
759     }
760     ret = res;
761 fini:
762     if (sg_fd >= 0) {
763         res = sg_cmds_close_device(sg_fd);
764         if (res < 0) {
765             pr2serr("close error: %s\n", safe_strerror(-res));
766             if (0 == ret)
767                 ret = sg_convert_errno(-res);
768         }
769     }
770     if (0 == verbose) {
771         if (! sg_if_can2stderr("sg_safte failed: ", ret))
772             pr2serr("Some error occurred, try again with '-v' "
773                     "or '-vv' for more information\n");
774     }
775     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
776 }
777