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