• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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