1 /* A utility program originally written for the Linux OS SCSI subsystem.
2 * Copyright (C) 1999 - 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 scans the "sg" device space (ie actual + simulated SCSI
11 * generic devices). Optionally sg_scan can be given other device names
12 * to scan (in place of the sg devices).
13 * Options: -a alpha scan: scan /dev/sga,b,c, ....
14 * -i do SCSI inquiry on device (implies -w)
15 * -n numeric scan: scan /dev/sg0,1,2, ....
16 * -V output version string and exit
17 * -w open writable (new driver opens readable unless -i)
18 * -x extra information output
19 *
20 * By default this program will look for /dev/sg0 first (i.e. numeric scan)
21 *
22 * Note: This program is written to work under both the original and
23 * the new sg driver.
24 *
25 * F. Jansen - modification to extend beyond 26 sg devices.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32
33 #ifndef _GNU_SOURCE
34 #define _GNU_SOURCE 1
35 #endif
36
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <dirent.h>
45 #include <libgen.h>
46 #include <sys/ioctl.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <scsi/scsi_ioctl.h>
50
51 #include "sg_lib.h"
52 #include "sg_io_linux.h"
53 #include "sg_pr2serr.h"
54
55
56 static const char * version_str = "4.18 20220118";
57
58 #define ME "sg_scan: "
59
60 #define NUMERIC_SCAN_DEF true /* change to false to make alpha scan default */
61
62 #define INQ_REPLY_LEN 36
63 #define INQ_CMD_LEN 6
64 #define MAX_ERRORS 4
65
66 #define EBUFF_SZ 256
67 #define FNAME_SZ 64
68 #define PRESENT_ARRAY_SIZE 8192
69
70 static const char * sysfs_sg_dir = "/sys/class/scsi_generic";
71 static int * gen_index_arr;
72
73 typedef struct my_scsi_idlun {
74 /* why can't userland see this structure ??? */
75 int dev_id;
76 int host_unique_id;
77 } My_scsi_idlun;
78
79 typedef struct my_sg_scsi_id {
80 int host_no; /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */
81 int channel;
82 int scsi_id; /* scsi id of target device */
83 int lun;
84 int scsi_type; /* TYPE_... defined in scsi/scsi.h */
85 short h_cmd_per_lun;/* host (adapter) maximum commands per lun */
86 short d_queue_depth;/* device (or adapter) maximum queue length */
87 int unused1; /* probably find a good use, set 0 for now */
88 int unused2; /* ditto */
89 } My_sg_scsi_id;
90
91 int sg3_inq(int sg_fd, uint8_t * inqBuff, bool do_extra);
92 int scsi_inq(int sg_fd, uint8_t * inqBuff);
93 int try_ata_identity(const char * file_namep, int ata_fd, bool do_inq);
94
95 static uint8_t inq_cdb[INQ_CMD_LEN] =
96 {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
97
98
usage()99 void usage()
100 {
101 printf("Usage: sg_scan [-a] [-i] [-n] [-v] [-V] [-w] [-x] "
102 "[DEVICE]*\n");
103 printf(" where:\n");
104 printf(" -a do alpha scan (ie sga, sgb, sgc)\n");
105 printf(" -i do SCSI INQUIRY, output results\n");
106 printf(" -n do numeric scan (ie sg0, sg1...) [default]\n");
107 printf(" -v increase verbosity\n");
108 printf(" -V output version string then exit\n");
109 printf(" -w force open with read/write flag\n");
110 printf(" -x extra information output about queuing\n");
111 printf(" DEVICE name of device\n");
112 }
113
scandir_select(const struct dirent * s)114 static int scandir_select(const struct dirent * s)
115 {
116 int k;
117
118 if (1 == sscanf(s->d_name, "sg%d", &k)) {
119 if ((k >= 0) && (k < PRESENT_ARRAY_SIZE)) {
120 gen_index_arr[k] = 1;
121 return 1;
122 }
123 }
124 return 0;
125 }
126
sysfs_sg_scan(const char * dir_name)127 static int sysfs_sg_scan(const char * dir_name)
128 {
129 struct dirent ** namelist;
130 int num, k;
131
132 num = scandir(dir_name, &namelist, scandir_select, NULL);
133 if (num < 0)
134 return -errno;
135 for (k = 0; k < num; ++k)
136 free(namelist[k]);
137 free(namelist);
138 return num;
139 }
140
make_dev_name(char * fname,int k,bool do_numeric)141 void make_dev_name(char * fname, int k, bool do_numeric)
142 {
143 char buff[FNAME_SZ];
144 int big,little;
145
146 strcpy(fname, "/dev/sg");
147 if (do_numeric) {
148 snprintf(buff, sizeof(buff), "%d", k);
149 strcat(fname, buff);
150 }
151 else {
152 if (k < 26) {
153 buff[0] = 'a' + (char)k;
154 buff[1] = '\0';
155 strcat(fname, buff);
156 }
157 else if (k <= 255) { /* assumes sequence goes x,y,z,aa,ab,ac etc */
158 big = k/26;
159 little = k - (26 * big);
160 big = big - 1;
161
162 buff[0] = 'a' + (char)big;
163 buff[1] = 'a' + (char)little;
164 buff[2] = '\0';
165 strcat(fname, buff);
166 }
167 else
168 strcat(fname, "xxxx");
169 }
170 }
171
172
main(int argc,char * argv[])173 int main(int argc, char * argv[])
174 {
175 bool do_extra = false;
176 bool do_inquiry = false;
177 bool do_numeric = NUMERIC_SCAN_DEF;
178 bool eacces_err = false;
179 bool has_file_args = false;
180 bool has_sysfs_sg = false;
181 bool jmp_out;
182 bool sg_ver3 = false;
183 bool sg_ver3_set = false;
184 bool writeable = false;
185 int sg_fd, res, k, j, f, plen;
186 int emul = -1;
187 int flags;
188 int host_no;
189 const int max_file_args = PRESENT_ARRAY_SIZE;
190 int num_errors = 0;
191 int num_silent = 0;
192 int verbose = 0;
193 char * file_namep;
194 const char * cp;
195 char fname[FNAME_SZ];
196 char ebuff[EBUFF_SZ];
197 uint8_t inqBuff[INQ_REPLY_LEN];
198 My_scsi_idlun my_idlun;
199 struct stat a_stat;
200
201 if (NULL == (gen_index_arr =
202 (int *)calloc(max_file_args + 1, sizeof(int)))) {
203 printf(ME "Out of memory\n");
204 return SG_LIB_CAT_OTHER;
205 }
206 strcpy(fname, "<null>");
207
208 for (k = 1, j = 0; k < argc; ++k) {
209 cp = argv[k];
210 plen = strlen(cp);
211 if (plen <= 0)
212 continue;
213 if ('-' == *cp) {
214 for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
215 switch (*cp) {
216 case 'a':
217 do_numeric = false;
218 break;
219 case 'h':
220 case '?':
221 printf("Scan sg device names and optionally do an "
222 "INQUIRY\n\n");
223 usage();
224 return 0;
225 case 'i':
226 do_inquiry = true;
227 break;
228 case 'n':
229 do_numeric = true;
230 break;
231 case 'v':
232 ++verbose;
233 break;
234 case 'V':
235 pr2serr("Version string: %s\n", version_str);
236 exit(0);
237 case 'w':
238 writeable = true;
239 break;
240 case 'x':
241 do_extra = true;
242 break;
243 default:
244 jmp_out = true;
245 break;
246 }
247 if (jmp_out)
248 break;
249 }
250 if (plen <= 0)
251 continue;
252 if (jmp_out) {
253 pr2serr("Unrecognized option: %s\n", cp);
254 usage();
255 return SG_LIB_SYNTAX_ERROR;
256 }
257 } else {
258 if (j < max_file_args) {
259 has_file_args = true;
260 gen_index_arr[j++] = k;
261 } else {
262 printf("Too many command line arguments\n");
263 return SG_LIB_SYNTAX_ERROR;
264 }
265 }
266 }
267
268 if ((! has_file_args) && (stat(sysfs_sg_dir, &a_stat) >= 0) &&
269 (S_ISDIR(a_stat.st_mode)))
270 has_sysfs_sg = !! sysfs_sg_scan(sysfs_sg_dir);
271
272 flags = O_NONBLOCK | (writeable ? O_RDWR : O_RDONLY);
273
274 for (k = 0, res = 0, j = 0, sg_fd = -1;
275 (k < max_file_args) && (has_file_args || (num_errors < MAX_ERRORS));
276 ++k, res = ((sg_fd >= 0) ? close(sg_fd) : 0)) {
277 if (res < 0) {
278 snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ", fname);
279 perror(ebuff);
280 return SG_LIB_FILE_ERROR;
281 }
282 if (has_file_args) {
283 if (gen_index_arr[j])
284 file_namep = argv[gen_index_arr[j++]];
285 else
286 break;
287 } else if (has_sysfs_sg) {
288 if (0 == gen_index_arr[k]) {
289 sg_fd = -1;
290 continue;
291 }
292 make_dev_name(fname, k, 1);
293 file_namep = fname;
294 } else {
295 make_dev_name(fname, k, do_numeric);
296 file_namep = fname;
297 }
298
299 sg_fd = open(file_namep, flags);
300 if (sg_fd < 0) {
301 if (EBUSY == errno) {
302 printf("%s: device busy (O_EXCL lock), skipping\n",
303 file_namep);
304 continue;
305 }
306 else if ((ENODEV == errno) || (ENOENT == errno) ||
307 (ENXIO == errno)) {
308 if (verbose)
309 pr2serr("Unable to open: %s, errno=%d\n", file_namep,
310 errno);
311 ++num_errors;
312 ++num_silent;
313 continue;
314 }
315 else {
316 if (EACCES == errno)
317 eacces_err = true;
318 snprintf(ebuff, EBUFF_SZ, ME "Error opening %s ", file_namep);
319 perror(ebuff);
320 ++num_errors;
321 continue;
322 }
323 }
324 res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
325 if (res < 0) {
326 res = try_ata_identity(file_namep, sg_fd, do_inquiry);
327 if (res == 0)
328 continue;
329 snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi+ata "
330 "ioctl, skip", file_namep);
331 perror(ebuff);
332 ++num_errors;
333 continue;
334 }
335 res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
336 if (res < 0) {
337 snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi "
338 "ioctl(2), skip", file_namep);
339 perror(ebuff);
340 ++num_errors;
341 continue;
342 }
343 res = ioctl(sg_fd, SG_EMULATED_HOST, &emul);
344 if (res < 0)
345 emul = -1;
346 printf("%s: scsi%d channel=%d id=%d lun=%d", file_namep, host_no,
347 (my_idlun.dev_id >> 16) & 0xff, my_idlun.dev_id & 0xff,
348 (my_idlun.dev_id >> 8) & 0xff);
349 if (1 == emul)
350 printf(" [em]");
351 #if 0
352 printf(", huid=%d", my_idlun.host_unique_id);
353 #endif
354 if (! has_file_args) {
355 My_sg_scsi_id m_id; /* compatible with sg_scsi_id_t in sg.h */
356
357 res = ioctl(sg_fd, SG_GET_SCSI_ID, &m_id);
358 if (res < 0) {
359 snprintf(ebuff, EBUFF_SZ, ME "device %s failed "
360 "SG_GET_SCSI_ID ioctl(4), skip", file_namep);
361 perror(ebuff);
362 ++num_errors;
363 continue;
364 }
365 /* printf(" type=%d", m_id.scsi_type); */
366 if (do_extra)
367 printf(" cmd_per_lun=%hd queue_depth=%hd\n",
368 m_id.h_cmd_per_lun, m_id.d_queue_depth);
369 else
370 printf("\n");
371 }
372 else
373 printf("\n");
374 if (do_inquiry) {
375 if (! sg_ver3_set) {
376 sg_ver3 = false;
377 sg_ver3_set = true;
378 if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &f) >= 0) &&
379 (f >= 30000))
380 sg_ver3 = true;
381 }
382 if (sg_ver3) {
383 res = sg3_inq(sg_fd, inqBuff, do_extra);
384 if (res)
385 ++num_errors;
386 }
387 }
388 }
389 if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors) &&
390 (! has_file_args)) {
391 printf("Stopping because there are too many error\n");
392 if (eacces_err)
393 printf(" root access may be required\n");
394 }
395 return 0;
396 }
397
sg3_inq(int sg_fd,uint8_t * inqBuff,bool do_extra)398 int sg3_inq(int sg_fd, uint8_t * inqBuff, bool do_extra)
399 {
400 bool ok;
401 int err, sg_io;
402 uint8_t sense_buffer[32] SG_C_CPP_ZERO_INIT;
403 struct sg_io_hdr io_hdr SG_C_CPP_ZERO_INIT;
404
405 memset(inqBuff, 0, INQ_REPLY_LEN);
406 inqBuff[0] = 0x7f;
407 io_hdr.interface_id = 'S';
408 io_hdr.cmd_len = sizeof(inq_cdb);
409 io_hdr.mx_sb_len = sizeof(sense_buffer);
410 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
411 io_hdr.dxfer_len = INQ_REPLY_LEN;
412 io_hdr.dxferp = inqBuff;
413 io_hdr.cmdp = inq_cdb;
414 io_hdr.sbp = sense_buffer;
415 io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
416
417 ok = true;
418 sg_io = 0;
419 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
420 if ((err = scsi_inq(sg_fd, inqBuff)) < 0) {
421 perror(ME "Inquiry SG_IO + SCSI_IOCTL_SEND_COMMAND ioctl error");
422 return 1;
423 } else if (err) {
424 printf(ME "SCSI_IOCTL_SEND_COMMAND ioctl error=0x%x\n", err);
425 return 1;
426 }
427 } else {
428 sg_io = 1;
429 /* now for the error processing */
430 switch (sg_err_category3(&io_hdr)) {
431 case SG_LIB_CAT_RECOVERED:
432 sg_chk_n_print3("Inquiry, continuing", &io_hdr, true);
433 #if defined(__GNUC__)
434 #if (__GNUC__ >= 7)
435 __attribute__((fallthrough));
436 /* FALL THROUGH */
437 #endif
438 #endif
439 case SG_LIB_CAT_CLEAN:
440 break;
441 default: /* won't bother decoding other categories */
442 ok = false;
443 sg_chk_n_print3("INQUIRY command error", &io_hdr, true);
444 break;
445 }
446 }
447
448 if (ok) { /* output result if it is available */
449 char * p = (char *)inqBuff;
450
451 printf(" %.8s %.16s %.4s ", p + 8, p + 16, p + 32);
452 printf("[rmb=%d cmdq=%d pqual=%d pdev=0x%x] ",
453 !!(p[1] & 0x80), !!(p[7] & 2), (p[0] & 0xe0) >> 5,
454 (p[0] & PDT_MASK));
455 if (do_extra && sg_io)
456 printf("dur=%ums\n", io_hdr.duration);
457 else
458 printf("\n");
459 }
460 return 0;
461 }
462
463 struct lscsi_ioctl_command {
464 unsigned int inlen; /* _excluding_ scsi command length */
465 unsigned int outlen;
466 uint8_t data[1]; /* was 0 but that's not ISO C!! */
467 /* on input, scsi command starts here then opt. data */
468 };
469
470 /* fallback INQUIRY using scsi mid-level's SCSI_IOCTL_SEND_COMMAND ioctl */
scsi_inq(int sg_fd,uint8_t * inqBuff)471 int scsi_inq(int sg_fd, uint8_t * inqBuff)
472 {
473 int res;
474 uint8_t buff[1024];
475 struct lscsi_ioctl_command * sicp = (struct lscsi_ioctl_command *)buff;
476
477 memset(buff, 0, sizeof(buff));
478 sicp->inlen = 0;
479 sicp->outlen = INQ_REPLY_LEN;
480 memcpy(sicp->data, inq_cdb, INQ_CMD_LEN);
481 res = ioctl(sg_fd, SCSI_IOCTL_SEND_COMMAND, sicp);
482 if (0 == res)
483 memcpy(inqBuff, sicp->data, INQ_REPLY_LEN);
484 return res;
485 }
486
487 /* Following code permits ATA IDENTIFY commands to be performed on
488 ATA non "Packet Interface" devices (e.g. ATA disks).
489 GPL-ed code borrowed from smartmontools (smartmontools.sf.net).
490 Copyright (C) 2002-4 Bruce Allen
491 <[email protected]>
492 */
493 #ifndef ATA_IDENTIFY_DEVICE
494 #define ATA_IDENTIFY_DEVICE 0xec
495 #endif
496 #ifndef HDIO_DRIVE_CMD
497 #define HDIO_DRIVE_CMD 0x031f
498 #endif
499
500 /* Needed parts of the ATA DRIVE IDENTIFY Structure. Those labeled
501 * word* are NOT used.
502 */
503 struct ata_identify_device {
504 unsigned short words000_009[10];
505 uint8_t serial_no[20];
506 unsigned short words020_022[3];
507 uint8_t fw_rev[8];
508 uint8_t model[40];
509 unsigned short words047_079[33];
510 unsigned short major_rev_num;
511 unsigned short minor_rev_num;
512 unsigned short command_set_1;
513 unsigned short command_set_2;
514 unsigned short command_set_extension;
515 unsigned short cfs_enable_1;
516 unsigned short word086;
517 unsigned short csf_default;
518 unsigned short words088_255[168];
519 };
520
521 /* Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents
522 * bytes.
523 */
swapbytes(char * out,const char * in,size_t n)524 void swapbytes(char *out, const char *in, size_t n)
525 {
526 size_t k;
527
528 if (n > 1) {
529 for (k = 0; k < (n - 1); k += 2) {
530 out[k] = in[k + 1];
531 out[k + 1] = in[k];
532 }
533 }
534 }
535
536 /* Copies in to out, but removes leading and trailing whitespace. */
trim(char * out,const char * in)537 void trim(char *out, const char *in)
538 {
539 int k, first, last, num;
540
541 /* Find the first non-space character (maybe none). */
542 first = -1;
543 for (k = 0; in[k]; k++) {
544 if (! isspace((int)in[k])) {
545 first = k;
546 break;
547 }
548 }
549
550 if (first == -1) {
551 /* There are no non-space characters. */
552 out[0] = '\0';
553 return;
554 }
555
556 /* Find the last non-space character. */
557 for (k = strlen(in) - 1; k >= first && isspace((int)in[k]); k--)
558 ;
559 last = k;
560 num = last - first + 1;
561 for (k = 0; k < num; ++k)
562 out[k] = in[first + k];
563 out[num] = '\0';
564 }
565
566 /* Convenience function for formatting strings from ata_identify_device */
formatdriveidstring(char * out,const char * in,int n)567 void formatdriveidstring(char *out, const char *in, int n)
568 {
569 char tmp[65];
570
571 n = n > 64 ? 64 : n;
572 swapbytes(tmp, in, n);
573 tmp[n] = '\0';
574 trim(out, tmp);
575 }
576
577 /* Function for printing ASCII byte-swapped strings, skipping white
578 * space. Please note that this is needed on both big- and
579 * little-endian hardware.
580 */
printswap(char * output,char * in,unsigned int n)581 void printswap(char *output, char *in, unsigned int n)
582 {
583 formatdriveidstring(output, in, n);
584 if (*output)
585 printf("%.*s ", (int)n, output);
586 else
587 printf("%.*s ", (int)n, "[No Information Found]\n");
588 }
589
590 #define ATA_IDENTIFY_BUFF_SZ sizeof(struct ata_identify_device)
591 #define HDIO_DRIVE_CMD_OFFSET 4
592
ata_command_interface(int device,char * data)593 int ata_command_interface(int device, char *data)
594 {
595 uint8_t buff[ATA_IDENTIFY_BUFF_SZ + HDIO_DRIVE_CMD_OFFSET];
596 int retval;
597
598 buff[0] = ATA_IDENTIFY_DEVICE;
599 buff[3] = 1;
600 /* We are now doing the HDIO_DRIVE_CMD type ioctl. */
601 if ((retval = ioctl(device, HDIO_DRIVE_CMD, buff)))
602 return retval;
603
604 /* if the command returns data, copy it back */
605 memcpy(data, buff + HDIO_DRIVE_CMD_OFFSET, ATA_IDENTIFY_BUFF_SZ);
606 return 0;
607 }
608
try_ata_identity(const char * file_namep,int ata_fd,bool do_inq)609 int try_ata_identity(const char * file_namep, int ata_fd, bool do_inq)
610 {
611 struct ata_identify_device ata_ident;
612 char model[64];
613 char serial[64];
614 char firm[64];
615 int res;
616
617 res = ata_command_interface(ata_fd, (char *)&ata_ident);
618 if (res)
619 return res;
620 printf("%s: ATA device\n", file_namep);
621 if (do_inq) {
622 printf(" ");
623 printswap(model, (char *)ata_ident.model, 40);
624 printswap(serial, (char *)ata_ident.serial_no, 20);
625 printswap(firm, (char *)ata_ident.fw_rev, 8);
626 printf("\n");
627 }
628 return res;
629 }
630