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