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