xref: /aosp_15_r20/external/sg3_utils/src/sg_rmsn.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2005-2018 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 <string.h>
17 #include <getopt.h>
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "sg_lib.h"
24 #include "sg_cmds_basic.h"
25 #include "sg_cmds_extra.h"
26 #include "sg_unaligned.h"
27 #include "sg_pr2serr.h"
28 
29 /* A utility program was originally written for the Linux OS SCSI subsystem.
30  *
31  *
32  * This program issues the SCSI command READ MEDIA SERIAL NUMBER
33  * to the given SCSI device.
34  */
35 
36 static const char * version_str = "1.18 20180628";
37 
38 #define SERIAL_NUM_SANITY_LEN (16 * 1024)
39 
40 
41 static struct option long_options[] = {
42         {"help", no_argument, 0, 'h'},
43         {"raw", no_argument, 0, 'r'},
44         {"readonly", no_argument, 0, 'R'},
45         {"verbose", no_argument, 0, 'v'},
46         {"version", no_argument, 0, 'V'},
47         {0, 0, 0, 0},
48 };
49 
usage()50 static void usage()
51 {
52     pr2serr("Usage: sg_rmsn   [--help] [--raw] [--readonly] [--verbose] "
53             "[--version]\n"
54             "                 DEVICE\n"
55             "  where:\n"
56             "    --help|-h       print out usage message\n"
57             "    --raw|-r        output serial number to stdout "
58             "(potentially binary)\n"
59             "    --readonly|-R    open DEVICE read-only (def: open it "
60             "read-write)\n"
61             "    --verbose|-v    increase verbosity\n"
62             "    --version|-V    print version string and exit\n\n"
63             "Performs a SCSI READ MEDIA SERIAL NUMBER command\n");
64 }
65 
main(int argc,char * argv[])66 int main(int argc, char * argv[])
67 {
68     bool raw = false;
69     bool readonly = false;
70     bool verbose_given = false;
71     bool version_given = false;
72     int res, c, sn_len, n;
73     int sg_fd = -1;
74     int ret = 0;
75     int verbose = 0;
76     uint8_t rmsn_buff[4];
77     uint8_t * bp = NULL;
78     const char * device_name = NULL;
79 
80     while (1) {
81         int option_index = 0;
82 
83         c = getopt_long(argc, argv, "hrRvV", long_options,
84                         &option_index);
85         if (c == -1)
86             break;
87 
88         switch (c) {
89         case 'h':
90         case '?':
91             usage();
92             return 0;
93         case 'r':
94             raw = true;
95             break;
96         case 'R':
97             readonly = true;
98             break;
99         case 'v':
100             verbose_given = true;
101             ++verbose;
102             break;
103         case 'V':
104             version_given = true;
105             break;
106         default:
107             pr2serr("unrecognised option code 0x%x ??\n", c);
108             usage();
109             return SG_LIB_SYNTAX_ERROR;
110         }
111     }
112     if (optind < argc) {
113         if (NULL == device_name) {
114             device_name = argv[optind];
115             ++optind;
116         }
117         if (optind < argc) {
118             for (; optind < argc; ++optind)
119                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
120             usage();
121             return SG_LIB_SYNTAX_ERROR;
122         }
123     }
124 #ifdef DEBUG
125     pr2serr("In DEBUG mode, ");
126     if (verbose_given && version_given) {
127         pr2serr("but override: '-vV' given, zero verbose and continue\n");
128         verbose_given = false;
129         version_given = false;
130         verbose = 0;
131     } else if (! verbose_given) {
132         pr2serr("set '-vv'\n");
133         verbose = 2;
134     } else
135         pr2serr("keep verbose=%d\n", verbose);
136 #else
137     if (verbose_given && version_given)
138         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
139 #endif
140     if (version_given) {
141         pr2serr("version: %s\n", version_str);
142         return 0;
143     }
144 
145     if (NULL == device_name) {
146         pr2serr("missing device name!\n");
147         usage();
148         return SG_LIB_SYNTAX_ERROR;
149     }
150     if (raw) {
151         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
152             perror("sg_set_binary_mode");
153             return SG_LIB_FILE_ERROR;
154         }
155     }
156 
157     sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
158     if (sg_fd < 0) {
159         if (verbose)
160             pr2serr("open error: %s: %s\n", device_name,
161                     safe_strerror(-sg_fd));
162         ret = sg_convert_errno(-sg_fd);
163         goto err_out;
164     }
165 
166     memset(rmsn_buff, 0x0, sizeof(rmsn_buff));
167 
168     res = sg_ll_read_media_serial_num(sg_fd, rmsn_buff, sizeof(rmsn_buff),
169                                       true, verbose);
170     ret = res;
171     if (0 == res) {
172         sn_len = sg_get_unaligned_be32(rmsn_buff + 0);
173         if (! raw)
174             printf("Reported serial number length = %d\n", sn_len);
175         if (0 == sn_len) {
176             pr2serr("    This implies the media has no serial number\n");
177             goto err_out;
178         }
179         if (sn_len > SERIAL_NUM_SANITY_LEN) {
180             pr2serr("    That length (%d) seems too long for a serial "
181                     "number\n", sn_len);
182             goto err_out;
183         }
184         sn_len += 4;
185         bp = (uint8_t *)malloc(sn_len);
186         if (NULL == bp) {
187             pr2serr("    Out of memory (ram)\n");
188             goto err_out;
189         }
190         res = sg_ll_read_media_serial_num(sg_fd, bp, sn_len, true, verbose);
191         if (0 == res) {
192             sn_len = sg_get_unaligned_be32(bp + 0);
193             if (raw) {
194                 if (sn_len > 0) {
195                     n = fwrite(bp + 4, 1, sn_len, stdout);
196                     if (n) { ; }  /* unused, dummy to suppress warning */
197                 }
198             } else {
199                 printf("Serial number:\n");
200                 if (sn_len > 0)
201                     hex2stdout(bp + 4, sn_len, 0);
202             }
203         }
204     }
205     if (res) {
206         char b[80];
207 
208         sg_get_category_sense_str(res, sizeof(b), b, verbose);
209         pr2serr("Read Media Serial Number: %s\n", b);
210         if (0 == verbose)
211             pr2serr("    try '-v' for more information\n");
212     }
213 
214 err_out:
215     if (bp)
216         free(bp);
217     if (sg_fd >= 0) {
218         res = sg_cmds_close_device(sg_fd);
219         if (res < 0) {
220             pr2serr("close error: %s\n", safe_strerror(-res));
221             if (0 == ret)
222                 ret = sg_convert_errno(-res);
223         }
224     }
225     if (0 == verbose) {
226         if (! sg_if_can2stderr("sg_rmsn failed: ", ret))
227             pr2serr("Some error occurred, try again with '-v' "
228                     "or '-vv' for more information\n");
229     }
230     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
231 }
232