xref: /aosp_15_r20/external/sg3_utils/src/sg_requests.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
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