xref: /aosp_15_r20/external/sg3_utils/src/sg_ident.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2005-2018 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 <string.h>
17 #include <getopt.h>
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include "sg_lib.h"
23 #include "sg_cmds_basic.h"
24 #include "sg_cmds_extra.h"
25 #include "sg_unaligned.h"
26 #include "sg_pr2serr.h"
27 
28 /* A utility program originally written for the Linux OS SCSI subsystem.
29  *
30  *
31  * This program issues these SCSI commands: REPORT IDENTIFYING INFORMATION
32  * and SET IDENTIFYING INFORMATION. These commands were called REPORT
33  * DEVICE IDENTIFIER and SET DEVICE IDENTIFIER prior to spc4r07.
34  */
35 
36 static const char * version_str = "1.23 20180814";
37 
38 #define ME "sg_ident: "
39 
40 #define REPORT_ID_INFO_SANITY_LEN 512
41 
42 
43 static struct option long_options[] = {
44         {"ascii", no_argument, 0, 'A'},
45         {"clear", no_argument, 0, 'C'},
46         {"help", no_argument, 0, 'h'},
47         {"itype", required_argument, 0, 'i'},
48         {"raw", no_argument, 0, 'r'},
49         {"set", no_argument, 0, 'S'},
50         {"verbose", no_argument, 0, 'v'},
51         {"version", no_argument, 0, 'V'},
52         {0, 0, 0, 0},
53 };
54 
55 static void
decode_ii(const uint8_t * iip,int ii_len,int itype,bool ascii,bool raw,int verbose)56 decode_ii(const uint8_t * iip, int ii_len, int itype, bool ascii,
57           bool raw, int verbose)
58 {
59     int k;
60 
61     if (raw) {
62         if (ii_len > 0) {
63             int n;
64 
65             if (sg_set_binary_mode(STDOUT_FILENO) < 0)
66                 perror("sg_set_binary_mode");
67 #if 0
68             n = fwrite(iip, 1, ii_len, stdout);
69 #else
70             n = write(STDOUT_FILENO, iip, ii_len);
71 #endif
72             if (verbose && (n < 1))
73                 pr2serr("unable to write to stdout\n");
74         }
75         return;
76     }
77     if (0x7f == itype) {  /* list of available information types */
78         for (k = 0; k < (ii_len - 3); k += 4)
79             printf("  Information type: %d, Maximum information length: "
80                    "%d bytes\n", iip[k], sg_get_unaligned_be16(iip + 2));
81     } else {        /* single element */
82         if (verbose)
83             printf("Information:\n");
84         if (ii_len > 0) {
85             if (ascii)
86                 printf("%.*s\n", ii_len, (const char *)iip);
87             else
88                 hex2stdout(iip, ii_len, 0);
89         }
90     }
91 }
92 
93 static void
usage(void)94 usage(void)
95 {
96     pr2serr("Usage: sg_ident   [--ascii] [--clear] [--help] [--itype=IT] "
97             "[--raw] [--set]\n"
98             "                  [--verbose] [--version] DEVICE\n"
99             "  where:\n"
100             "    --ascii|-A      report identifying information as ASCII "
101             "(or UTF8) string\n"
102             "    --clear|-C      clear (set to zero length) identifying "
103             "information\n"
104             "    --help|-h       print out usage message\n"
105             "    --itype=IT|-i IT    specify identifying information type "
106             "(def: 0)\n"
107             "    --raw|-r        output identifying information to "
108             "stdout\n"
109             "    --set|-S        invoke set identifying information with "
110             "data from stdin\n"
111             "    --verbose|-v    increase verbosity of output\n"
112             "    --version|-V    print version string and exit\n\n"
113             "Performs a SCSI REPORT (or SET) IDENTIFYING INFORMATION "
114             "command. When no\noptions are given then REPORT IDENTIFYING "
115             "INFORMATION is sent and the\nresponse is output in "
116             "hexadecimal with ASCII to the right.\n");
117 }
118 
119 int
main(int argc,char * argv[])120 main(int argc, char * argv[])
121 {
122     bool ascii = false;
123     bool do_clear = false;
124     bool raw = false;
125     bool do_set = false;
126     bool verbose_given = false;
127     bool version_given = false;
128     int sg_fd, res, c, ii_len;
129     uint8_t rdi_buff[REPORT_ID_INFO_SANITY_LEN + 4];
130     char b[80];
131     uint8_t * bp = NULL;
132     int itype = 0;
133     int verbose = 0;
134     const char * device_name = NULL;
135     int ret = 0;
136 
137     while (1) {
138         int option_index = 0;
139 
140         c = getopt_long(argc, argv, "AChi:rSvV", long_options,
141                         &option_index);
142         if (c == -1)
143             break;
144 
145         switch (c) {
146         case 'A':
147             ascii = true;
148             break;
149         case 'C':
150             do_clear = true;
151             break;
152         case 'h':
153         case '?':
154             usage();
155             return 0;
156         case 'i':
157            itype = sg_get_num(optarg);
158            if ((itype < 0) || (itype > 127)) {
159                 pr2serr("argument to '--itype' should be in range 0 to 127\n");
160                 return SG_LIB_SYNTAX_ERROR;
161             }
162             break;
163         case 'r':
164             raw = true;
165             break;
166         case 'S':
167             do_set = true;
168             break;
169         case 'v':
170             verbose_given = true;
171             ++verbose;
172             break;
173         case 'V':
174             version_given = true;
175             break;
176         default:
177             pr2serr("unrecognised option code 0x%x ??\n", c);
178             usage();
179             return SG_LIB_SYNTAX_ERROR;
180         }
181     }
182     if (optind < argc) {
183         if (NULL == device_name) {
184             device_name = argv[optind];
185             ++optind;
186         }
187         if (optind < argc) {
188             for (; optind < argc; ++optind)
189                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
190             usage();
191             return SG_LIB_SYNTAX_ERROR;
192         }
193     }
194 
195 #ifdef DEBUG
196     pr2serr("In DEBUG mode, ");
197     if (verbose_given && version_given) {
198         pr2serr("but override: '-vV' given, zero verbose and continue\n");
199         verbose_given = false;
200         version_given = false;
201         verbose = 0;
202     } else if (! verbose_given) {
203         pr2serr("set '-vv'\n");
204         verbose = 2;
205     } else
206         pr2serr("keep verbose=%d\n", verbose);
207 #else
208     if (verbose_given && version_given)
209         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
210 #endif
211     if (version_given) {
212         pr2serr(ME "version: %s\n", version_str);
213         return 0;
214     }
215 
216     if (NULL == device_name) {
217         pr2serr("missing device name!\n\n");
218         usage();
219         return SG_LIB_SYNTAX_ERROR;
220     }
221     if (do_set && do_clear) {
222         pr2serr("only one of '--clear' and '--set' can be given\n");
223         usage();
224         return SG_LIB_CONTRADICT;
225     }
226     if (ascii && raw) {
227         pr2serr("only one of '--ascii' and '--raw' can be given\n");
228         usage();
229         return SG_LIB_CONTRADICT;
230     }
231     if ((do_set || do_clear) && (raw || ascii)) {
232         pr2serr("'--set' cannot be used with either '--ascii' or '--raw'\n");
233         usage();
234         return SG_LIB_CONTRADICT;
235     }
236     sg_fd = sg_cmds_open_device(device_name, false /* rw=false */, verbose);
237     if (sg_fd < 0) {
238         pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
239         return sg_convert_errno(-sg_fd);
240     }
241 
242     memset(rdi_buff, 0x0, sizeof(rdi_buff));
243     if (do_set || do_clear) {
244         if (do_set) {
245             res = fread(rdi_buff, 1, REPORT_ID_INFO_SANITY_LEN + 2, stdin);
246             if (res <= 0) {
247                 pr2serr("no data read from stdin; to clear identifying "
248                         "information use '--clear' instead\n");
249                 ret = -1;
250                 goto err_out;
251             } else if (res > REPORT_ID_INFO_SANITY_LEN) {
252                 pr2serr("SPC-4 limits information length to 512 bytes\n");
253                 ret = -1;
254                 goto err_out;
255             }
256             ii_len = res;
257             res = sg_ll_set_id_info(sg_fd, itype, rdi_buff, ii_len, true,
258                                     verbose);
259         } else    /* do_clear */
260             res = sg_ll_set_id_info(sg_fd, itype, rdi_buff, 0, true, verbose);
261         if (res) {
262             ret = res;
263             sg_get_category_sense_str(res, sizeof(b), b, verbose);
264             pr2serr("Set identifying information: %s\n", b);
265             if (0 == verbose)
266                 pr2serr("    try '-v' for more information\n");
267         }
268     } else {    /* do report identifying information */
269         res = sg_ll_report_id_info(sg_fd, itype, rdi_buff, 4, true, verbose);
270         if (0 == res) {
271             ii_len = sg_get_unaligned_be32(rdi_buff + 0);
272             if ((! raw) && (verbose > 0))
273                 printf("Reported identifying information length = %d\n",
274                        ii_len);
275             if (0 == ii_len) {
276                 if (verbose > 1)
277                     pr2serr("    This implies the device has an empty "
278                             "information field\n");
279                 goto err_out;
280             }
281             if (ii_len > REPORT_ID_INFO_SANITY_LEN) {
282                 pr2serr("    That length (%d) seems too long for an "
283                         "information\n", ii_len);
284                 ret = -1;
285                 goto err_out;
286             }
287             bp = rdi_buff;
288             res = sg_ll_report_id_info(sg_fd, itype, bp, ii_len + 4, true,
289                                        verbose);
290             if (0 == res) {
291                 ii_len = sg_get_unaligned_be32(bp + 0);
292                 decode_ii(bp + 4, ii_len, itype, ascii, raw, verbose);
293             } else
294                 ret = res;
295         } else
296             ret = res;
297         if (ret) {
298             sg_get_category_sense_str(res, sizeof(b), b, verbose);
299             pr2serr("Report identifying information: %s\n", b);
300             if (0 == verbose)
301                 pr2serr("    try '-v' for more information\n");
302         }
303     }
304 
305 err_out:
306     res = sg_cmds_close_device(sg_fd);
307     if (res < 0) {
308         pr2serr("close error: %s\n", safe_strerror(-res));
309         if (0 == ret)
310             ret = sg_convert_errno(-res);
311     }
312     if (0 == verbose) {
313         if (! sg_if_can2stderr("sg_ident failed: ", ret))
314             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
315                     "more information\n");
316     }
317     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
318 }
319