• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (C) 2000-2022 D. Gilbert
3  *  This program is free software; you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation; either version 2, or (at your option)
6  *  any later version.
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  *
10  *  This program outputs information provided by a SCSI MODE SENSE command.
11  *  Does 10 byte MODE SENSE commands by default, Trent Piepho added a "-6"
12  *  switch for force 6 byte mode sense commands.
13  *  This utility cannot modify mode pages. See the sdparm utility for that.
14  */
15 
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <stdbool.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <getopt.h>
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 #include "sg_lib.h"
31 #include "sg_cmds_basic.h"
32 #include "sg_unaligned.h"
33 #include "sg_pr2serr.h"
34 
35 static const char * version_str = "1.75 20220202";
36 
37 #define DEF_ALLOC_LEN (1024 * 4)
38 #define DEF_6_ALLOC_LEN 252
39 #define UNLIKELY_ABOVE_LEN 512
40 #define PG_CODE_ALL 0x3f
41 #define PG_CODE_MASK 0x3f
42 #define PG_CODE_MAX 0x3f
43 #define SPG_CODE_ALL 0xff
44 #define PROTO_SPECIFIC_1 0x18
45 #define PROTO_SPECIFIC_2 0x19
46 
47 #define EBUFF_SZ 256
48 
49 
50 struct opts_t {
51     bool do_dbd;
52     bool do_dbout;
53     bool do_examine;
54     bool do_flexible;
55     bool do_list;
56     bool do_llbaa;
57     bool do_six;
58     bool o_readwrite;
59     bool subpg_code_given;
60     bool opt_new;
61     bool verbose_given;
62     bool version_given;
63     int do_all;
64     int do_help;
65     int do_hex;
66     int maxlen;
67     int do_raw;
68     int verbose;
69     int page_control;
70     int pg_code;
71     int subpg_code;
72     const char * device_name;
73     const char * page_acron;
74 };
75 
76 struct page_code_desc {
77     int page_code;
78     int subpage_code;
79     const char * acron;
80     const char * desc;
81 };
82 
83 struct pc_desc_group {
84     struct page_code_desc * pcdp;
85     const char * group_name;
86 };
87 
88 static struct option long_options[] = {
89         {"all", no_argument, 0, 'a'},
90         {"control", required_argument, 0, 'c'},
91         {"dbd", no_argument, 0, 'd'},
92         {"dbout", no_argument, 0, 'D'},
93         {"examine", no_argument, 0, 'e'},
94         {"flexible", no_argument, 0, 'f'},
95         {"help", no_argument, 0, 'h'},
96         {"hex", no_argument, 0, 'H'},
97         {"list", no_argument, 0, 'l'},
98         {"llbaa", no_argument, 0, 'L'},
99         {"maxlen", required_argument, 0, 'm'},
100         {"new", no_argument, 0, 'N'},
101         {"old", no_argument, 0, 'O'},
102         {"page", required_argument, 0, 'p'},
103         {"raw", no_argument, 0, 'r'},
104         {"read-write", no_argument, 0, 'w'},
105         {"read_write", no_argument, 0, 'w'},
106         {"readwrite", no_argument, 0, 'w'},
107         {"six", no_argument, 0, '6'},
108         {"verbose", no_argument, 0, 'v'},
109         {"version", no_argument, 0, 'V'},
110         {0, 0, 0, 0},
111 };
112 
113 /* Common to all SCSI devices (found in SPCx). In numerical order */
114 static struct page_code_desc pc_desc_common[] = {
115     {0x0, 0x0, "ua", "Unit Attention condition [vendor specific format]"},
116     {0x2, 0x0, "dr", "Disconnect-Reconnect"},
117     {0x9, 0x0, "pd", "Peripheral device (obsolete)"},
118     {0xa, 0x0, "co", "Control"},
119     {0xa, 0x1, "coe", "Control extension"},
120     {0xa, 0x3, "cdla", "Command duration limit A"},
121     {0xa, 0x4, "cdlb", "Command duration limit B"},
122     {0xa, 0x7, "cdt2a", "Command duration limit T2A"},  /* spc6r01 */
123     {0xa, 0x8, "cdt2b", "Command duration limit T2B"},  /* spc6r01 */
124     {0x15, 0x0, "ext_", "Extended"},
125     {0x16, 0x0, "edts", "Extended device-type specific"},
126     {0x18, 0x0, "pslu", "Protocol specific lu"},
127     {0x19, 0x0, "pspo", "Protocol specific port"},
128     {0x1a, 0x0, "po", "Power condition"},
129     {0x1a, 0x1, "ps", "Power consumption"},
130     {0x1c, 0x0, "ie", "Informational exceptions control"},
131     {PG_CODE_ALL, 0x0, "asmp", "[yields all supported pages]"},
132     {PG_CODE_ALL, SPG_CODE_ALL,"asmsp",
133         "[yields all supported pages and subpages]"},
134     {0x0, 0x0, NULL, NULL},
135 };
136 
137 static struct page_code_desc pc_desc_disk[] = {
138     {0x1, 0x0, "rw", "Read-Write error recovery"},
139     {0x3, 0x0, "fo", "Format (obsolete)"},
140     {0x4, 0x0, "rd", "Rigid disk geometry (obsolete)"},
141     {0x5, 0x0, "fd", "Flexible disk (obsolete)"},
142     {0x7, 0x0, "ve", "Verify error recovery"},
143     {0x8, 0x0, "ca", "Caching"},
144     {0xa, 0x2, "atag", "Application tag"},
145     {0xa, 0x5, "ioad", "IO advice hints grouping"}, /* added sbc4r06 */
146     {0xa, 0x6, "bop", "Background operation control"}, /* added sbc4r07 */
147     {0xa, 0xf1, "pat", "Parallel ATA control (SAT)"},
148     {0xa, 0xf2, "afc", "ATA feature control (SAT)"},   /* added 20-085r2 */
149     {0xb, 0x0, "mts", "Medium types supported (obsolete)"},
150     {0xc, 0x0, "not", "Notch and partition (obsolete)"},
151     {0xd, 0x0, "pco", "Power condition (obsolete, moved to 0x1a)"},
152     {0x10, 0x0, "xo", "XOR control"}, /* obsolete in sbc3r32 */
153     {0x1a, 0xf1, "apo", "ATA Power condition"},
154     {0x1c, 0x1, "bc", "Background control"},
155     {0x1c, 0x2, "lbp", "Logical block provisioning"},
156     {0x0, 0x0, NULL, NULL},
157 };
158 
159 static struct page_code_desc pc_desc_tape[] = {
160     {0x1, 0x0, "rw", "Read-Write error recovery"},
161     {0xa, 0xf0, "cdp", "Control data protection"},
162     {0xf, 0x0, "dac", "Data Compression"},
163     {0x10, 0x0, "dc", "Device configuration"},
164     {0x10, 0x1, "dcs", "Device configuration extension"},
165     {0x11, 0x0, "mpa", "Medium Partition [1]"},
166     {0x12, 0x0, "mpa2", "Medium Partition [2]"},
167     {0x13, 0x0, "mpa3", "Medium Partition [3]"},
168     {0x14, 0x0, "mpar", "Medium Partition [4]"},
169     {0x1c, 0x0, "ie", "Informational exceptions control (tape version)"},
170     {0x1d, 0x0, "mco", "Medium configuration"},
171     {0x0, 0x0, NULL, NULL},
172 };
173 
174 static struct page_code_desc pc_desc_cddvd[] = {
175     {0x1, 0x0, "rw", "Read-Write error recovery"},
176     {0x3, 0x0, "mrw", "Mount Rainer rewritable"},
177     {0x5, 0x0, "wp", "Write parameters"},
178     {0x7, 0x0, "ve", "Verify error recovery"},
179     {0x8, 0x0, "ca", "Caching"},
180     {0xd, 0x0, "cddp", "CD device parameters (obsolete)"},
181     {0xe, 0x0, "cda", "CD audio"},
182     {0x1a, 0x0, "po", "Power condition (mmc)"},
183     {0x1c, 0x0, "ffrc", "Fault/failure reporting control (mmc)"},
184     {0x1d, 0x0, "tp", "Timeout and protect"},
185     {0x2a, 0x0, "cms", "MM capabilities and mechanical status (obsolete)"},
186     {0x0, 0x0, NULL, NULL},
187 };
188 
189 static struct page_code_desc pc_desc_smc[] = {
190     {0x1d, 0x0, "eaa", "Element address assignment"},
191     {0x1e, 0x0, "tgp", "Transport geometry parameters"},
192     {0x1f, 0x0, "dcs", "Device capabilities"},
193     {0x1f, 0x41, "edc", "Extended device capabilities"},
194     {0x0, 0x0, NULL, NULL},
195 };
196 
197 static struct page_code_desc pc_desc_scc[] = {
198     {0x1b, 0x0, "sslm", "LUN mapping"},
199     {0x0, 0x0, NULL, NULL},
200 };
201 
202 static struct page_code_desc pc_desc_ses[] = {
203     {0x14, 0x0, "esm", "Enclosure services management"},
204     {0x0, 0x0, NULL, NULL},
205 };
206 
207 static struct page_code_desc pc_desc_rbc[] = {
208     {0x6, 0x0, "rbc", "RBC device parameters"},
209     {0x0, 0x0, NULL, NULL},
210 };
211 
212 static struct page_code_desc pc_desc_adc[] = {
213     /* {0xe, 0x0, "ADC device configuration"}, */
214     {0xe, 0x1, "adtd", "Target device"},
215     {0xe, 0x2, "addp", "DT device primary port"},
216     {0xe, 0x3, "adlu", "Logical unit"},
217     {0xe, 0x4, "adts", "Target device serial number"},
218     {0x0, 0x0, NULL, NULL},
219 };
220 
221 
222 /* Transport reated mode pages */
223 static struct page_code_desc pc_desc_t_fcp[] = {
224     {0x18, 0x0, "pl", "LU control"},
225     {0x19, 0x0, "pp", "Port control"},
226     {0x0, 0x0, NULL, NULL},
227 };
228 
229 static struct page_code_desc pc_desc_t_spi4[] = {
230     {0x18, 0x0, "luc", "LU control"},
231     {0x19, 0x0, "pp", "Port control short format"},
232     {0x19, 0x1, "mc", "Margin control"},
233     {0x19, 0x2, "stc", "Saved training configuration value"},
234     {0x19, 0x3, "ns", "Negotiated settings"},
235     {0x19, 0x4, "rtc", "Report transfer capabilities"},
236     {0x0, 0x0, NULL, NULL},
237 };
238 
239 /* SAS protocol layers now in SPL standards */
240 static struct page_code_desc pc_desc_t_sas[] = {
241     {0x18, 0x0, "pslu", "Protocol specific logical unit (SPL)"},
242     {0x19, 0x0, "pspo", "Protocol specific port (SPL)"},
243     {0x19, 0x1, "pcd", "Phy control and discover (SPL)"},
244     {0x19, 0x2, "spc", "Shared port control (SPL)"},
245     {0x19, 0x3, "sep", "Enhanced phy control (SPL)"},
246     {0x19, 0x4, "oobm", "Out of band management control (SPL)"}, /* spl5r01 */
247     {0x0, 0x0, NULL, NULL},
248 };
249 
250 static struct page_code_desc pc_desc_t_adc[] = {
251     {0xe, 0x1, "addt", "Target device"},
252     {0xe, 0x2, "addp", "DT device primary port"},
253     {0xe, 0x3, "adlu", "Logical unit"},
254     {0x18, 0x0, "pslu", "Protocol specific lu"},
255     {0x19, 0x0, "pspo", "Protocol specific port"},
256     {0x0, 0x0, NULL, NULL},
257 };
258 
259 static struct page_code_desc pc_desc_zbc[] = {
260     {0x1, 0x0, "rw", "Read-Write error recovery"},
261     {0x7, 0x0, "ve", "Verify error recovery"},
262     {0x8, 0x0, "ca", "Caching"},
263     {0xa, 0x2, "atag", "Application tag"},
264     {0xa, 0xf, "zbdct", "Zoned block device control"},  /* zbc2r04a */
265     {0x1c, 0x1, "bc", "Background control"},
266     {0x0, 0x0, NULL, NULL},
267 };
268 
269 struct pc_desc_group pcd_gr_arr[] = {
270     {pc_desc_common, "common"},
271     {pc_desc_disk, "disk"},
272     {pc_desc_tape, "tape"},
273     {pc_desc_cddvd, "cd/dvd"},
274     {pc_desc_smc, "media changer"},
275     {pc_desc_scc, "scsi controller"},
276     {pc_desc_ses, "enclosure"},
277     {pc_desc_rbc, "reduced block"},
278     {pc_desc_adc, "adc"},
279     {pc_desc_zbc, "zbc"},
280     {pc_desc_t_fcp, "transport: FCP"},
281     {pc_desc_t_spi4, "transport: SPI"},
282     {pc_desc_t_sas, "transport: SAS"},
283     {pc_desc_t_adc, "transport: ADC"},
284 
285     {NULL, NULL},
286 };
287 
288 
289 
290 static void
usage()291 usage()
292 {
293     printf("Usage: sg_modes [--all] [--control=PC] [--dbd] [--dbout] "
294            "[--examine]\n"
295            "                [--flexible] [--help] [--hex] [--list] "
296            "[--llbaa]\n"
297            "                [--maxlen=LEN] [--page=PG[,SPG]] [--raw] [-R] "
298            "[--readwrite]\n"
299            "                [--six] [--verbose] [--version] [DEVICE]\n"
300            "  where:\n"
301            "    --all|-a        get all mode pages supported by device\n"
302            "                    use twice to get all mode pages and subpages\n"
303            "    --control=PC|-c PC    page control (default: 0)\n"
304            "                       0: current, 1: changeable,\n"
305            "                       2: (manufacturer's) defaults, 3: saved\n"
306            "    --dbd|-d        disable block descriptors (DBD field in cdb)\n"
307            "    --dbout|-D      disable block descriptor output\n"
308            "    --examine|-e    examine pages # 0 through to 0x3e, note if "
309            "found\n"
310            "    --flexible|-f    be flexible, cope with MODE SENSE 6/10 "
311            "response mixup\n");
312     printf("    --help|-h       print usage message then exit\n"
313            "    --hex|-H        output full response in hex\n"
314            "                    use twice to output page number and header "
315            "in hex\n"
316            "    --list|-l       list common page codes for device peripheral "
317            "type,\n"
318            "                    if no device given then assume disk type\n"
319            "    --llbaa|-L      set Long LBA Accepted (LLBAA field in mode "
320            "sense (10) cdb)\n"
321            "    --maxlen=LEN|-m LEN    max response length (allocation "
322            "length in cdb)\n"
323            "                           (def: 0 -> 4096 or 252 (for MODE "
324            "SENSE 6) bytes)\n"
325            "    --page=PG|-p PG    page code to fetch (def: 63). May be "
326            "acronym\n"
327            "    --page=PG,SPG|-p PG,SPG\n"
328            "                       page code and subpage code to fetch "
329            "(defs: 63,0)\n"
330            "    --raw|-r        output response in binary to stdout\n"
331            "    -R              mode page response to stdout, a byte per "
332            "line in ASCII\n"
333            "                    hex (same result as '--raw --raw')\n"
334            "    --readwrite|-w    open DEVICE read-write (def: open "
335            "read-only)\n"
336            "    --six|-6|-s     use MODE SENSE(6), by default uses MODE "
337            "SENSE(10)\n"
338            "    --verbose|-v    increase verbosity\n"
339            "    --old|-O        use old interface (use as first option)\n"
340            "    --version|-V    output version string then exit\n\n"
341            "Performs a SCSI MODE SENSE (10 or 6) command. To access and "
342            "possibly change\nmode page fields see the sdparm utility.\n");
343 }
344 
345 static void
usage_old()346 usage_old()
347 {
348     printf("Usage:  sg_modes [-a] [-A] [-c=PC] [-d] [-D] [-e] [-f] [-h] "
349            "[-H] [-l] [-L]\n"
350            "                 [-m=LEN] [-p=PG[,SPG]] [-r] [-subp=SPG] [-v] "
351            "[-V] [-6]\n"
352            "                 [DEVICE]\n"
353            " where:\n"
354            "   -a    get all mode pages supported by device\n"
355            "   -A    get all mode pages and subpages supported by device\n"
356            "   -c=PC    page control (def: 0 [current],"
357            " 1 [changeable],\n"
358            "                               2 [default], 3 [saved])\n"
359            "   -d    disable block descriptors (DBD field in cdb)\n"
360            "   -D    disable block descriptor output\n"
361            "   -e    examine pages # 0 through to 0x3e, note if found\n"
362            "   -f    be flexible, cope with MODE SENSE 6/10 response "
363            "mixup\n");
364     printf("   -h    output page number and header in hex\n"
365            "   -H    output page number and header in hex (same as '-h')\n"
366            "   -l    list common page codes for device peripheral type,\n"
367            "         if no device given then assume disk type\n"
368            "   -L    set Long LBA Accepted (LLBAA field in mode sense "
369            "10 cdb)\n"
370            "   -m=LEN    max response length (allocation length in cdb)\n"
371            "             (def: 0 -> 4096 or 252 (for MODE SENSE 6) bytes)\n"
372            "   -p=PG     page code in hex (def: 3f). No acronym allowed\n"
373            "   -p=PG,SPG    both in hex, (defs: 3f,0)\n"
374            "   -r    mode page output to stdout, a byte per line in "
375            "ASCII hex\n"
376            "   -subp=SPG    sub page code in hex (def: 0)\n"
377            "   -v    verbose\n"
378            "   -V    output version string\n"
379            "   -6    Use MODE SENSE(6), by default uses MODE SENSE(10)\n"
380            "   -N|--new     use new interface\n"
381            "   -?    output this usage message\n\n"
382            "Performs a SCSI MODE SENSE (10 or 6) command\n");
383 }
384 
385 static void
enum_pc_desc(void)386 enum_pc_desc(void)
387 {
388     bool first = true;
389     const struct pc_desc_group * pcd_grp = pcd_gr_arr;
390     char b[128];
391 
392     for ( ; pcd_grp->pcdp; ++pcd_grp) {
393         const struct page_code_desc * pcdp = pcd_grp->pcdp;
394 
395         if (first)
396             first = false;
397         else
398             printf("\n");
399         printf("Mode pages group: %s:\n", pcd_grp->group_name);
400         for ( ; pcdp->acron; ++pcdp) {
401             if (pcdp->subpage_code > 0)
402                 snprintf(b, sizeof(b), "[0x%x,0x%x]", pcdp->page_code,
403                          pcdp->subpage_code);
404             else
405                 snprintf(b, sizeof(b), "[0x%x]", pcdp->page_code);
406             printf("  %s: %s  %s\n", pcdp->acron, pcdp->desc, b);
407         }
408     }
409 }
410 
411 static const struct page_code_desc *
find_pc_desc(const char * acron)412 find_pc_desc(const char * acron)
413 {
414     const struct pc_desc_group * pcd_grp = pcd_gr_arr;
415 
416     for ( ; pcd_grp->pcdp; ++pcd_grp) {
417         const struct page_code_desc * pcdp = pcd_grp->pcdp;
418 
419         for ( ; pcdp->acron; ++pcdp) {
420             if (0 == strcmp(acron, pcdp->acron))
421                 return pcdp;
422         }
423     }
424     return NULL;
425 }
426 
427 static void
usage_for(const struct opts_t * op)428 usage_for(const struct opts_t * op)
429 {
430     if (op->opt_new)
431         usage();
432     else
433         usage_old();
434 }
435 
436 /* Processes command line options according to new option format. Returns
437  * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
438 static int
new_parse_cmd_line(struct opts_t * op,int argc,char * argv[])439 new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
440 {
441     int c, n, nn;
442     char * cp;
443 
444     while (1) {
445         int option_index = 0;
446 
447         c = getopt_long(argc, argv, "6aAc:dDefhHlLm:NOp:rRsvV", long_options,
448                         &option_index);
449         if (c == -1)
450             break;
451 
452         switch (c) {
453         case '6':
454             op->do_six = true;
455             break;
456         case 'a':
457             ++op->do_all;
458             break;
459         case 'A':
460             op->do_all += 2;
461             break;
462         case 'c':
463             n = sg_get_num(optarg);
464             if ((n < 0) || (n > 3)) {
465                 pr2serr("bad argument to '--control='\n");
466                 usage();
467                 return SG_LIB_SYNTAX_ERROR;
468             }
469             op->page_control = n;
470             break;
471         case 'd':
472             op->do_dbd = true;
473             break;
474         case 'D':
475             op->do_dbout = true;
476             break;
477         case 'e':
478             op->do_examine = true;
479             break;
480         case 'f':
481             op->do_flexible = true;
482             break;
483         case 'h':
484         case '?':
485             ++op->do_help;
486             break;
487         case 'H':
488             ++op->do_hex;
489             break;
490         case 'l':
491             op->do_list = true;
492             break;
493         case 'L':
494             op->do_llbaa = true;
495             break;
496         case 'm':
497             n = sg_get_num(optarg);
498             if ((n < 0) || (n > 65535)) {
499                 pr2serr("bad argument to '--maxlen='\n");
500                 usage();
501                 return SG_LIB_SYNTAX_ERROR;
502             }
503             if ((n > 0) && (n < 4)) {
504                 pr2serr("Changing that '--maxlen=' value to 4\n");
505                 n = 4;
506             }
507             op->maxlen = n;
508             break;
509         case 'N':
510             break;      /* ignore */
511         case 'O':
512             op->opt_new = false;
513             return 0;
514         case 'p':
515             if (isalpha((uint8_t)optarg[0])) {
516                 const struct page_code_desc * pcdp;
517 
518                 op->page_acron = optarg;
519                 if (0 == memcmp("xxx", optarg, 3)) {
520                     enum_pc_desc();
521                     return SG_LIB_OK_FALSE;     /* for quick exit */
522                 }
523                 pcdp = find_pc_desc(optarg);
524                 if (pcdp) {
525                     if (pcdp->subpage_code > 0) {
526                         op->subpg_code = pcdp->subpage_code;
527                         op->subpg_code_given = true;
528                     }
529                     op->pg_code = pcdp->page_code;
530                 } else {
531                     pr2serr(" Couldn't match acronym '%s', try '-p xxx' for "
532                             "list\n", optarg);
533                     return SG_LIB_SYNTAX_ERROR;
534                 }
535             } else {
536                 cp = strchr(optarg, ',');
537                 n = sg_get_num_nomult(optarg);
538                 if ((n < 0) || (n > 63)) {
539                     pr2serr("Bad argument to '--page='\n");
540                     usage();
541                     return SG_LIB_SYNTAX_ERROR;
542                 }
543                 if (cp) {
544                     nn = sg_get_num_nomult(cp + 1);
545                     if ((nn < 0) || (nn > 255)) {
546                         pr2serr("Bad second value in argument to "
547                                 "'--page='\n");
548                         usage();
549                         return SG_LIB_SYNTAX_ERROR;
550                     }
551                     op->subpg_code = nn;
552                     op->subpg_code_given = true;
553                 }
554                 op->pg_code = n;
555             }
556             break;
557         case 'r':
558             ++op->do_raw;
559             break;
560         case 'R':
561             op->do_raw += 2;
562             break;
563         case 's':
564             op->do_six = true;
565             break;
566         case 'v':
567             op->verbose_given = true;
568             ++op->verbose;
569             break;
570         case 'V':
571             op->version_given = true;
572             break;
573         case 'w':
574             op->o_readwrite = true;
575             break;
576         default:
577             pr2serr("unrecognised option code %c [0x%x]\n", c, c);
578             if (op->do_help)
579                 break;
580             usage();
581             return SG_LIB_SYNTAX_ERROR;
582         }
583     }
584     if (optind < argc) {
585         if (NULL == op->device_name) {
586             op->device_name = argv[optind];
587             ++optind;
588         }
589         if (optind < argc) {
590             for (; optind < argc; ++optind)
591                 pr2serr("Unexpected extra argument: %s\n",
592                         argv[optind]);
593             usage();
594             return SG_LIB_SYNTAX_ERROR;
595         }
596     }
597     return 0;
598 }
599 
600 /* Processes command line options according to old option format. Returns
601  * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
602 static int
old_parse_cmd_line(struct opts_t * op,int argc,char * argv[])603 old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
604 {
605     bool jmp_out;
606     int k, plen, num, n;
607     char pc1;
608     unsigned int u, uu;
609     const char * cp;
610 
611     for (k = 1; k < argc; ++k) {
612         cp = argv[k];
613         plen = strlen(cp);
614         if (plen <= 0)
615             continue;
616         if ('-' == *cp) {
617             for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
618                 switch (*cp) {
619                 case '6':
620                     op->do_six = true;
621                     break;
622                 case 'a':
623                     ++op->do_all;
624                     break;
625                 case 'A':
626                     op->do_all += 2;
627                     break;
628                 case 'd':
629                     op->do_dbd = true;
630                     break;
631                 case 'D':
632                     op->do_dbout = true;
633                     break;
634                 case 'e':
635                     op->do_examine = true;
636                     break;
637                 case 'f':
638                     op->do_flexible = true;
639                     break;
640                 case 'h':
641                 case 'H':
642                     op->do_hex += 2;
643                     break;
644                 case 'l':
645                     op->do_list = true;
646                     break;
647                 case 'L':
648                     op->do_llbaa = true;
649                     break;
650                 case 'N':
651                     op->opt_new = true;
652                     return 0;
653                 case 'O':
654                     break;
655                 case 'r':
656                     op->do_raw += 2;
657                     break;
658                 case 'v':
659                     op->verbose_given = true;
660                     ++op->verbose;
661                     break;
662                 case 'V':
663                     op->version_given = true;
664                     break;
665                 case '?':
666                     ++op->do_help;
667                     break;
668                 default:
669                     jmp_out = true;
670                     break;
671                 }
672                 if (jmp_out)
673                     break;
674             }
675             if (plen <= 0)
676                 continue;
677             if (0 == strncmp("c=", cp, 2)) {
678                 num = sscanf(cp + 2, "%x", &u);
679                 if ((1 != num) || (u > 3)) {
680                     pr2serr("Bad page control after 'c=' option\n");
681                     usage_old();
682                     return SG_LIB_SYNTAX_ERROR;
683                 }
684                 op->page_control = u;
685             } else if (0 == strncmp("m=", cp, 2)) {
686                 num = sscanf(cp + 2, "%d", &n);
687                 if ((1 != num) || (n < 0) || (n > 65535)) {
688                     pr2serr("Bad argument after 'm=' option\n");
689                     usage_old();
690                     return SG_LIB_SYNTAX_ERROR;
691                 }
692                 if ((n > 0) && (n < 4)) {
693                     pr2serr("Changing that '-m=' value to 4\n");
694                     n = 4;
695                 }
696                 op->maxlen = n;
697             } else if (0 == strncmp("p=", cp, 2)) {
698                 pc1 = *(cp + 2);
699                 if (isalpha(pc1) && ((islower(pc1) && (pc1 > 'f')) ||
700                                      (isupper(pc1) && (pc1 > 'F')))) {
701                     pr2serr("Old format doesn't accept mode page acronyms: "
702                             "%s\n", cp + 2);
703                     return SG_LIB_SYNTAX_ERROR;
704                 }
705                 if (NULL == strchr(cp + 2, ',')) {
706                     num = sscanf(cp + 2, "%x", &u);
707                     if ((1 != num) || (u > 63)) {
708                         pr2serr("Bad page code value after 'p=' option\n");
709                         usage_old();
710                         return SG_LIB_SYNTAX_ERROR;
711                     }
712                     op->pg_code = u;
713                 } else if (2 == sscanf(cp + 2, "%x,%x", &u, &uu)) {
714                     if (uu > 255) {
715                         pr2serr("Bad subpage code value after 'p=' option\n");
716                         usage_old();
717                         return SG_LIB_SYNTAX_ERROR;
718                     }
719                     op->pg_code = u;
720                     op->subpg_code = uu;
721                     op->subpg_code_given = true;
722                 } else {
723                     pr2serr("Bad page code, subpage code sequence after 'p=' "
724                             "option\n");
725                     usage_old();
726                     return SG_LIB_SYNTAX_ERROR;
727                 }
728             } else if (0 == strncmp("subp=", cp, 5)) {
729                 num = sscanf(cp + 5, "%x", &u);
730                 if ((1 != num) || (u > 255)) {
731                     pr2serr("Bad sub page code after 'subp=' option\n");
732                     usage_old();
733                     return SG_LIB_SYNTAX_ERROR;
734                 }
735                 op->subpg_code = u;
736                 op->subpg_code_given = true;
737                 if (-1 == op->pg_code)
738                     op->pg_code = 0;
739             } else if (0 == strncmp("-old", cp, 4))
740                 ;
741             else if (jmp_out) {
742                 pr2serr("Unrecognized option: %s\n", cp);
743                 usage_old();
744                 return SG_LIB_SYNTAX_ERROR;
745             }
746         } else if (0 == op->device_name)
747             op->device_name = cp;
748         else {
749             pr2serr("too many arguments, got: %s, not expecting: %s\n",
750                     op->device_name, cp);
751             usage_old();
752             return SG_LIB_SYNTAX_ERROR;
753         }
754     }
755     return 0;
756 }
757 
758 /* Process command line options. First check using new option format unless
759  * the SG3_UTILS_OLD_OPTS environment variable is defined which causes the
760  * old option format to be checked first. Both new and old format can be
761  * countermanded by a '-O' and '-N' options respectively. As soon as either
762  * of these options is detected (when processing the other format), processing
763  * stops and is restarted using the other format. Clear? */
764 static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])765 parse_cmd_line(struct opts_t * op, int argc, char * argv[])
766 {
767     int res;
768     char * cp;
769 
770     cp = getenv("SG3_UTILS_OLD_OPTS");
771     if (cp) {
772         op->opt_new = false;
773         res = old_parse_cmd_line(op, argc, argv);
774         if ((0 == res) && op->opt_new)
775             res = new_parse_cmd_line(op, argc, argv);
776     } else {
777         op->opt_new = true;
778         res = new_parse_cmd_line(op, argc, argv);
779         if ((0 == res) && (! op->opt_new))
780             res = old_parse_cmd_line(op, argc, argv);
781     }
782     return res;
783 }
784 
785 static void
dStrRaw(const uint8_t * str,int len)786 dStrRaw(const uint8_t * str, int len)
787 {
788     int k;
789 
790     for (k = 0; k < len; ++k)
791         printf("%c", str[k]);
792 }
793 
794 /* Note to coverity: this function is safe as long as the page_code_desc
795  * objects pointed to by pcdp have a sentinel object at the end of each
796  * array. And they do by design.*/
797 static int
count_desc_elems(const struct page_code_desc * pcdp)798 count_desc_elems(const struct page_code_desc * pcdp)
799 {
800     int k;
801 
802     for (k = 0; k < 1024; ++k, ++pcdp) {
803         if (NULL == pcdp->acron)
804             return k;
805     }
806     pr2serr("%s: sanity check trip, invalid pc_desc table\n", __func__);
807     return k;
808 }
809 
810 /* Returns pointer to base of table for scsi_ptype or pointer to common
811  * table if scsi_ptype is -1. Yields numbers of elements in returned
812  * table via pointer sizep. If scsi_ptype not known then returns NULL
813  * with *sizep set to zero. */
814 static struct page_code_desc *
get_mpage_tbl_size(int scsi_ptype,int * sizep)815 get_mpage_tbl_size(int scsi_ptype, int * sizep)
816 {
817     switch (scsi_ptype)
818     {
819         case -1:        /* common list */
820             *sizep = count_desc_elems(pc_desc_common);
821             return &pc_desc_common[0];
822         case PDT_DISK:         /* disk (direct access) type devices */
823         case PDT_WO:
824         case PDT_OPTICAL:
825             *sizep = count_desc_elems(pc_desc_disk);
826             return &pc_desc_disk[0];
827         case PDT_TAPE:         /* tape devices */
828         case PDT_PRINTER:
829             *sizep = count_desc_elems(pc_desc_tape);
830             return &pc_desc_tape[0];
831         case PDT_MMC:         /* cd/dvd/bd devices */
832             *sizep = count_desc_elems(pc_desc_cddvd);
833             return &pc_desc_cddvd[0];
834         case PDT_MCHANGER:         /* medium changer devices */
835             *sizep = count_desc_elems(pc_desc_smc);
836             return &pc_desc_smc[0];
837         case PDT_SAC:       /* storage array devices */
838             *sizep = count_desc_elems(pc_desc_scc);
839             return &pc_desc_scc[0];
840         case PDT_SES:       /* enclosure services devices */
841             *sizep = count_desc_elems(pc_desc_ses);
842             return &pc_desc_ses[0];
843         case PDT_RBC:       /* simplified direct access device */
844             *sizep = count_desc_elems(pc_desc_rbc);
845             return &pc_desc_rbc[0];
846         case PDT_ADC:       /* automation device/interface */
847             *sizep = count_desc_elems(pc_desc_adc);
848             return &pc_desc_adc[0];
849         case PDT_ZBC:
850             *sizep = count_desc_elems(pc_desc_zbc);
851             return &pc_desc_zbc[0];
852     }
853     *sizep = 0;
854     return NULL;
855 }
856 
857 
858 static struct page_code_desc *
get_mpage_trans_tbl_size(int t_proto,int * sizep)859 get_mpage_trans_tbl_size(int t_proto, int * sizep)
860 {
861     switch (t_proto)
862     {
863         case TPROTO_FCP:
864             *sizep = count_desc_elems(pc_desc_t_fcp);
865             return &pc_desc_t_fcp[0];
866         case TPROTO_SPI:
867             *sizep = count_desc_elems(pc_desc_t_spi4);
868             return &pc_desc_t_spi4[0];
869         case TPROTO_SAS:
870             *sizep = count_desc_elems(pc_desc_t_sas);
871             return &pc_desc_t_sas[0];
872         case TPROTO_ADT:
873             *sizep = count_desc_elems(pc_desc_t_adc);
874             return &pc_desc_t_adc[0];
875     }
876     *sizep = 0;
877     return NULL;
878 }
879 
880 static const char *
find_page_code_desc(int page_num,int subpage_num,int scsi_ptype,bool encserv,bool mchngr,int t_proto)881 find_page_code_desc(int page_num, int subpage_num, int scsi_ptype,
882                     bool encserv, bool mchngr, int t_proto)
883 {
884     int k, num, decayed_pdt;
885     const struct page_code_desc * pcdp;
886 
887     if (t_proto >= 0) {
888         pcdp = get_mpage_trans_tbl_size(t_proto, &num);
889         if (pcdp) {
890             for (k = 0; k < num; ++k, ++pcdp) {
891                 if ((page_num == pcdp->page_code) &&
892                     (subpage_num == pcdp->subpage_code))
893                     return pcdp->desc;
894                 else if (page_num < pcdp->page_code)
895                     break;
896             }
897         }
898     }
899 try_again:
900     pcdp = get_mpage_tbl_size(scsi_ptype, &num);
901     if (pcdp) {
902         for (k = 0; k < num; ++k, ++pcdp) {
903             if ((page_num == pcdp->page_code) &&
904                 (subpage_num == pcdp->subpage_code))
905                 return pcdp->desc;
906             else if (page_num < pcdp->page_code)
907                 break;
908         }
909     }
910     decayed_pdt = sg_lib_pdt_decay(scsi_ptype);
911     if (decayed_pdt != scsi_ptype) {
912         scsi_ptype = decayed_pdt;
913         goto try_again;
914     }
915     if ((0xd != scsi_ptype) && encserv) {
916         /* check for attached enclosure services processor */
917         pcdp = get_mpage_tbl_size(0xd, &num);
918         if (pcdp) {
919             for (k = 0; k < num; ++k, ++pcdp) {
920                 if ((page_num == pcdp->page_code) &&
921                     (subpage_num == pcdp->subpage_code))
922                     return pcdp->desc;
923                 else if (page_num < pcdp->page_code)
924                     break;
925             }
926         }
927     }
928     if ((0x8 != scsi_ptype) && mchngr) {
929         /* check for attached medium changer device */
930         pcdp = get_mpage_tbl_size(0x8, &num);
931         if (pcdp) {
932             for (k = 0; k < num; ++k, ++pcdp) {
933                 if ((page_num == pcdp->page_code) &&
934                     (subpage_num == pcdp->subpage_code))
935                     return pcdp->desc;
936                 else if (page_num < pcdp->page_code)
937                     break;
938             }
939         }
940     }
941     pcdp = get_mpage_tbl_size(-1, &num);
942     for (k = 0; k < num; ++k, ++pcdp) {
943         if ((page_num == pcdp->page_code) &&
944             (subpage_num == pcdp->subpage_code))
945             return pcdp->desc;
946         else if (page_num < pcdp->page_code)
947             break;
948     }
949     return NULL;
950 }
951 
952 static void
list_page_codes(int scsi_ptype,bool encserv,bool mchngr,int t_proto)953 list_page_codes(int scsi_ptype, bool encserv, bool mchngr, int t_proto)
954 {
955     int num, num_ptype, pg, spg, c, d;
956     bool valid_transport;
957     const struct page_code_desc * dp;
958     const struct page_code_desc * pe_dp;
959     char b[64];
960 
961     valid_transport = ((t_proto >= 0) && (t_proto <= 0xf));
962     printf("Page[,subpage]   Name\n");
963     printf("=====================\n");
964     dp = get_mpage_tbl_size(-1, &num);
965     pe_dp = get_mpage_tbl_size(scsi_ptype, &num_ptype);
966     while (1) {
967         pg = dp ? dp->page_code : PG_CODE_ALL + 1;
968         spg = dp ? dp->subpage_code : SPG_CODE_ALL;
969         c = (pg << 8) + spg;
970         pg = pe_dp ? pe_dp->page_code : PG_CODE_ALL + 1;
971         spg = pe_dp ? pe_dp->subpage_code : SPG_CODE_ALL;
972         d = (pg << 8) + spg;
973         if (valid_transport &&
974             ((PROTO_SPECIFIC_1 == c) || (PROTO_SPECIFIC_2 == c)))
975             dp = (--num <= 0) ? NULL : (dp + 1); /* skip protocol specific */
976         else if (c == d) {
977             if (pe_dp) {
978                 if (pe_dp->subpage_code)
979                     printf(" 0x%02x,0x%02x    *  %s\n", pe_dp->page_code,
980                            pe_dp->subpage_code, pe_dp->desc);
981                 else
982                     printf(" 0x%02x         *  %s\n", pe_dp->page_code,
983                            pe_dp->desc);
984                 pe_dp = (--num_ptype <= 0) ? NULL : (pe_dp + 1);
985             }
986             if (dp)
987                 dp = (--num <= 0) ? NULL : (dp + 1);
988         } else if (c < d) {
989             if (dp) {
990                 if (dp->subpage_code)
991                     printf(" 0x%02x,0x%02x       %s\n", dp->page_code,
992                            dp->subpage_code, dp->desc);
993                 else
994                     printf(" 0x%02x            %s\n", dp->page_code,
995                            dp->desc);
996                 dp = (--num <= 0) ? NULL : (dp + 1);
997             }
998         } else {
999             if (pe_dp) {
1000                 if (pe_dp->subpage_code)
1001                     printf(" 0x%02x,0x%02x       %s\n", pe_dp->page_code,
1002                            pe_dp->subpage_code, pe_dp->desc);
1003                 else
1004                     printf(" 0x%02x            %s\n", pe_dp->page_code,
1005                            pe_dp->desc);
1006                 pe_dp = (--num_ptype <= 0) ? NULL : (pe_dp + 1);
1007             }
1008         }
1009         if ((NULL == dp) && (NULL == pe_dp))
1010             break;
1011     }
1012     if ((0xd != scsi_ptype) && encserv) {
1013         /* check for attached enclosure services processor */
1014         printf("\n    Attached enclosure services processor\n");
1015         dp = get_mpage_tbl_size(0xd, &num);
1016         while (dp) {
1017             if (dp->subpage_code)
1018                 printf(" 0x%02x,0x%02x       %s\n", dp->page_code,
1019                        dp->subpage_code, dp->desc);
1020             else
1021                 printf(" 0x%02x            %s\n", dp->page_code,
1022                        dp->desc);
1023             dp = (--num <= 0) ? NULL : (dp + 1);
1024         }
1025     }
1026     if ((0x8 != scsi_ptype) && mchngr) {
1027         /* check for attached medium changer device */
1028         printf("\n    Attached medium changer device\n");
1029         dp = get_mpage_tbl_size(0x8, &num);
1030         while (dp) {
1031             if (dp->subpage_code)
1032                 printf(" 0x%02x,0x%02x       %s\n", dp->page_code,
1033                        dp->subpage_code, dp->desc);
1034             else
1035                 printf(" 0x%02x            %s\n", dp->page_code,
1036                        dp->desc);
1037             dp = (--num <= 0) ? NULL : (dp + 1);
1038         }
1039     }
1040     if (valid_transport) {
1041         printf("\n    Transport protocol: %s\n",
1042                sg_get_trans_proto_str(t_proto, sizeof(b), b));
1043         dp = get_mpage_trans_tbl_size(t_proto, &num);
1044         while (dp) {
1045             if (dp->subpage_code)
1046                 printf(" 0x%02x,0x%02x       %s\n", dp->page_code,
1047                        dp->subpage_code, dp->desc);
1048             else
1049                 printf(" 0x%02x            %s\n", dp->page_code,
1050                        dp->desc);
1051             dp = (--num <= 0) ? NULL : (dp + 1);
1052         }
1053     }
1054 }
1055 
1056 /* Returns 0 for ok, else error value */
1057 static int
examine_pages(int sg_fd,int inq_pdt,bool encserv,bool mchngr,const struct opts_t * op)1058 examine_pages(int sg_fd, int inq_pdt, bool encserv, bool mchngr,
1059               const struct opts_t * op)
1060 {
1061     bool header_printed;
1062     int k, mresp_len, len, resid;
1063     int res = 0;
1064     const int mx_len = op->do_six ? DEF_6_ALLOC_LEN : DEF_ALLOC_LEN;
1065     const char * cp;
1066     uint8_t * rbuf;
1067     uint8_t * free_rbuf = NULL;
1068 
1069     rbuf = sg_memalign(mx_len, 0, &free_rbuf, false);
1070     if (NULL == rbuf) {
1071         pr2serr("%s: out of heap\n", __func__);
1072         return sg_convert_errno(ENOMEM);
1073     }
1074     mresp_len = (op->do_raw || op->do_hex) ? mx_len : 4;
1075     for (header_printed = false, k = 0; k < PG_CODE_MAX; ++k) {
1076         resid = 0;
1077         if (op->do_six) {
1078             res = sg_ll_mode_sense6(sg_fd, 0, 0, k, 0, rbuf, mresp_len,
1079                                     true, op->verbose);
1080             if (SG_LIB_CAT_INVALID_OP == res) {
1081                 pr2serr(">>>>>> try again without the '-6' switch for a 10 "
1082                         "byte MODE SENSE command\n");
1083                 goto out;
1084             } else if (SG_LIB_CAT_NOT_READY == res) {
1085                 pr2serr("MODE SENSE (6) failed, device not ready\n");
1086                 goto out;
1087             }
1088         } else {
1089             res = sg_ll_mode_sense10_v2(sg_fd, 0, 0, 0, k, 0, rbuf, mresp_len,
1090                                         0, &resid, true, op->verbose);
1091             if (SG_LIB_CAT_INVALID_OP == res) {
1092                 pr2serr(">>>>>> try again with a '-6' switch for a 6 byte "
1093                         "MODE SENSE command\n");
1094                 goto out;
1095             } else if (SG_LIB_CAT_NOT_READY == res) {
1096                 pr2serr("MODE SENSE (10) failed, device not ready\n");
1097                 goto out;
1098             }
1099         }
1100         if (0 == res) {
1101             len = sg_msense_calc_length(rbuf, mresp_len, op->do_six, NULL);
1102             if (resid > 0) {
1103                 mresp_len -= resid;
1104                 if (mresp_len < 0) {
1105                     pr2serr("%s: MS(10) resid=%d implies negative response "
1106                             "length (%d)\n", __func__, resid, mresp_len);
1107                     res = SG_LIB_WILD_RESID;
1108                     goto out;
1109                 }
1110             }
1111             if (len > mresp_len)
1112                 len = mresp_len;
1113             if (op->do_raw) {
1114                 dStrRaw(rbuf, len);
1115                 continue;
1116             }
1117             if (op->do_hex > 2) {
1118                 hex2stdout(rbuf, len, -1);
1119                 continue;
1120             }
1121             if (! header_printed) {
1122                 printf("Discovered mode pages:\n");
1123                 header_printed = true;
1124             }
1125             cp = find_page_code_desc(k, 0, inq_pdt, encserv, mchngr, -1);
1126             if (cp)
1127                 printf("    %s\n", cp);
1128             else
1129                 printf("    [0x%x]\n", k);
1130             if (op->do_hex)
1131                 hex2stdout(rbuf, len, 1);
1132         } else if (op->verbose) {
1133             char b[80];
1134 
1135             sg_get_category_sense_str(res, sizeof(b), b, op->verbose - 1);
1136             pr2serr("MODE SENSE (%s) failed: %s\n", (op->do_six ? "6" : "10"),
1137                     b);
1138         }
1139     }
1140 out:
1141     if (free_rbuf)
1142         free(free_rbuf);
1143     return res;
1144 }
1145 
1146 static const char * pg_control_str_arr[] = {
1147     "current",
1148     "changeable",
1149     "default",
1150     "saved",
1151 };
1152 
1153 
1154 int
main(int argc,char * argv[])1155 main(int argc, char * argv[])
1156 {
1157     bool resp_mode6, longlba, spf;
1158     bool encserv = false;
1159     bool mchngr = false;
1160     uint8_t uc;
1161     int k, num, len, res, md_len, bd_len, page_num, resid;
1162     int density_code_off, t_proto, inq_pdt, num_ua_pages, vb;
1163     int sg_fd = -1;
1164     int ret = 0;
1165     int rsp_buff_sz = DEF_ALLOC_LEN;
1166     const char * descp;
1167     struct opts_t * op;
1168     uint8_t * rsp_buff = NULL;
1169     uint8_t * free_rsp_buff = NULL;
1170     uint8_t * bp;
1171     const char * cdbLenStr;
1172     struct sg_simple_inquiry_resp inq_out;
1173     struct opts_t opts;
1174     char b[80];
1175     char ebuff[EBUFF_SZ];
1176     char pdt_name[64];
1177 
1178     op = &opts;
1179     memset(op, 0, sizeof(opts));
1180     op->pg_code = -1;
1181     res = parse_cmd_line(op, argc, argv);
1182     if (res)
1183         return (SG_LIB_OK_FALSE == res) ? 0 : res;
1184     if (op->do_help) {
1185         usage_for(op);
1186         return 0;
1187     }
1188 #ifdef DEBUG
1189     pr2serr("In DEBUG mode, ");
1190     if (op->verbose_given && op->version_given) {
1191         pr2serr("but override: '-vV' given, zero verbose and continue\n");
1192         op->verbose_given = false;
1193         op->version_given = false;
1194         op->verbose = 0;
1195     } else if (! op->verbose_given) {
1196         pr2serr("set '-vv'\n");
1197         op->verbose = 2;
1198     } else
1199         pr2serr("keep verbose=%d\n", op->verbose);
1200 #else
1201     if (op->verbose_given && op->version_given)
1202         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
1203 #endif
1204     if (op->version_given) {
1205         pr2serr("Version string: %s\n", version_str);
1206         return 0;
1207     }
1208     vb = op->verbose;
1209     if (vb && op->page_acron) {
1210         pr2serr("page acronynm: '%s' maps to page_code=0x%x",
1211                 op->page_acron, op->pg_code);
1212         if (op->subpg_code > 0)
1213             pr2serr(", subpage_code=0x%x\n", op->subpg_code);
1214         else
1215             pr2serr("\n");
1216     }
1217 
1218     if (NULL == op->device_name) {
1219         if (op->do_list) {
1220             if ((op->pg_code < 0) || (op->pg_code > PG_CODE_MAX)) {
1221                 printf("    Assume peripheral device type: disk\n");
1222                 list_page_codes(0, false, false, -1);
1223             } else {
1224                 printf("    peripheral device type: %s\n",
1225                        sg_get_pdt_str(op->pg_code, sizeof(pdt_name),
1226                                       pdt_name));
1227                 if (op->subpg_code_given)
1228                     list_page_codes(op->pg_code, false, false,
1229                                     op->subpg_code);
1230                 else
1231                     list_page_codes(op->pg_code, false, false, -1);
1232             }
1233             return 0;
1234         }
1235         pr2serr("No DEVICE argument given\n\n");
1236         usage_for(op);
1237         return SG_LIB_SYNTAX_ERROR;
1238     }
1239 
1240     if (op->do_examine && (op->pg_code >= 0)) {
1241         pr2serr("can't give '-e' and a page number\n");
1242         return SG_LIB_CONTRADICT;
1243     }
1244 
1245     if (op->do_six && op->do_llbaa) {
1246         pr2serr("LLBAA not defined for MODE SENSE 6, try without '-L'\n");
1247         return SG_LIB_CONTRADICT;
1248     }
1249     if (op->maxlen > 0) {
1250         if (op->do_six && (op->maxlen > 255)) {
1251             pr2serr("For Mode Sense (6) maxlen cannot exceed 255\n");
1252             return SG_LIB_SYNTAX_ERROR;
1253         }
1254         rsp_buff = sg_memalign(op->maxlen, 0, &free_rsp_buff, false);
1255         rsp_buff_sz = op->maxlen;
1256     } else {    /* maxlen == 0 */
1257         rsp_buff = sg_memalign(rsp_buff_sz, 0, &free_rsp_buff, false);
1258         if (op->do_six)
1259             rsp_buff_sz = DEF_6_ALLOC_LEN;
1260     }
1261     if (NULL == rsp_buff) {     /* check for both sg_memalign()s */
1262         pr2serr("Unable to allocate %d bytes on heap\n", rsp_buff_sz);
1263         return sg_convert_errno(ENOMEM);
1264     }
1265     /* If no pages or list selected than treat as 'a' */
1266     if (! ((op->pg_code >= 0) || op->do_all || op->do_list || op->do_examine))
1267         op->do_all = 1;
1268 
1269     if (op->do_raw) {
1270         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
1271             perror("sg_set_binary_mode");
1272             ret = SG_LIB_FILE_ERROR;
1273             goto fini;
1274         }
1275     }
1276 
1277     if ((sg_fd = sg_cmds_open_device(op->device_name, ! op->o_readwrite,
1278                                      vb)) < 0) {
1279         pr2serr("error opening file: %s: %s\n", op->device_name,
1280                 safe_strerror(-sg_fd));
1281         ret = sg_convert_errno(-sg_fd);
1282         goto fini;
1283     }
1284 
1285     if ((res = sg_simple_inquiry(sg_fd, &inq_out, true, vb))) {
1286         pr2serr("%s doesn't respond to a SCSI INQUIRY\n", op->device_name);
1287         ret = (res > 0) ? res : sg_convert_errno(-res);
1288         goto fini;
1289     }
1290     inq_pdt = inq_out.peripheral_type;
1291     encserv = !! (0x40 & inq_out.byte_6);
1292     mchngr = !! (0x8 & inq_out.byte_6);
1293     if ((0 == op->do_raw) && (op->do_hex < 3))
1294         printf("    %.8s  %.16s  %.4s   peripheral_type: %s [0x%x]\n",
1295                inq_out.vendor, inq_out.product, inq_out.revision,
1296                sg_get_pdt_str(inq_pdt, sizeof(pdt_name), pdt_name), inq_pdt);
1297     if (op->do_list) {
1298         if (op->subpg_code_given)
1299             list_page_codes(inq_pdt, encserv, mchngr, op->subpg_code);
1300         else
1301             list_page_codes(inq_pdt, encserv, mchngr, -1);
1302         goto fini;
1303     }
1304     if (op->do_examine) {
1305         ret = examine_pages(sg_fd, inq_pdt, encserv, mchngr, op);
1306         goto fini;
1307     }
1308     if (PG_CODE_ALL == op->pg_code) {
1309         if (0 == op->do_all)
1310             ++op->do_all;
1311     } else if (op->do_all)
1312         op->pg_code = PG_CODE_ALL;
1313     if (op->do_all > 1)
1314         op->subpg_code = SPG_CODE_ALL;
1315 
1316     if (op->do_raw > 1) {
1317         if (op->do_all) {
1318             if (op->opt_new)
1319                 pr2serr("'-R' requires a specific (sub)page, not all\n");
1320             else
1321                 pr2serr("'-r' requires a specific (sub)page, not all\n");
1322             usage_for(op);
1323             ret = SG_LIB_CONTRADICT;
1324             goto fini;
1325         }
1326     }
1327 
1328     resid = 0;
1329     if (op->do_six) {
1330         res = sg_ll_mode_sense6(sg_fd, op->do_dbd, op->page_control,
1331                                 op->pg_code, op->subpg_code, rsp_buff,
1332                                 rsp_buff_sz, true, vb);
1333         if (SG_LIB_CAT_INVALID_OP == res)
1334             pr2serr(">>>>>> try again without the '-6' switch for a 10 byte "
1335                     "MODE SENSE command\n");
1336     } else {
1337         res = sg_ll_mode_sense10_v2(sg_fd, op->do_llbaa, op->do_dbd,
1338                                     op->page_control, op->pg_code,
1339                                     op->subpg_code, rsp_buff, rsp_buff_sz,
1340                                     0, &resid, true, vb);
1341         if (SG_LIB_CAT_INVALID_OP == res)
1342             pr2serr(">>>>>> try again with a '-6' switch for a 6 byte MODE "
1343                     "SENSE command\n");
1344     }
1345     if (SG_LIB_CAT_ILLEGAL_REQ == res) {
1346         if (op->subpg_code > 0)
1347             pr2serr("invalid field in cdb (perhaps subpages not "
1348                     "supported)\n");
1349         else if (op->page_control > 0)
1350             pr2serr("invalid field in cdb (perhaps page control (PC) not "
1351                     "supported)\n");
1352         else
1353             pr2serr("invalid field in cdb (perhaps page 0x%x not "
1354                     "supported)\n", op->pg_code);
1355     } else if (res) {
1356         sg_get_category_sense_str(res, sizeof(b), b, vb);
1357         pr2serr("%s\n", b);
1358     }
1359     ret = res;
1360     if (0 == res) {
1361         int medium_type, specific, headerlen;
1362 
1363         ret = 0;
1364         resp_mode6 = op->do_six;
1365         if (op->do_flexible) {
1366             num = rsp_buff[0];
1367             if (op->do_six && (num < 3))
1368                 resp_mode6 = false;
1369             if ((! op->do_six) && (num > 5)) {
1370                 if ((num > 11) && (0 == (num % 2)) && (0 == rsp_buff[4]) &&
1371                     (0 == rsp_buff[5]) && (0 == rsp_buff[6])) {
1372                     rsp_buff[1] = num;
1373                     rsp_buff[0] = 0;
1374                     pr2serr(">>> msense(10) but resp[0]=%d and not msense(6) "
1375                             "response so fix length\n", num);
1376                 } else
1377                     resp_mode6 = true;
1378             }
1379         }
1380         cdbLenStr = resp_mode6 ? "6" : "10";
1381         if (op->do_raw || (1 == op->do_hex) || (op->do_hex > 2))
1382             ;
1383         else {
1384             if (resp_mode6 == op->do_six)
1385                 printf("Mode parameter header from MODE SENSE(%s):\n",
1386                        cdbLenStr);
1387             else
1388                 printf(" >>> Mode parameter header from MODE SENSE(%s),\n"
1389                        "     decoded as %s byte response:\n",
1390                        cdbLenStr, (resp_mode6 ? "6" : "10"));
1391         }
1392         rsp_buff_sz -= resid;
1393         if (rsp_buff_sz < 0) {
1394             pr2serr("MS(%s) resid=%d implies negative response length "
1395                     "(%d)\n", cdbLenStr, resid, rsp_buff_sz);
1396             ret = SG_LIB_WILD_RESID;
1397             goto fini;
1398         }
1399         if (resp_mode6) {
1400             if (rsp_buff_sz < 4) {
1401                 pr2serr("MS(6) resid=%d implies abridged header length "
1402                         "(%d)\n", resid, rsp_buff_sz);
1403                 ret = SG_LIB_WILD_RESID;
1404                 goto fini;
1405             }
1406             headerlen = 4;
1407             medium_type = rsp_buff[1];
1408             specific = rsp_buff[2];
1409             longlba = false;
1410         } else {        /* MODE SENSE(10) with resid */
1411             if (rsp_buff_sz < 8) {
1412                 pr2serr("MS(10) resid=%d implies abridged header length "
1413                         "(%d)\n", resid, rsp_buff_sz);
1414                 ret = SG_LIB_WILD_RESID;
1415                 goto fini;
1416             }
1417             headerlen = 8;
1418             medium_type = rsp_buff[2];
1419             specific = rsp_buff[3];
1420             longlba = !!(rsp_buff[4] & 1);
1421         }
1422         md_len = sg_msense_calc_length(rsp_buff, rsp_buff_sz, resp_mode6,
1423                                        &bd_len);
1424         if (md_len < 0) {
1425             pr2serr("MS(%s): sg_msense_calc_length() failed\n", cdbLenStr);
1426             ret = SG_LIB_CAT_MALFORMED;
1427             goto fini;
1428         }
1429         md_len = (md_len < rsp_buff_sz) ? md_len : rsp_buff_sz;
1430         if ((bd_len + headerlen) > md_len) {
1431             pr2serr("Invalid block descriptor length=%d, ignore\n", bd_len);
1432             bd_len = 0;
1433         }
1434         if (op->do_raw || (op->do_hex > 2)) {
1435             if (1 == op->do_raw)
1436                 dStrRaw(rsp_buff, md_len);
1437             else if (op->do_raw > 1) {
1438                 bp = rsp_buff + bd_len + headerlen;
1439                 md_len -= bd_len + headerlen;
1440                 spf = !!(bp[0] & 0x40);
1441                 len = (spf ? (sg_get_unaligned_be16(bp + 2) + 4) :
1442                              (bp[1] + 2));
1443                 len = (len < md_len) ? len : md_len;
1444                 for (k = 0; k < len; ++k)
1445                     printf("%02x\n", bp[k]);
1446             } else
1447                 hex2stdout(rsp_buff, md_len, -1);
1448             goto fini;
1449         }
1450         if (1 == op->do_hex) {
1451             hex2stdout(rsp_buff, md_len, 1);
1452             goto fini;
1453         } else if (op->do_hex > 1) {
1454             hex2stdout(rsp_buff, headerlen, 1);
1455             goto fini;
1456         }
1457         if ((PDT_DISK == inq_pdt) || (PDT_ZBC == inq_pdt))
1458             printf("  Mode data length=%d, medium type=0x%.2x, WP=%d,"
1459                    " DpoFua=%d, longlba=%d\n", md_len, medium_type,
1460                    !!(specific & 0x80), !!(specific & 0x10), (int)longlba);
1461         else
1462             printf("  Mode data length=%d, medium type=0x%.2x, specific"
1463                    " param=0x%.2x, longlba=%d\n", md_len, medium_type,
1464                    specific, (int)longlba);
1465         if (md_len > rsp_buff_sz) {
1466             printf("Only fetched %d bytes of response, truncate output\n",
1467                    rsp_buff_sz);
1468             md_len = rsp_buff_sz;
1469             if (bd_len + headerlen > rsp_buff_sz)
1470                 bd_len = rsp_buff_sz - headerlen;
1471         }
1472         if (! op->do_dbout) {
1473             printf("  Block descriptor length=%d\n", bd_len);
1474             if (bd_len > 0) {
1475                 len = 8;
1476                 density_code_off = 0;
1477                 num = bd_len;
1478                 if (longlba) {
1479                     printf("> longlba direct access device block "
1480                            "descriptors:\n");
1481                     len = 16;
1482                     density_code_off = 8;
1483                 }
1484                 else if ((PDT_DISK == inq_pdt) || (PDT_ZBC == inq_pdt)) {
1485                     printf("> Direct access device block descriptors:\n");
1486                     density_code_off = 4;
1487                 }
1488                 else
1489                     printf("> General mode parameter block descriptors:\n");
1490 
1491                 bp = rsp_buff + headerlen;
1492                 while (num > 0) {
1493                     printf("   Density code=0x%x\n",
1494                            *(bp + density_code_off));
1495                     hex2stdout(bp, len, 1);
1496                     bp += len;
1497                     num -= len;
1498                 }
1499                 printf("\n");
1500             }
1501         }
1502         bp = rsp_buff + bd_len + headerlen;    /* start of mode page(s) */
1503         md_len -= bd_len + headerlen;           /* length of mode page(s) */
1504         num_ua_pages = 0;
1505         for (k = 0; md_len > 0; ++k) { /* got mode page(s) */
1506             if ((k > 0) && (! op->do_all) &&
1507                 (SPG_CODE_ALL != op->subpg_code)) {
1508                 pr2serr("Unexpectedly received extra mode page responses, "
1509                         "ignore\n");
1510                 break;
1511             }
1512             uc = *bp;
1513             spf = !!(uc & 0x40);
1514             len = (spf ? (sg_get_unaligned_be16(bp + 2) + 4) : (bp[1] + 2));
1515             page_num = bp[0] & PG_CODE_MASK;
1516             if (0x0 == page_num) {
1517                 ++num_ua_pages;
1518                 if((num_ua_pages > 3) && (md_len > 0xa00)) {
1519                     pr2serr(">>> Seen 3 unit attention pages (only one "
1520                             "should be at end)\n     and mpage length=%d, "
1521                             "looks malformed, try '-f' option\n", md_len);
1522                     break;
1523                 }
1524             }
1525             if (op->do_hex) {
1526                 if (spf)
1527                     printf(">> page_code=0x%x, subpage_code=0x%x, page_cont"
1528                            "rol=%d\n", page_num, bp[1], op->page_control);
1529                 else
1530                     printf(">> page_code=0x%x, page_control=%d\n", page_num,
1531                            op->page_control);
1532             } else {
1533                 descp = NULL;
1534                 if ((0x18 == page_num) || (0x19 == page_num)) {
1535                     t_proto = (spf ? bp[5] : bp[2]) & 0xf;
1536                     descp = find_page_code_desc(page_num, (spf ? bp[1] : 0),
1537                                                 inq_pdt, encserv, mchngr,
1538                                                 t_proto);
1539                 } else
1540                     descp = find_page_code_desc(page_num, (spf ? bp[1] : 0),
1541                                                 inq_pdt, encserv, mchngr, -1);
1542                 if (NULL == descp) {
1543                     if (spf)
1544                         snprintf(ebuff, EBUFF_SZ, "0x%x, subpage_code: 0x%x",
1545                                  page_num, bp[1]);
1546                     else
1547                         snprintf(ebuff, EBUFF_SZ, "0x%x", page_num);
1548                 }
1549                 if (descp)
1550                     printf(">> %s, page_control: %s\n", descp,
1551                            pg_control_str_arr[op->page_control]);
1552                 else
1553                     printf(">> page_code: %s, page_control: %s\n", ebuff,
1554                            pg_control_str_arr[op->page_control]);
1555             }
1556             num = (len > md_len) ? md_len : len;
1557             if ((k > 0) && (num > UNLIKELY_ABOVE_LEN)) {
1558                 num = UNLIKELY_ABOVE_LEN;
1559                 pr2serr(">>> page length (%d) > %d bytes, unlikely, trim\n"
1560                         "    Try '-f' option\n", len, num);
1561             }
1562             hex2stdout(bp, num , 1);
1563             bp += len;
1564             md_len -= len;
1565         }
1566     }
1567 
1568 fini:
1569     if (sg_fd >= 0)
1570         sg_cmds_close_device(sg_fd);
1571     if (free_rsp_buff)
1572         free(free_rsp_buff);
1573     if (0 == vb) {
1574         if (! sg_if_can2stderr("sg_modes failed: ", ret))
1575             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
1576                     "more information\n");
1577     }
1578     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
1579 }
1580