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