1 /*
2 * Copyright (c) 2004-2022 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 <errno.h>
19 #include <getopt.h>
20 #include <sys/time.h>
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "sg_lib.h"
27 #include "sg_cmds_basic.h"
28 #include "sg_pr2serr.h"
29 #include "sg_pt.h"
30
31 /* A utility program for the Linux OS SCSI subsystem.
32 *
33 *
34 * This program issues the SCSI command REQUEST SENSE to the given SCSI device.
35 */
36
37 static const char * version_str = "1.40 20220607";
38
39 #define MAX_REQS_RESP_LEN 255
40 #define DEF_REQS_RESP_LEN 252
41
42 #define SENSE_BUFF_LEN 96 /* Arbitrary, could be larger */
43 #define DEF_PT_TIMEOUT 60 /* 60 seconds */
44
45 #define REQUEST_SENSE_CMD 0x3
46 #define REQUEST_SENSE_CMDLEN 6
47
48 #define ME "sg_requests: "
49
50
51 static struct option long_options[] = {
52 {"desc", no_argument, 0, 'd'},
53 {"error", no_argument, 0, 'e'},
54 {"help", no_argument, 0, 'h'},
55 {"hex", no_argument, 0, 'H'},
56 {"maxlen", required_argument, 0, 'm'},
57 {"num", required_argument, 0, 'n'},
58 {"number", required_argument, 0, 'n'},
59 {"progress", no_argument, 0, 'p'},
60 {"raw", no_argument, 0, 'r'},
61 {"status", no_argument, 0, 's'},
62 {"time", no_argument, 0, 't'},
63 {"verbose", no_argument, 0, 'v'},
64 {"version", no_argument, 0, 'V'},
65 {0, 0, 0, 0},
66 };
67
68 static void
usage()69 usage()
70 {
71 pr2serr("Usage: sg_requests [--desc] [--error] [--help] [--hex] "
72 "[--maxlen=LEN]\n"
73 " [--num=NUM] [--number=NUM] [--progress] "
74 "[--raw]\n"
75 " [--status] [--time] [--verbose] "
76 "[--version] DEVICE\n"
77 " where:\n"
78 " --desc|-d set flag for descriptor sense "
79 "format\n"
80 " --error|-e change opcode to 0xff; to measure "
81 "overhead\n"
82 " twice: skip ioctl call\n"
83 " --help|-h print out usage message\n"
84 " --hex|-H output in hexadecimal\n"
85 " --maxlen=LEN|-m LEN max response length (allocation "
86 "length in cdb)\n"
87 " (def: 0 -> 252 bytes)\n"
88 " --num=NUM|-n NUM number of REQUEST SENSE commands "
89 "to send (def: 1)\n"
90 " --number=NUM same action as '--num=NUM'\n"
91 " --progress|-p output a progress indication (percentage) "
92 "if available\n"
93 " --raw|-r output in binary (to stdout)\n"
94 " --status|-s set exit status from parameter data "
95 "(def: only set\n"
96 " exit status from autosense)\n"
97 " --time|-t time the transfer, calculate commands "
98 "per second\n"
99 " --verbose|-v increase verbosity\n"
100 " --version|-V print version string and exit\n\n"
101 "Performs a SCSI REQUEST SENSE command\n"
102 );
103
104 }
105
106 static void
dStrRaw(const uint8_t * str,int len)107 dStrRaw(const uint8_t * str, int len)
108 {
109 int k;
110
111 for (k = 0; k < len; ++k)
112 printf("%c", str[k]);
113 }
114
115 int
main(int argc,char * argv[])116 main(int argc, char * argv[])
117 {
118 int c, n, k, progress, rs, sense_cat, act_din_len;
119 int do_error = 0;
120 int err = 0;
121 int num_errs = 0;
122 int num_din_errs = 0;
123 int most_recent_skey = 0;
124 int sg_fd = -1;
125 int res = 0;
126 uint8_t rsBuff[MAX_REQS_RESP_LEN + 1];
127 bool desc = false;
128 bool do_progress = false;
129 bool do_raw = false;
130 bool do_status = false;
131 bool verbose_given = false;
132 bool version_given = false;
133 bool not_raw_hex;
134 int num_rs = 1;
135 int do_hex = 0;
136 int maxlen = 0;
137 int verbose = 0;
138 const char * device_name = NULL;
139 int ret = 0;
140 struct sg_pt_base * ptvp = NULL;
141 char b[256];
142 uint8_t rs_cdb[REQUEST_SENSE_CMDLEN] =
143 {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0};
144 uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
145 #ifndef SG_LIB_MINGW
146 bool do_time = false;
147 struct timeval start_tm, end_tm;
148 #endif
149
150 while (1) {
151 int option_index = 0;
152
153 c = getopt_long(argc, argv, "dehHm:n:prstvV", long_options,
154 &option_index);
155 if (c == -1)
156 break;
157
158 switch (c) {
159 case 'd':
160 desc = true;
161 break;
162 case 'e':
163 ++do_error;
164 break;
165 case 'h':
166 case '?':
167 usage();
168 return 0;
169 case 'H':
170 ++do_hex;
171 break;
172 case 'm':
173 maxlen = sg_get_num(optarg);
174 if ((maxlen < 0) || (maxlen > MAX_REQS_RESP_LEN)) {
175 pr2serr("argument to '--maxlen' should be %d or less\n",
176 MAX_REQS_RESP_LEN);
177 return SG_LIB_SYNTAX_ERROR;
178 }
179 break;
180 case 'n':
181 num_rs = sg_get_num(optarg);
182 if (num_rs < 1) {
183 pr2serr("bad argument to '--num'\n");
184 return SG_LIB_SYNTAX_ERROR;
185 }
186 break;
187 case 'p':
188 do_progress = true;
189 break;
190 case 'r':
191 do_raw = true;
192 break;
193 case 's':
194 do_status = true;
195 break;
196 case 't':
197 #ifndef SG_LIB_MINGW
198 do_time = true;
199 #endif
200 break;
201 case 'v':
202 verbose_given = true;
203 ++verbose;
204 break;
205 case 'V':
206 version_given = true;
207 break;
208 default:
209 pr2serr("unrecognised option code 0x%x ??\n", c);
210 usage();
211 return SG_LIB_SYNTAX_ERROR;
212 }
213 }
214 if (optind < argc) {
215 if (NULL == device_name) {
216 device_name = argv[optind];
217 ++optind;
218 }
219 if (optind < argc) {
220 for (; optind < argc; ++optind)
221 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
222 usage();
223 return SG_LIB_SYNTAX_ERROR;
224 }
225 }
226 #ifdef DEBUG
227 pr2serr("In DEBUG mode, ");
228 if (verbose_given && version_given) {
229 pr2serr("but override: '-vV' given, zero verbose and continue\n");
230 verbose_given = false;
231 version_given = false;
232 verbose = 0;
233 } else if (! verbose_given) {
234 pr2serr("set '-vv'\n");
235 verbose = 2;
236 } else
237 pr2serr("keep verbose=%d\n", verbose);
238 #else
239 if (verbose_given && version_given)
240 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
241 #endif
242 if (version_given) {
243 pr2serr(ME "version: %s\n", version_str);
244 return 0;
245 }
246
247 if (0 == maxlen)
248 maxlen = DEF_REQS_RESP_LEN;
249 if (NULL == device_name) {
250 pr2serr("Missing device name!\n\n");
251 usage();
252 return SG_LIB_SYNTAX_ERROR;
253 }
254 if (do_raw) {
255 if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
256 perror("sg_set_binary_mode");
257 return SG_LIB_FILE_ERROR;
258 }
259 }
260 if (do_raw || do_hex) {
261 not_raw_hex = false;
262 #ifdef SG_LIB_MINGW
263 bool prog_time = do_progress;
264 #else
265 bool prog_time = do_progress || do_time;
266 #endif
267
268 if (prog_time) {
269 pr2serr("With either --raw or --hex, --progress and --time "
270 "contradict\n");
271 ret = SG_LIB_CONTRADICT;
272 goto finish;
273 }
274 } else
275 not_raw_hex = true;
276
277 sg_fd = sg_cmds_open_device(device_name, true /* ro */, verbose);
278 if (sg_fd < 0) {
279 if (not_raw_hex && verbose)
280 pr2serr(ME "open error: %s: %s\n", device_name,
281 safe_strerror(-sg_fd));
282 ret = sg_convert_errno(-sg_fd);
283 goto finish;
284 }
285 ptvp = construct_scsi_pt_obj_with_fd(sg_fd, verbose);
286 if ((NULL == ptvp) || ((err = get_scsi_pt_os_err(ptvp)))) {
287 if (not_raw_hex)
288 pr2serr("%s: unable to construct pt object\n", __func__);
289 ret = sg_convert_errno(err ? err : ENOMEM);
290 goto finish;
291 }
292 if (do_error)
293 rs_cdb[0] = 0xff;
294 if (desc)
295 rs_cdb[1] |= 0x1;
296 rs_cdb[4] = maxlen;
297 if (do_progress) {
298 for (k = 0; k < num_rs; ++k) {
299 act_din_len = 0;
300 if (k > 0)
301 sg_sleep_secs(30);
302 set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
303 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
304 memset(rsBuff, 0x0, sizeof(rsBuff));
305 set_scsi_pt_data_in(ptvp, rsBuff, sizeof(rsBuff));
306 set_scsi_pt_packet_id(ptvp, k + 1);
307 if (do_error > 1) {
308 ++num_errs;
309 n = 0;
310 } else {
311 if (verbose && (0 == k)) {
312 char bb[128];
313
314 pr2serr(" cdb: %s\n",
315 sg_get_command_str(rs_cdb, REQUEST_SENSE_CMDLEN,
316 true, sizeof(bb), bb));
317 }
318 rs = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose);
319 n = sg_cmds_process_resp(ptvp, "Request sense", rs, (0 == k),
320 verbose, &sense_cat);
321 }
322 if (-1 == n) {
323 if (get_scsi_pt_transport_err(ptvp))
324 ret = SG_LIB_TRANSPORT_ERROR;
325 else
326 ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
327 goto finish;
328 } else if (-2 == n) {
329 switch (sense_cat) {
330 case SG_LIB_CAT_RECOVERED:
331 case SG_LIB_CAT_NO_SENSE:
332 break;
333 case SG_LIB_CAT_NOT_READY:
334 ++num_errs;
335 if (1 == num_rs) {
336 ret = sense_cat;
337 printf("device not ready\n");
338 }
339 break;
340 case SG_LIB_CAT_UNIT_ATTENTION:
341 ++num_errs;
342 if (verbose) {
343 pr2serr("Ignoring Unit attention (sense key)\n");
344 }
345 break;
346 default:
347 ++num_errs;
348 if (1 == num_rs) {
349 ret = sense_cat;
350 sg_get_category_sense_str(sense_cat, sizeof(b), b,
351 verbose);
352 printf("%s\n", b);
353 break; // return k;
354 }
355 break;
356 }
357 }
358 if (n >= 0)
359 act_din_len = n;
360 if (ret)
361 goto finish;
362
363 if (verbose > 1) {
364 pr2serr("Parameter data in hex\n");
365 hex2stderr(rsBuff, act_din_len, 1);
366 }
367 progress = -1;
368 sg_get_sense_progress_fld(rsBuff, act_din_len, &progress);
369 if (progress < 0) {
370 ret = res;
371 if (verbose > 1)
372 pr2serr("No progress indication found, iteration %d\n",
373 k + 1);
374 /* N.B. exits first time there isn't a progress indication */
375 break;
376 } else
377 printf("Progress indication: %d.%02d%% done\n",
378 (progress * 100) / 65536,
379 ((progress * 100) % 65536) / 656);
380 partial_clear_scsi_pt_obj(ptvp);
381 } /* >>>>> end of for(num_rs) loop */
382 goto finish;
383 }
384
385 #ifndef SG_LIB_MINGW
386 if (not_raw_hex && do_time) {
387 start_tm.tv_sec = 0;
388 start_tm.tv_usec = 0;
389 gettimeofday(&start_tm, NULL);
390 }
391 #endif
392
393 rsBuff[0] = '\0';
394 rsBuff[7] = '\0';
395 for (k = 0; k < num_rs; ++k) {
396 act_din_len = 0;
397 ret = 0;
398 set_scsi_pt_cdb(ptvp, rs_cdb, sizeof(rs_cdb));
399 set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
400 memset(rsBuff, 0x0, sizeof(rsBuff));
401 set_scsi_pt_data_in(ptvp, rsBuff, sizeof(rsBuff));
402 set_scsi_pt_packet_id(ptvp, k + 1);
403 if (do_error > 1) {
404 ++num_errs;
405 n = 0;
406 } else {
407 if (verbose && (0 == k)) {
408 char bb[128];
409
410 pr2serr(" cdb: %s\n",
411 sg_get_command_str(rs_cdb, REQUEST_SENSE_CMDLEN,
412 true, sizeof(bb), bb));
413 }
414 rs = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, verbose);
415 n = sg_cmds_process_resp(ptvp, "Request sense", rs, (0 == k),
416 verbose, &sense_cat);
417 }
418 if (-1 == n) {
419 if (get_scsi_pt_transport_err(ptvp))
420 ret = SG_LIB_TRANSPORT_ERROR;
421 else
422 ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
423 goto finish;
424 } else if (-2 == n) {
425 switch (sense_cat) {
426 case SG_LIB_CAT_RECOVERED:
427 case SG_LIB_CAT_NO_SENSE:
428 break;
429 case SG_LIB_CAT_NOT_READY:
430 ++num_errs;
431 if (1 == num_rs) {
432 ret = sense_cat;
433 printf("device not ready\n");
434 }
435 break;
436 case SG_LIB_CAT_UNIT_ATTENTION:
437 ++num_errs;
438 if (verbose) {
439 pr2serr("Ignoring Unit attention (sense key)\n");
440 }
441 break;
442 default:
443 ++num_errs;
444 if (1 == num_rs) {
445 ret = sense_cat;
446 sg_get_category_sense_str(sense_cat, sizeof(b), b,
447 verbose);
448 printf("%s\n", b);
449 break; // return k;
450 }
451 break;
452 }
453 }
454 if (n >= 0)
455 act_din_len = n;
456
457 if (act_din_len > 7) {
458 struct sg_scsi_sense_hdr ssh;
459
460 if (sg_scsi_normalize_sense(rsBuff, act_din_len, &ssh)) {
461 if (ssh.sense_key > 0) {
462 ++num_din_errs;
463 most_recent_skey = ssh.sense_key;
464 }
465 if (not_raw_hex && ((1 == num_rs) || verbose)) {
466 char bb[144];
467
468 sg_get_sense_str(NULL, rsBuff, act_din_len,
469 false, sizeof(bb), bb);
470 pr2serr("data-in decoded as sense:\n%s\n", bb);
471 }
472 }
473 }
474 partial_clear_scsi_pt_obj(ptvp);
475 if (ret)
476 goto finish;
477
478 if (act_din_len > 0) {
479 if (do_raw)
480 dStrRaw(rsBuff, act_din_len);
481 else if (do_hex)
482 hex2stdout(rsBuff, act_din_len, 1);
483 }
484 } /* <<<<< end of for(num_rs) loop */
485 if ((0 == ret) && do_status) {
486 ret = sg_err_category_sense(rsBuff, act_din_len);
487 if (SG_LIB_CAT_NO_SENSE == ret) {
488 struct sg_scsi_sense_hdr ssh;
489
490 if (sg_scsi_normalize_sense(rsBuff, act_din_len, &ssh)) {
491 if ((0 == ssh.asc) && (0 == ssh.ascq))
492 ret = 0;
493 }
494 }
495 }
496 #ifndef SG_LIB_MINGW
497 if (not_raw_hex && do_time && (start_tm.tv_sec || start_tm.tv_usec)) {
498 struct timeval res_tm;
499 double den, num;
500
501 gettimeofday(&end_tm, NULL);
502 res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
503 res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
504 if (res_tm.tv_usec < 0) {
505 --res_tm.tv_sec;
506 res_tm.tv_usec += 1000000;
507 }
508 den = res_tm.tv_sec;
509 den += (0.000001 * res_tm.tv_usec);
510 num = (double)num_rs;
511 printf("time to perform commands was %d.%06d secs",
512 (int)res_tm.tv_sec, (int)res_tm.tv_usec);
513 if (den > 0.00001)
514 printf("; %.2f operations/sec\n", num / den);
515 else
516 printf("\n");
517 }
518 #endif
519
520 finish:
521 if (not_raw_hex) {
522 if (num_errs > 0)
523 printf("Number of command errors detected: %d\n", num_errs);
524 if (num_din_errs > 0)
525 printf("Number of data-in errors detected: %d, most recent "
526 "sense_key=%d\n", num_din_errs, most_recent_skey);
527 }
528 if (sg_fd >= 0) {
529 res = sg_cmds_close_device(sg_fd);
530 if (res < 0) {
531 if (not_raw_hex)
532 pr2serr("close error: %s\n", safe_strerror(-res));
533 if (0 == ret)
534 ret = sg_convert_errno(-res);
535 }
536 }
537 if (not_raw_hex && (0 == verbose)) {
538 if (! sg_if_can2stderr("sg_requests failed: ", ret))
539 pr2serr("Some error occurred, try again with '-v' "
540 "or '-vv' for more information\n");
541 }
542 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
543 }
544