aboutsummaryrefslogtreecommitdiff
path: root/src/sg_map.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_map.c')
-rw-r--r--src/sg_map.c508
1 files changed, 508 insertions, 0 deletions
diff --git a/src/sg_map.c b/src/sg_map.c
new file mode 100644
index 00000000..ebf2c2e4
--- /dev/null
+++ b/src/sg_map.c
@@ -0,0 +1,508 @@
+/*
+ * Utility program for the Linux OS SCSI generic ("sg") device driver.
+ * Copyright (C) 2000-2017 D. Gilbert
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+
+ This shows the mapping from "sg" devices to other scsi devices
+ (i.e. sd, scd or st) if any.
+
+ Note: This program requires sg version 2 or better.
+
+ Version 0.19 20041203
+
+ Version 1.02 20050511
+ - allow for sparse disk name with up to 3 letter SCSI
+ disk device node names (e.g. /dev/sdaaa)
+ [Nate Dailey < Nate dot Dailey at stratus dot com >]
+*/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_io_linux.h"
+
+
+static const char * version_str = "1.12 20171010";
+
+static const char * devfs_id = "/dev/.devfsd";
+
+#define NUMERIC_SCAN_DEF true /* set to false to make alpha scan default */
+
+#define INQUIRY_RESP_INITIAL_LEN 36
+#define MAX_SG_DEVS 4096
+#define PRESENT_ARRAY_SIZE MAX_SG_DEVS
+
+static const char * sysfs_sg_dir = "/sys/class/scsi_generic";
+static char gen_index_arr[PRESENT_ARRAY_SIZE];
+static int has_sysfs_sg = 0;
+
+
+typedef struct my_map_info
+{
+ int active;
+ int lin_dev_type;
+ int oth_dev_num;
+ struct sg_scsi_id sg_dat;
+ char vendor[8];
+ char product[16];
+ char revision[4];
+} my_map_info_t;
+
+
+#define MAX_SD_DEVS (26 + 26*26 + 26*26*26) /* sdX, sdXX, sdXXX */
+ /* (26 + 676 + 17576) = 18278 */
+#define MAX_SR_DEVS 128
+#define MAX_ST_DEVS 128
+#define MAX_OSST_DEVS 128
+#define MAX_ERRORS 5
+
+static my_map_info_t map_arr[MAX_SG_DEVS];
+
+#define LIN_DEV_TYPE_UNKNOWN 0
+#define LIN_DEV_TYPE_SD 1
+#define LIN_DEV_TYPE_SR 2
+#define LIN_DEV_TYPE_ST 3
+#define LIN_DEV_TYPE_SCD 4
+#define LIN_DEV_TYPE_OSST 5
+
+
+typedef struct my_scsi_idlun {
+/* why can't userland see this structure ??? */
+ int dev_id;
+ int host_unique_id;
+} My_scsi_idlun;
+
+
+#define EBUFF_SZ 256
+static char ebuff[EBUFF_SZ];
+
+static void scan_dev_type(const char * leadin, int max_dev, bool do_numeric,
+ int lin_dev_type, int last_sg_ind);
+
+static void usage()
+{
+ printf("Usage: sg_map [-a] [-h] [-i] [-n] [-sd] [-scd or -sr] [-st] "
+ "[-V] [-x]\n");
+ printf(" where:\n");
+ printf(" -a do alphabetic scan (ie sga, sgb, sgc)\n");
+ printf(" -h or -? show this usage message then exit\n");
+ printf(" -i also show device INQUIRY strings\n");
+ printf(" -n do numeric scan (i.e. sg0, sg1, sg2) "
+ "(default)\n");
+ printf(" -sd show mapping to disks\n");
+ printf(" -scd show mapping to cdroms (look for /dev/scd<n>\n");
+ printf(" -sr show mapping to cdroms (look for /dev/sr<n>\n");
+ printf(" -st show mapping to tapes (st and osst devices)\n");
+ printf(" -V print version string then exit\n");
+ printf(" -x also show bus,chan,id,lun and type\n\n");
+ printf("If no '-s*' arguments given then show all mappings. This "
+ "utility\nis DEPRECATED, do not use in Linux 2.6 series or "
+ "later.\n");
+}
+
+static int scandir_select(const struct dirent * s)
+{
+ int k;
+
+ if (1 == sscanf(s->d_name, "sg%d", &k)) {
+ if ((k >= 0) && (k < PRESENT_ARRAY_SIZE)) {
+ gen_index_arr[k] = 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int sysfs_sg_scan(const char * dir_name)
+{
+ struct dirent ** namelist;
+ int num, k;
+
+ num = scandir(dir_name, &namelist, scandir_select, NULL);
+ if (num < 0)
+ return -errno;
+ for (k = 0; k < num; ++k)
+ free(namelist[k]);
+ free(namelist);
+ return num;
+}
+
+static void make_dev_name(char * fname, const char * leadin, int k,
+ bool do_numeric)
+{
+ char buff[64];
+ int ones,tens,hundreds; /* for lack of a better name */
+ int buff_idx;
+
+ strcpy(fname, leadin ? leadin : "/dev/sg");
+ if (do_numeric) {
+ sprintf(buff, "%d", k);
+ strcat(fname, buff);
+ }
+ else if (k >= (26 + 26*26 + 26*26*26)) {
+ strcat(fname, "xxxx");
+ }
+ else {
+ ones = k % 26;
+
+ if ((k - 26) >= 0)
+ tens = ((k-26)/26) % 26;
+ else tens = -1;
+
+ if ((k - (26 + 26*26)) >= 0)
+ hundreds = ((k - (26 + 26*26))/(26*26)) % 26;
+ else hundreds = -1;
+
+ buff_idx = 0;
+ if (hundreds >= 0) buff[buff_idx++] = 'a' + (char)hundreds;
+ if (tens >= 0) buff[buff_idx++] = 'a' + (char)tens;
+ buff[buff_idx++] = 'a' + (char)ones;
+ buff[buff_idx] = '\0';
+ strcat(fname, buff);
+ }
+}
+
+
+int main(int argc, char * argv[])
+{
+ bool do_all_s = true;
+ bool do_extra = false;
+ bool do_inquiry = false;
+ bool do_numeric = NUMERIC_SCAN_DEF;
+ bool do_osst = false;
+ bool do_scd = false;
+ bool do_sd = false;
+ bool do_sr = false;
+ bool do_st = false;
+ bool eacces_err = false;
+ int sg_fd, res, k;
+ int num_errors = 0;
+ int num_silent = 0;
+ int last_sg_ind = -1;
+ char fname[64];
+ struct stat a_stat;
+
+ for (k = 1; k < argc; ++k) {
+ if (0 == strcmp("-n", argv[k]))
+ do_numeric = true;
+ else if (0 == strcmp("-a", argv[k]))
+ do_numeric = false;
+ else if (0 == strcmp("-x", argv[k]))
+ do_extra = true;
+ else if (0 == strcmp("-i", argv[k]))
+ do_inquiry = true;
+ else if (0 == strcmp("-sd", argv[k])) {
+ do_sd = true;
+ do_all_s = false;
+ } else if (0 == strcmp("-st", argv[k])) {
+ do_st = true;
+ do_osst = true;
+ do_all_s = false;
+ } else if (0 == strcmp("-sr", argv[k])) {
+ do_sr = true;
+ do_all_s = false;
+ } else if (0 == strcmp("-scd", argv[k])) {
+ do_scd = true;
+ do_all_s = false;
+ } else if (0 == strcmp("-V", argv[k])) {
+ fprintf(stderr, "Version string: %s\n", version_str);
+ exit(0);
+ } else if ((0 == strcmp("-?", argv[k])) ||
+ (0 == strncmp("-h", argv[k], 2))) {
+ printf(
+ "Show mapping from sg devices to other scsi device names\n\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ } else if (*argv[k] == '-') {
+ printf("Unknown switch: %s\n", argv[k]);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ } else if (*argv[k] != '-') {
+ printf("Unknown argument\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+
+ if ((stat(sysfs_sg_dir, &a_stat) >= 0) && (S_ISDIR(a_stat.st_mode)))
+ has_sysfs_sg = sysfs_sg_scan(sysfs_sg_dir);
+
+ if (stat(devfs_id, &a_stat) == 0)
+ printf("# Note: the devfs pseudo file system is present\n");
+
+ for (k = 0, res = 0; (k < MAX_SG_DEVS) && (num_errors < MAX_ERRORS);
+ ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
+ if (res < 0) {
+ snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
+ perror("sg_map: close error");
+ return SG_LIB_FILE_ERROR;
+ }
+ if (has_sysfs_sg) {
+ if (0 == gen_index_arr[k]) {
+ sg_fd = -1;
+ continue;
+ }
+ make_dev_name(fname, "/dev/sg", k, true);
+ } else
+ make_dev_name(fname, "/dev/sg", k, do_numeric);
+
+ sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
+ if (sg_fd < 0) {
+ if (EBUSY == errno) {
+ map_arr[k].active = -2;
+ continue;
+ }
+ else if ((ENODEV == errno) || (ENOENT == errno) ||
+ (ENXIO == errno)) {
+ ++num_errors;
+ ++num_silent;
+ map_arr[k].active = -1;
+ continue;
+ }
+ else {
+ if (EACCES == errno)
+ eacces_err = true;
+ snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname);
+ perror(ebuff);
+ ++num_errors;
+ continue;
+ }
+ }
+ res = ioctl(sg_fd, SG_GET_SCSI_ID, &map_arr[k].sg_dat);
+ if (res < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "device %s failed on sg ioctl, skip", fname);
+ perror(ebuff);
+ ++num_errors;
+ continue;
+ }
+ if (do_inquiry) {
+ char buff[INQUIRY_RESP_INITIAL_LEN];
+
+ if (0 == sg_ll_inquiry(sg_fd, false, false, 0, buff, sizeof(buff),
+ true, 0)) {
+ memcpy(map_arr[k].vendor, &buff[8], 8);
+ memcpy(map_arr[k].product, &buff[16], 16);
+ memcpy(map_arr[k].revision, &buff[32], 4);
+ }
+ }
+ map_arr[k].active = 1;
+ map_arr[k].oth_dev_num = -1;
+ last_sg_ind = k;
+ }
+ if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) {
+ printf("Stopping because there are too many error\n");
+ if (eacces_err)
+ printf(" root access may be required\n");
+ return SG_LIB_FILE_ERROR;
+ }
+ if (last_sg_ind < 0) {
+ printf("Stopping because no sg devices found\n");
+ }
+
+ if (do_all_s || do_sd)
+ scan_dev_type("/dev/sd", MAX_SD_DEVS, 0, LIN_DEV_TYPE_SD, last_sg_ind);
+ if (do_all_s || do_sr)
+ scan_dev_type("/dev/sr", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SR, last_sg_ind);
+ if (do_all_s || do_scd)
+ scan_dev_type("/dev/scd", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SCD,
+ last_sg_ind);
+ if (do_all_s || do_st)
+ scan_dev_type("/dev/nst", MAX_ST_DEVS, 1, LIN_DEV_TYPE_ST,
+ last_sg_ind);
+ if (do_all_s || do_osst)
+ scan_dev_type("/dev/osst", MAX_OSST_DEVS, 1, LIN_DEV_TYPE_OSST,
+ last_sg_ind);
+
+ for (k = 0; k <= last_sg_ind; ++k) {
+ if (has_sysfs_sg) {
+ if (0 == gen_index_arr[k]) {
+ continue;
+ }
+ make_dev_name(fname, "/dev/sg", k, true);
+ } else
+ make_dev_name(fname, "/dev/sg", k, do_numeric);
+ printf("%s", fname);
+ switch (map_arr[k].active)
+ {
+ case -2:
+ printf(do_extra ? " -2 -2 -2 -2 -2" : " busy");
+ break;
+ case -1:
+ printf(do_extra ? " -1 -1 -1 -1 -1" : " not present");
+ break;
+ case 0:
+ printf(do_extra ? " -3 -3 -3 -3 -3" : " some error");
+ break;
+ case 1:
+ if (do_extra)
+ printf(" %d %d %d %d %d", map_arr[k].sg_dat.host_no,
+ map_arr[k].sg_dat.channel, map_arr[k].sg_dat.scsi_id,
+ map_arr[k].sg_dat.lun, map_arr[k].sg_dat.scsi_type);
+ switch (map_arr[k].lin_dev_type)
+ {
+ case LIN_DEV_TYPE_SD:
+ make_dev_name(fname, "/dev/sd" , map_arr[k].oth_dev_num, 0);
+ printf(" %s", fname);
+ break;
+ case LIN_DEV_TYPE_ST:
+ make_dev_name(fname, "/dev/nst" , map_arr[k].oth_dev_num, 1);
+ printf(" %s", fname);
+ break;
+ case LIN_DEV_TYPE_OSST:
+ make_dev_name(fname, "/dev/osst" , map_arr[k].oth_dev_num, 1);
+ printf(" %s", fname);
+ break;
+ case LIN_DEV_TYPE_SR:
+ make_dev_name(fname, "/dev/sr" , map_arr[k].oth_dev_num, 1);
+ printf(" %s", fname);
+ break;
+ case LIN_DEV_TYPE_SCD:
+ make_dev_name(fname, "/dev/scd" , map_arr[k].oth_dev_num, 1);
+ printf(" %s", fname);
+ break;
+ default:
+ break;
+ }
+ if (do_inquiry)
+ printf(" %.8s %.16s %.4s", map_arr[k].vendor,
+ map_arr[k].product, map_arr[k].revision);
+ break;
+ default:
+ printf(" bad logic\n");
+ break;
+ }
+ printf("\n");
+ }
+ return 0;
+}
+
+static int find_dev_in_sg_arr(My_scsi_idlun * my_idlun, int host_no,
+ int last_sg_ind)
+{
+ int k;
+ struct sg_scsi_id * sidp;
+
+ for (k = 0; k <= last_sg_ind; ++k) {
+ sidp = &(map_arr[k].sg_dat);
+ if ((host_no == sidp->host_no) &&
+ ((my_idlun->dev_id & 0xff) == sidp->scsi_id) &&
+ (((my_idlun->dev_id >> 8) & 0xff) == sidp->lun) &&
+ (((my_idlun->dev_id >> 16) & 0xff) == sidp->channel))
+ return k;
+ }
+ return -1;
+}
+
+static void scan_dev_type(const char * leadin, int max_dev, bool do_numeric,
+ int lin_dev_type, int last_sg_ind)
+{
+ int k, res, ind, sg_fd = 0;
+ int num_errors = 0;
+ int num_silent = 0;
+ int host_no = -1;
+ My_scsi_idlun my_idlun;
+ char fname[64];
+
+ for (k = 0, res = 0; (k < max_dev) && (num_errors < MAX_ERRORS);
+ ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
+
+/* ignore close() errors */
+#if 0
+ if (res < 0) {
+ snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
+ perror("sg_map: close error");
+#ifndef IGN_CLOSE_ERR
+ return;
+#else
+ ++num_errors;
+ sg_fd = 0;
+#endif
+ }
+#endif
+ make_dev_name(fname, leadin, k, do_numeric);
+#ifdef DEBUG
+ printf ("Trying %s: ", fname);
+#endif
+
+ sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
+ if (sg_fd < 0) {
+#ifdef DEBUG
+ printf ("ERROR %i\n", errno);
+#endif
+ if (EBUSY == errno) {
+ printf("Device %s is busy\n", fname);
+ ++num_errors;
+ } else if ((ENODEV == errno) || (ENXIO == errno)) {
+ ++num_errors;
+ ++num_silent;
+ } else if (ENOENT != errno) { /* ignore ENOENT for sparse names */
+ snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname);
+ perror(ebuff);
+ ++num_errors;
+ }
+ continue;
+ }
+
+ res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
+ if (res < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "device %s failed on scsi ioctl(idlun), skip", fname);
+ perror(ebuff);
+ ++num_errors;
+#ifdef DEBUG
+ printf ("Couldn't get IDLUN!\n");
+#endif
+ continue;
+ }
+ res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
+ if (res < 0) {
+ snprintf(ebuff, EBUFF_SZ,
+ "device %s failed on scsi ioctl(bus_number), skip", fname);
+ perror(ebuff);
+ ++num_errors;
+#ifdef DEBUG
+ printf ("Couldn't get BUS!\n");
+#endif
+ continue;
+ }
+#ifdef DEBUG
+ printf ("%i(%x) %i %i %i %i\n", host_no, my_idlun.host_unique_id,
+ (my_idlun.dev_id>>24)&0xff, (my_idlun.dev_id>>16)&0xff,
+ (my_idlun.dev_id>>8)&0xff, my_idlun.dev_id&0xff);
+#endif
+ ind = find_dev_in_sg_arr(&my_idlun, host_no, last_sg_ind);
+ if (ind >= 0) {
+ map_arr[ind].oth_dev_num = k;
+ map_arr[ind].lin_dev_type = lin_dev_type;
+ }
+ else
+ printf("Strange, could not find device %s mapped to sg device??\n",
+ fname);
+ }
+}