1 /*
2 * Copyright (c) 2006-2016 Luben Tuikov 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
8 #include <unistd.h>
9 #include <fcntl.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #include <string.h>
14 #include <getopt.h>
15 #define __STDC_FORMAT_MACROS 1
16 #include <inttypes.h>
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 #include "sg_lib.h"
22 #include "sg_cmds_basic.h"
23 #include "sg_cmds_extra.h"
24 #include "sg_pt.h" /* needed for scsi_pt_win32_direct() */
25 #include "sg_unaligned.h"
26 #include "sg_pr2serr.h"
27
28 /*
29 * This utility issues the SCSI READ BUFFER(10 or 16) command to the given
30 * device.
31 */
32
33 static const char * version_str = "1.15 20160131";
34
35
36 #ifndef SG_READ_BUFFER_10_CMD
37 #define SG_READ_BUFFER_10_CMD 0x3c
38 #define SG_READ_BUFFER_10_CMDLEN 10
39 #endif
40 #ifndef SG_READ_BUFFER_16_CMD
41 #define SG_READ_BUFFER_16_CMD 0x9b
42 #define SG_READ_BUFFER_16_CMDLEN 16
43 #endif
44
45 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
46 #define DEF_PT_TIMEOUT 60 /* 60 seconds */
47
48
49 static struct option long_options[] = {
50 {"16", no_argument, 0, 'L'},
51 {"help", no_argument, 0, 'h'},
52 {"hex", no_argument, 0, 'H'},
53 {"id", required_argument, 0, 'i'},
54 {"length", required_argument, 0, 'l'},
55 {"long", no_argument, 0, 'L'},
56 {"mode", required_argument, 0, 'm'},
57 {"offset", required_argument, 0, 'o'},
58 {"raw", no_argument, 0, 'r'},
59 {"readonly", no_argument, 0, 'R'},
60 {"specific", required_argument, 0, 'S'},
61 {"verbose", no_argument, 0, 'v'},
62 {"version", no_argument, 0, 'V'},
63 {0, 0, 0, 0}, /* sentinel */
64 };
65
66
67 static void
usage()68 usage()
69 {
70 pr2serr("Usage: sg_read_buffer [--16] [--help] [--hex] [--id=ID] "
71 "[--length=LEN]\n"
72 " [--long] [--mode=MO] [--offset=OFF] "
73 "[--raw]\n"
74 " [--readonly] [--specific=MS] [--verbose] "
75 "[--version]\n"
76 " DEVICE\n"
77 " where:\n"
78 " --16|-L issue READ BUFFER(16) (def: 10)\n"
79 " --help|-h print out usage message\n"
80 " --hex|-H print output in hex\n"
81 " --id=ID|-i ID buffer identifier (0 (default) to 255)\n"
82 " --length=LEN|-l LEN length in bytes to read (def: 4)\n"
83 " --long|-L issue READ BUFFER(16) (def: 10)\n"
84 " --mode=MO|-m MO read buffer mode, MO is number or "
85 "acronym (def: 0)\n"
86 " --offset=OFF|-o OFF buffer offset (unit: bytes, def: 0)\n"
87 " --raw|-r output response to stdout\n"
88 " --specific=MS|-S MS mode specific value; 3 bit field (0 "
89 "to 7)\n"
90 " --readonly|-R open DEVICE read-only (def: read-write)\n"
91 " --verbose|-v increase verbosity\n"
92 " --version|-V print version string and exit\n\n"
93 "Performs a SCSI READ BUFFER (10 or 16) command. Use '-m xxx' to "
94 "list\navailable modes. Numbers given in options are decimal "
95 "unless they have\na hex indicator (e.g. a leading '0x').\n"
96 );
97 }
98
99
100 #define MODE_HEADER_DATA 0
101 #define MODE_VENDOR 1
102 #define MODE_DATA 2
103 #define MODE_DESCRIPTOR 3
104 #define MODE_ECHO_BUFFER 0x0A
105 #define MODE_ECHO_BDESC 0x0B
106 #define MODE_EN_EX_ECHO 0x1A
107 #define MODE_ERR_HISTORY 0x1C
108
109 static struct mode_s {
110 const char *mode_string;
111 int mode;
112 const char *comment;
113 } modes[] = {
114 { "hd", MODE_HEADER_DATA, "combined header and data"},
115 { "vendor", MODE_VENDOR, "vendor specific"},
116 { "data", MODE_DATA, "data"},
117 { "desc", MODE_DESCRIPTOR, "descriptor"},
118 { "echo", MODE_ECHO_BUFFER, "read data from echo buffer "
119 "(spc-2)"},
120 { "echo_desc", MODE_ECHO_BDESC, "echo buffer descriptor (spc-2)"},
121 { "en_ex", MODE_EN_EX_ECHO,
122 "enable expander communications protocol and echo buffer (spc-3)"},
123 { "err_hist", MODE_ERR_HISTORY, "error history (spc-4)"},
124 { NULL, 999, NULL}, /* end sentinel */
125 };
126
127
128 static void
print_modes(void)129 print_modes(void)
130 {
131 const struct mode_s *mp;
132
133 pr2serr("The modes parameter argument can be numeric (hex or decimal)\n"
134 "or symbolic:\n");
135 for (mp = modes; mp->mode_string; ++mp) {
136 pr2serr(" %2d (0x%02x) %-16s%s\n", mp->mode, mp->mode,
137 mp->mode_string, mp->comment);
138 }
139 }
140
141 /* Invokes a SCSI READ BUFFER(10) command (spc5r02). Return of 0 -> success,
142 * various SG_LIB_CAT_* positive values or -1 -> other errors */
143 static int
ll_read_buffer_10(int sg_fd,int rb_mode,int rb_mode_sp,int rb_id,uint32_t rb_offset,void * resp,int mx_resp_len,int * residp,int noisy,int verbose)144 ll_read_buffer_10(int sg_fd, int rb_mode, int rb_mode_sp, int rb_id,
145 uint32_t rb_offset, void * resp, int mx_resp_len,
146 int * residp, int noisy, int verbose)
147 {
148 int k, ret, res, sense_cat;
149 uint8_t rb10_cb[SG_READ_BUFFER_10_CMDLEN] =
150 {SG_READ_BUFFER_10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0};
151 uint8_t sense_b[SENSE_BUFF_LEN];
152 struct sg_pt_base * ptvp;
153
154 rb10_cb[1] = (uint8_t)(rb_mode & 0x1f);
155 if (rb_mode_sp)
156 rb10_cb[1] |= (uint8_t)((rb_mode_sp & 0x7) << 5);
157 rb10_cb[2] = (uint8_t)rb_id;
158 sg_put_unaligned_be24(rb_offset, rb10_cb + 3);
159 sg_put_unaligned_be24(mx_resp_len, rb10_cb + 6);
160 if (verbose) {
161 pr2serr(" Read buffer(10) cdb: ");
162 for (k = 0; k < SG_READ_BUFFER_10_CMDLEN; ++k)
163 pr2serr("%02x ", rb10_cb[k]);
164 pr2serr("\n");
165 }
166
167 ptvp = construct_scsi_pt_obj();
168 if (NULL == ptvp) {
169 pr2serr("Read buffer(10): out of memory\n");
170 return -1;
171 }
172 set_scsi_pt_cdb(ptvp, rb10_cb, sizeof(rb10_cb));
173 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
174 set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
175 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
176 ret = sg_cmds_process_resp(ptvp, "Read buffer(10)", res, mx_resp_len,
177 sense_b, noisy, verbose, &sense_cat);
178 if (-1 == ret)
179 ;
180 else if (-2 == ret) {
181 switch (sense_cat) {
182 case SG_LIB_CAT_RECOVERED:
183 case SG_LIB_CAT_NO_SENSE:
184 ret = 0;
185 break;
186 default:
187 ret = sense_cat;
188 break;
189 }
190 } else {
191 if ((verbose > 2) && (ret > 0)) {
192 pr2serr(" Read buffer(10): response%s\n",
193 (ret > 256 ? ", first 256 bytes" : ""));
194 dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
195 }
196 ret = 0;
197 }
198 if (residp)
199 *residp = get_scsi_pt_resid(ptvp);
200 destruct_scsi_pt_obj(ptvp);
201 return ret;
202 }
203
204 /* Invokes a SCSI READ BUFFER(16) command (spc5r02). Return of 0 -> success,
205 * various SG_LIB_CAT_* positive values or -1 -> other errors */
206 static int
ll_read_buffer_16(int sg_fd,int rb_mode,int rb_mode_sp,int rb_id,uint64_t rb_offset,void * resp,int mx_resp_len,int * residp,int noisy,int verbose)207 ll_read_buffer_16(int sg_fd, int rb_mode, int rb_mode_sp, int rb_id,
208 uint64_t rb_offset, void * resp, int mx_resp_len,
209 int * residp, int noisy, int verbose)
210 {
211 int k, ret, res, sense_cat;
212 uint8_t rb16_cb[SG_READ_BUFFER_16_CMDLEN] =
213 {SG_READ_BUFFER_16_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
214 0, 0, 0, 0};
215 uint8_t sense_b[SENSE_BUFF_LEN];
216 struct sg_pt_base * ptvp;
217
218 rb16_cb[1] = (uint8_t)(rb_mode & 0x1f);
219 if (rb_mode_sp)
220 rb16_cb[1] |= (uint8_t)((rb_mode_sp & 0x7) << 5);
221 sg_put_unaligned_be64(rb_offset, rb16_cb + 2);
222 sg_put_unaligned_be24(mx_resp_len, rb16_cb + 11);
223 rb16_cb[14] = (uint8_t)rb_id;
224 if (verbose) {
225 pr2serr(" Read buffer(16) cdb: ");
226 for (k = 0; k < SG_READ_BUFFER_16_CMDLEN; ++k)
227 pr2serr("%02x ", rb16_cb[k]);
228 pr2serr("\n");
229 }
230
231 ptvp = construct_scsi_pt_obj();
232 if (NULL == ptvp) {
233 pr2serr("Read buffer(16): out of memory\n");
234 return -1;
235 }
236 set_scsi_pt_cdb(ptvp, rb16_cb, sizeof(rb16_cb));
237 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
238 set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len);
239 res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose);
240 ret = sg_cmds_process_resp(ptvp, "Read buffer(16)", res, mx_resp_len,
241 sense_b, noisy, verbose, &sense_cat);
242 if (-1 == ret)
243 ;
244 else if (-2 == ret) {
245 switch (sense_cat) {
246 case SG_LIB_CAT_RECOVERED:
247 case SG_LIB_CAT_NO_SENSE:
248 ret = 0;
249 break;
250 default:
251 ret = sense_cat;
252 break;
253 }
254 } else {
255 if ((verbose > 2) && (ret > 0)) {
256 pr2serr(" Read buffer(16): response%s\n",
257 (ret > 256 ? ", first 256 bytes" : ""));
258 dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1);
259 }
260 ret = 0;
261 }
262 if (residp)
263 *residp = get_scsi_pt_resid(ptvp);
264 destruct_scsi_pt_obj(ptvp);
265 return ret;
266 }
267
268 static void
dStrRaw(const char * str,int len)269 dStrRaw(const char* str, int len)
270 {
271 int k;
272
273 for (k = 0 ; k < len; ++k)
274 printf("%c", str[k]);
275 }
276
277 int
main(int argc,char * argv[])278 main(int argc, char * argv[])
279 {
280 int res, c, len, k;
281 int sg_fd = -1;
282 int do_help = 0;
283 int do_hex = 0;
284 int do_long = 0;
285 int o_readonly = 0;
286 int rb_id = 0;
287 int rb_len = 4;
288 int rb_mode = 0;
289 int rb_mode_sp = 0;
290 int64_t ll;
291 uint64_t rb_offset = 0;
292 int do_raw = 0;
293 int resid = 0;
294 int verbose = 0;
295 int ret = 0;
296 const char * device_name = NULL;
297 unsigned char * resp;
298 const struct mode_s * mp;
299
300 while (1) {
301 int option_index = 0;
302
303 c = getopt_long(argc, argv, "hHi:l:Lm:o:rRS:vV", long_options,
304 &option_index);
305 if (c == -1)
306 break;
307
308 switch (c) {
309 case 'h':
310 case '?':
311 ++do_help;
312 break;
313 case 'H':
314 ++do_hex;
315 break;
316 case 'i':
317 rb_id = sg_get_num(optarg);
318 if ((rb_id < 0) || (rb_id > 255)) {
319 pr2serr("argument to '--id' should be in the range 0 to "
320 "255\n");
321 return SG_LIB_SYNTAX_ERROR;
322 }
323 break;
324 case 'l':
325 rb_len = sg_get_num(optarg);
326 if (rb_len < 0) {
327 pr2serr("bad argument to '--length'\n");
328 return SG_LIB_SYNTAX_ERROR;
329 }
330 if (rb_len > 0xffffff) {
331 pr2serr("argument to '--length' must be <= 0xffffff\n");
332 return SG_LIB_SYNTAX_ERROR;
333 }
334 break;
335 case 'L':
336 ++do_long;
337 break;
338 case 'm':
339 if (isdigit(*optarg)) {
340 rb_mode = sg_get_num(optarg);
341 if ((rb_mode < 0) || (rb_mode > 31)) {
342 pr2serr("argument to '--mode' should be in the range 0 "
343 "to 31\n");
344 return SG_LIB_SYNTAX_ERROR;
345 }
346 } else {
347 len = strlen(optarg);
348 for (mp = modes; mp->mode_string; ++mp) {
349 if (0 == strncmp(mp->mode_string, optarg, len)) {
350 rb_mode = mp->mode;
351 break;
352 }
353 }
354 if (NULL == mp) {
355 print_modes();
356 return SG_LIB_SYNTAX_ERROR;
357 }
358 }
359 break;
360 case 'o':
361 ll = sg_get_llnum(optarg);
362 if (ll < 0) {
363 pr2serr("bad argument to '--offset'\n");
364 return SG_LIB_SYNTAX_ERROR;
365 }
366 rb_offset = ll;
367 break;
368 case 'r':
369 ++do_raw;
370 break;
371 case 'R':
372 ++o_readonly;
373 break;
374 case 'S':
375 rb_mode_sp = sg_get_num(optarg);
376 if ((rb_mode_sp < 0) || (rb_mode_sp > 7)) {
377 pr2serr("expected argument to '--specific' to be 0 to 7\n");
378 return SG_LIB_SYNTAX_ERROR;
379 }
380 break;
381 case 'v':
382 ++verbose;
383 break;
384 case 'V':
385 pr2serr("version: %s\n", version_str);
386 return 0;
387 default:
388 pr2serr("unrecognised option code 0x%x ??\n", c);
389 usage();
390 return SG_LIB_SYNTAX_ERROR;
391 }
392 }
393 if (do_help) {
394 if (do_help > 1) {
395 usage();
396 pr2serr("\n");
397 print_modes();
398 } else
399 usage();
400 return 0;
401 }
402 if (optind < argc) {
403 if (NULL == device_name) {
404 device_name = argv[optind];
405 ++optind;
406 }
407 if (optind < argc) {
408 for (; optind < argc; ++optind)
409 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
410 usage();
411 return SG_LIB_SYNTAX_ERROR;
412 }
413 }
414
415 if (NULL == device_name) {
416 pr2serr("missing device name!\n");
417 usage();
418 return SG_LIB_SYNTAX_ERROR;
419 }
420
421 if (rb_len > 0) {
422 resp = (unsigned char *)malloc(rb_len);
423 if (NULL == resp) {
424 pr2serr("unable to allocate %d bytes on the heap\n", rb_len);
425 return SG_LIB_CAT_OTHER;
426 }
427 memset(resp, 0, rb_len);
428 } else
429 resp = NULL;
430
431 if (do_raw) {
432 if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
433 perror("sg_set_binary_mode");
434 ret = SG_LIB_FILE_ERROR;
435 goto fini;
436 }
437 }
438
439 #ifdef SG_LIB_WIN32
440 #ifdef SG_LIB_WIN32_DIRECT
441 if (verbose > 4)
442 pr2serr("Initial win32 SPT interface state: %s\n",
443 scsi_pt_win32_spt_state() ? "direct" : "indirect");
444 scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
445 #endif
446 #endif
447
448 sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
449 if (sg_fd < 0) {
450 pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
451 ret = SG_LIB_FILE_ERROR;
452 goto fini;
453 }
454
455 if (do_long)
456 res = ll_read_buffer_16(sg_fd, rb_mode, rb_mode_sp, rb_id, rb_offset,
457 resp, rb_len, &resid, 1, verbose);
458 else if (rb_offset > 0xffffff) {
459 pr2serr("--offset value is too large for READ BUFFER(10), try "
460 "--16\n");
461 ret = SG_LIB_SYNTAX_ERROR;
462 goto fini;
463 } else
464 res = ll_read_buffer_10(sg_fd, rb_mode, rb_mode_sp, rb_id,
465 (uint32_t)rb_offset, resp, rb_len, &resid, 1,
466 verbose);
467 if (0 != res) {
468 char b[80];
469
470 ret = res;
471 if (res > 0) {
472 sg_get_category_sense_str(res, sizeof(b), b, verbose);
473 pr2serr("Read buffer(%d) failed: %s\n", (do_long ? 16 : 10), b);
474 }
475 goto fini;
476 }
477 if (resid > 0)
478 rb_len -= resid; /* got back less than requested */
479 if (rb_len > 0) {
480 if (do_raw)
481 dStrRaw((const char *)resp, rb_len);
482 else if (do_hex || (rb_len < 4))
483 dStrHex((const char *)resp, rb_len, ((do_hex > 1) ? 0 : 1));
484 else {
485 switch (rb_mode) {
486 case MODE_DESCRIPTOR:
487 k = sg_get_unaligned_be24(resp + 1);
488 printf("OFFSET BOUNDARY: %d, Buffer offset alignment: "
489 "%d-byte\n", resp[0], (1 << resp[0]));
490 printf("BUFFER CAPACITY: %d (0x%x)\n", k, k);
491 break;
492 case MODE_ECHO_BDESC:
493 k = sg_get_unaligned_be16(resp + 2) & 0x1fff;
494 printf("EBOS:%d\n", resp[0] & 1 ? 1 : 0);
495 printf("Echo buffer capacity: %d (0x%x)\n", k, k);
496 break;
497 default:
498 dStrHex((const char *)resp, rb_len, (verbose > 1 ? 0 : 1));
499 break;
500 }
501 }
502 }
503
504 fini:
505 if (resp)
506 free(resp);
507 if (sg_fd >= 0) {
508 res = sg_cmds_close_device(sg_fd);
509 if (res < 0) {
510 pr2serr("close error: %s\n", safe_strerror(-res));
511 if (0 == ret)
512 return SG_LIB_FILE_ERROR;
513 }
514 }
515 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
516 }
517