1 /*
2 * Utility program for the Linux OS SCSI generic ("sg") device driver.
3 * Copyright (C) 2000-2017 D. Gilbert
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10
11 This shows the mapping from "sg" devices to other scsi devices
12 (i.e. sd, scd or st) if any.
13
14 Note: This program requires sg version 2 or better.
15
16 Version 0.19 20041203
17
18 Version 1.02 20050511
19 - allow for sparse disk name with up to 3 letter SCSI
20 disk device node names (e.g. /dev/sdaaa)
21 [Nate Dailey < Nate dot Dailey at stratus dot com >]
22 */
23
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE 1
26 #endif
27
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <stdbool.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <dirent.h>
37 #include <libgen.h>
38 #include <sys/ioctl.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45 #include "sg_lib.h"
46 #include "sg_cmds_basic.h"
47 #include "sg_io_linux.h"
48
49
50 static const char * version_str = "1.12 20171010";
51
52 static const char * devfs_id = "/dev/.devfsd";
53
54 #define NUMERIC_SCAN_DEF true /* set to false to make alpha scan default */
55
56 #define INQUIRY_RESP_INITIAL_LEN 36
57 #define MAX_SG_DEVS 4096
58 #define PRESENT_ARRAY_SIZE MAX_SG_DEVS
59
60 static const char * sysfs_sg_dir = "/sys/class/scsi_generic";
61 static char gen_index_arr[PRESENT_ARRAY_SIZE];
62 static int has_sysfs_sg = 0;
63
64
65 typedef struct my_map_info
66 {
67 int active;
68 int lin_dev_type;
69 int oth_dev_num;
70 struct sg_scsi_id sg_dat;
71 char vendor[8];
72 char product[16];
73 char revision[4];
74 } my_map_info_t;
75
76
77 #define MAX_SD_DEVS (26 + 26*26 + 26*26*26) /* sdX, sdXX, sdXXX */
78 /* (26 + 676 + 17576) = 18278 */
79 #define MAX_SR_DEVS 128
80 #define MAX_ST_DEVS 128
81 #define MAX_OSST_DEVS 128
82 #define MAX_ERRORS 5
83
84 static my_map_info_t map_arr[MAX_SG_DEVS];
85
86 #define LIN_DEV_TYPE_UNKNOWN 0
87 #define LIN_DEV_TYPE_SD 1
88 #define LIN_DEV_TYPE_SR 2
89 #define LIN_DEV_TYPE_ST 3
90 #define LIN_DEV_TYPE_SCD 4
91 #define LIN_DEV_TYPE_OSST 5
92
93
94 typedef struct my_scsi_idlun {
95 /* why can't userland see this structure ??? */
96 int dev_id;
97 int host_unique_id;
98 } My_scsi_idlun;
99
100
101 #define EBUFF_SZ 256
102 static char ebuff[EBUFF_SZ];
103
104 static void scan_dev_type(const char * leadin, int max_dev, bool do_numeric,
105 int lin_dev_type, int last_sg_ind);
106
usage()107 static void usage()
108 {
109 printf("Usage: sg_map [-a] [-h] [-i] [-n] [-sd] [-scd or -sr] [-st] "
110 "[-V] [-x]\n");
111 printf(" where:\n");
112 printf(" -a do alphabetic scan (ie sga, sgb, sgc)\n");
113 printf(" -h or -? show this usage message then exit\n");
114 printf(" -i also show device INQUIRY strings\n");
115 printf(" -n do numeric scan (i.e. sg0, sg1, sg2) "
116 "(default)\n");
117 printf(" -sd show mapping to disks\n");
118 printf(" -scd show mapping to cdroms (look for /dev/scd<n>\n");
119 printf(" -sr show mapping to cdroms (look for /dev/sr<n>\n");
120 printf(" -st show mapping to tapes (st and osst devices)\n");
121 printf(" -V print version string then exit\n");
122 printf(" -x also show bus,chan,id,lun and type\n\n");
123 printf("If no '-s*' arguments given then show all mappings. This "
124 "utility\nis DEPRECATED, do not use in Linux 2.6 series or "
125 "later.\n");
126 }
127
scandir_select(const struct dirent * s)128 static int scandir_select(const struct dirent * s)
129 {
130 int k;
131
132 if (1 == sscanf(s->d_name, "sg%d", &k)) {
133 if ((k >= 0) && (k < PRESENT_ARRAY_SIZE)) {
134 gen_index_arr[k] = 1;
135 return 1;
136 }
137 }
138 return 0;
139 }
140
sysfs_sg_scan(const char * dir_name)141 static int sysfs_sg_scan(const char * dir_name)
142 {
143 struct dirent ** namelist;
144 int num, k;
145
146 num = scandir(dir_name, &namelist, scandir_select, NULL);
147 if (num < 0)
148 return -errno;
149 for (k = 0; k < num; ++k)
150 free(namelist[k]);
151 free(namelist);
152 return num;
153 }
154
make_dev_name(char * fname,const char * leadin,int k,bool do_numeric)155 static void make_dev_name(char * fname, const char * leadin, int k,
156 bool do_numeric)
157 {
158 char buff[64];
159 int ones,tens,hundreds; /* for lack of a better name */
160 int buff_idx;
161
162 strcpy(fname, leadin ? leadin : "/dev/sg");
163 if (do_numeric) {
164 sprintf(buff, "%d", k);
165 strcat(fname, buff);
166 }
167 else if (k >= (26 + 26*26 + 26*26*26)) {
168 strcat(fname, "xxxx");
169 }
170 else {
171 ones = k % 26;
172
173 if ((k - 26) >= 0)
174 tens = ((k-26)/26) % 26;
175 else tens = -1;
176
177 if ((k - (26 + 26*26)) >= 0)
178 hundreds = ((k - (26 + 26*26))/(26*26)) % 26;
179 else hundreds = -1;
180
181 buff_idx = 0;
182 if (hundreds >= 0) buff[buff_idx++] = 'a' + (char)hundreds;
183 if (tens >= 0) buff[buff_idx++] = 'a' + (char)tens;
184 buff[buff_idx++] = 'a' + (char)ones;
185 buff[buff_idx] = '\0';
186 strcat(fname, buff);
187 }
188 }
189
190
main(int argc,char * argv[])191 int main(int argc, char * argv[])
192 {
193 bool do_all_s = true;
194 bool do_extra = false;
195 bool do_inquiry = false;
196 bool do_numeric = NUMERIC_SCAN_DEF;
197 bool do_osst = false;
198 bool do_scd = false;
199 bool do_sd = false;
200 bool do_sr = false;
201 bool do_st = false;
202 bool eacces_err = false;
203 int sg_fd, res, k;
204 int num_errors = 0;
205 int num_silent = 0;
206 int last_sg_ind = -1;
207 char fname[64];
208 struct stat a_stat;
209
210 for (k = 1; k < argc; ++k) {
211 if (0 == strcmp("-n", argv[k]))
212 do_numeric = true;
213 else if (0 == strcmp("-a", argv[k]))
214 do_numeric = false;
215 else if (0 == strcmp("-x", argv[k]))
216 do_extra = true;
217 else if (0 == strcmp("-i", argv[k]))
218 do_inquiry = true;
219 else if (0 == strcmp("-sd", argv[k])) {
220 do_sd = true;
221 do_all_s = false;
222 } else if (0 == strcmp("-st", argv[k])) {
223 do_st = true;
224 do_osst = true;
225 do_all_s = false;
226 } else if (0 == strcmp("-sr", argv[k])) {
227 do_sr = true;
228 do_all_s = false;
229 } else if (0 == strcmp("-scd", argv[k])) {
230 do_scd = true;
231 do_all_s = false;
232 } else if (0 == strcmp("-V", argv[k])) {
233 fprintf(stderr, "Version string: %s\n", version_str);
234 exit(0);
235 } else if ((0 == strcmp("-?", argv[k])) ||
236 (0 == strncmp("-h", argv[k], 2))) {
237 printf(
238 "Show mapping from sg devices to other scsi device names\n\n");
239 usage();
240 return SG_LIB_SYNTAX_ERROR;
241 } else if (*argv[k] == '-') {
242 printf("Unknown switch: %s\n", argv[k]);
243 usage();
244 return SG_LIB_SYNTAX_ERROR;
245 } else if (*argv[k] != '-') {
246 printf("Unknown argument\n");
247 usage();
248 return SG_LIB_SYNTAX_ERROR;
249 }
250 }
251
252 if ((stat(sysfs_sg_dir, &a_stat) >= 0) && (S_ISDIR(a_stat.st_mode)))
253 has_sysfs_sg = sysfs_sg_scan(sysfs_sg_dir);
254
255 if (stat(devfs_id, &a_stat) == 0)
256 printf("# Note: the devfs pseudo file system is present\n");
257
258 for (k = 0, res = 0; (k < MAX_SG_DEVS) && (num_errors < MAX_ERRORS);
259 ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
260 if (res < 0) {
261 snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
262 perror("sg_map: close error");
263 return SG_LIB_FILE_ERROR;
264 }
265 if (has_sysfs_sg) {
266 if (0 == gen_index_arr[k]) {
267 sg_fd = -1;
268 continue;
269 }
270 make_dev_name(fname, "/dev/sg", k, true);
271 } else
272 make_dev_name(fname, "/dev/sg", k, do_numeric);
273
274 sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
275 if (sg_fd < 0) {
276 if (EBUSY == errno) {
277 map_arr[k].active = -2;
278 continue;
279 }
280 else if ((ENODEV == errno) || (ENOENT == errno) ||
281 (ENXIO == errno)) {
282 ++num_errors;
283 ++num_silent;
284 map_arr[k].active = -1;
285 continue;
286 }
287 else {
288 if (EACCES == errno)
289 eacces_err = true;
290 snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname);
291 perror(ebuff);
292 ++num_errors;
293 continue;
294 }
295 }
296 res = ioctl(sg_fd, SG_GET_SCSI_ID, &map_arr[k].sg_dat);
297 if (res < 0) {
298 snprintf(ebuff, EBUFF_SZ,
299 "device %s failed on sg ioctl, skip", fname);
300 perror(ebuff);
301 ++num_errors;
302 continue;
303 }
304 if (do_inquiry) {
305 char buff[INQUIRY_RESP_INITIAL_LEN];
306
307 if (0 == sg_ll_inquiry(sg_fd, false, false, 0, buff, sizeof(buff),
308 true, 0)) {
309 memcpy(map_arr[k].vendor, &buff[8], 8);
310 memcpy(map_arr[k].product, &buff[16], 16);
311 memcpy(map_arr[k].revision, &buff[32], 4);
312 }
313 }
314 map_arr[k].active = 1;
315 map_arr[k].oth_dev_num = -1;
316 last_sg_ind = k;
317 }
318 if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) {
319 printf("Stopping because there are too many error\n");
320 if (eacces_err)
321 printf(" root access may be required\n");
322 return SG_LIB_FILE_ERROR;
323 }
324 if (last_sg_ind < 0) {
325 printf("Stopping because no sg devices found\n");
326 }
327
328 if (do_all_s || do_sd)
329 scan_dev_type("/dev/sd", MAX_SD_DEVS, 0, LIN_DEV_TYPE_SD, last_sg_ind);
330 if (do_all_s || do_sr)
331 scan_dev_type("/dev/sr", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SR, last_sg_ind);
332 if (do_all_s || do_scd)
333 scan_dev_type("/dev/scd", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SCD,
334 last_sg_ind);
335 if (do_all_s || do_st)
336 scan_dev_type("/dev/nst", MAX_ST_DEVS, 1, LIN_DEV_TYPE_ST,
337 last_sg_ind);
338 if (do_all_s || do_osst)
339 scan_dev_type("/dev/osst", MAX_OSST_DEVS, 1, LIN_DEV_TYPE_OSST,
340 last_sg_ind);
341
342 for (k = 0; k <= last_sg_ind; ++k) {
343 if (has_sysfs_sg) {
344 if (0 == gen_index_arr[k]) {
345 continue;
346 }
347 make_dev_name(fname, "/dev/sg", k, true);
348 } else
349 make_dev_name(fname, "/dev/sg", k, do_numeric);
350 printf("%s", fname);
351 switch (map_arr[k].active)
352 {
353 case -2:
354 printf(do_extra ? " -2 -2 -2 -2 -2" : " busy");
355 break;
356 case -1:
357 printf(do_extra ? " -1 -1 -1 -1 -1" : " not present");
358 break;
359 case 0:
360 printf(do_extra ? " -3 -3 -3 -3 -3" : " some error");
361 break;
362 case 1:
363 if (do_extra)
364 printf(" %d %d %d %d %d", map_arr[k].sg_dat.host_no,
365 map_arr[k].sg_dat.channel, map_arr[k].sg_dat.scsi_id,
366 map_arr[k].sg_dat.lun, map_arr[k].sg_dat.scsi_type);
367 switch (map_arr[k].lin_dev_type)
368 {
369 case LIN_DEV_TYPE_SD:
370 make_dev_name(fname, "/dev/sd" , map_arr[k].oth_dev_num, 0);
371 printf(" %s", fname);
372 break;
373 case LIN_DEV_TYPE_ST:
374 make_dev_name(fname, "/dev/nst" , map_arr[k].oth_dev_num, 1);
375 printf(" %s", fname);
376 break;
377 case LIN_DEV_TYPE_OSST:
378 make_dev_name(fname, "/dev/osst" , map_arr[k].oth_dev_num, 1);
379 printf(" %s", fname);
380 break;
381 case LIN_DEV_TYPE_SR:
382 make_dev_name(fname, "/dev/sr" , map_arr[k].oth_dev_num, 1);
383 printf(" %s", fname);
384 break;
385 case LIN_DEV_TYPE_SCD:
386 make_dev_name(fname, "/dev/scd" , map_arr[k].oth_dev_num, 1);
387 printf(" %s", fname);
388 break;
389 default:
390 break;
391 }
392 if (do_inquiry)
393 printf(" %.8s %.16s %.4s", map_arr[k].vendor,
394 map_arr[k].product, map_arr[k].revision);
395 break;
396 default:
397 printf(" bad logic\n");
398 break;
399 }
400 printf("\n");
401 }
402 return 0;
403 }
404
find_dev_in_sg_arr(My_scsi_idlun * my_idlun,int host_no,int last_sg_ind)405 static int find_dev_in_sg_arr(My_scsi_idlun * my_idlun, int host_no,
406 int last_sg_ind)
407 {
408 int k;
409 struct sg_scsi_id * sidp;
410
411 for (k = 0; k <= last_sg_ind; ++k) {
412 sidp = &(map_arr[k].sg_dat);
413 if ((host_no == sidp->host_no) &&
414 ((my_idlun->dev_id & 0xff) == sidp->scsi_id) &&
415 (((my_idlun->dev_id >> 8) & 0xff) == sidp->lun) &&
416 (((my_idlun->dev_id >> 16) & 0xff) == sidp->channel))
417 return k;
418 }
419 return -1;
420 }
421
scan_dev_type(const char * leadin,int max_dev,bool do_numeric,int lin_dev_type,int last_sg_ind)422 static void scan_dev_type(const char * leadin, int max_dev, bool do_numeric,
423 int lin_dev_type, int last_sg_ind)
424 {
425 int k, res, ind, sg_fd = 0;
426 int num_errors = 0;
427 int num_silent = 0;
428 int host_no = -1;
429 My_scsi_idlun my_idlun;
430 char fname[64];
431
432 for (k = 0, res = 0; (k < max_dev) && (num_errors < MAX_ERRORS);
433 ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
434
435 /* ignore close() errors */
436 #if 0
437 if (res < 0) {
438 snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
439 perror("sg_map: close error");
440 #ifndef IGN_CLOSE_ERR
441 return;
442 #else
443 ++num_errors;
444 sg_fd = 0;
445 #endif
446 }
447 #endif
448 make_dev_name(fname, leadin, k, do_numeric);
449 #ifdef DEBUG
450 printf ("Trying %s: ", fname);
451 #endif
452
453 sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
454 if (sg_fd < 0) {
455 #ifdef DEBUG
456 printf ("ERROR %i\n", errno);
457 #endif
458 if (EBUSY == errno) {
459 printf("Device %s is busy\n", fname);
460 ++num_errors;
461 } else if ((ENODEV == errno) || (ENXIO == errno)) {
462 ++num_errors;
463 ++num_silent;
464 } else if (ENOENT != errno) { /* ignore ENOENT for sparse names */
465 snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname);
466 perror(ebuff);
467 ++num_errors;
468 }
469 continue;
470 }
471
472 res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
473 if (res < 0) {
474 snprintf(ebuff, EBUFF_SZ,
475 "device %s failed on scsi ioctl(idlun), skip", fname);
476 perror(ebuff);
477 ++num_errors;
478 #ifdef DEBUG
479 printf ("Couldn't get IDLUN!\n");
480 #endif
481 continue;
482 }
483 res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
484 if (res < 0) {
485 snprintf(ebuff, EBUFF_SZ,
486 "device %s failed on scsi ioctl(bus_number), skip", fname);
487 perror(ebuff);
488 ++num_errors;
489 #ifdef DEBUG
490 printf ("Couldn't get BUS!\n");
491 #endif
492 continue;
493 }
494 #ifdef DEBUG
495 printf ("%i(%x) %i %i %i %i\n", host_no, my_idlun.host_unique_id,
496 (my_idlun.dev_id>>24)&0xff, (my_idlun.dev_id>>16)&0xff,
497 (my_idlun.dev_id>>8)&0xff, my_idlun.dev_id&0xff);
498 #endif
499 ind = find_dev_in_sg_arr(&my_idlun, host_no, last_sg_ind);
500 if (ind >= 0) {
501 map_arr[ind].oth_dev_num = k;
502 map_arr[ind].lin_dev_type = lin_dev_type;
503 }
504 else
505 printf("Strange, could not find device %s mapped to sg device??\n",
506 fname);
507 }
508 }
509