blob: 6a156755a95eff2616f6628f46150d4ffac92051 [file] [log] [blame]
/* Utility program for the Linux OS SCSI generic ("sg") device driver.
* Copyright (C) 2000-2007 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.
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 <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.09 20130507";
static const char * devfs_id = "/dev/.devfsd";
#define NUMERIC_SCAN_DEF 1 /* change to 0 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, int 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,
int 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[])
{
int sg_fd, res, k;
int do_numeric = NUMERIC_SCAN_DEF;
int do_all_s = 1;
int do_sd = 0;
int do_st = 0;
int do_osst = 0;
int do_sr = 0;
int do_scd = 0;
int do_extra = 0;
int do_inquiry = 0;
char fname[64];
int num_errors = 0;
int num_silent = 0;
int eacces_err = 0;
int last_sg_ind = -1;
struct stat a_stat;
for (k = 1; k < argc; ++k) {
if (0 == strcmp("-n", argv[k]))
do_numeric = 1;
else if (0 == strcmp("-a", argv[k]))
do_numeric = 0;
else if (0 == strcmp("-x", argv[k]))
do_extra = 1;
else if (0 == strcmp("-i", argv[k]))
do_inquiry = 1;
else if (0 == strcmp("-sd", argv[k])) {
do_sd = 1;
do_all_s = 0;
} else if (0 == strcmp("-st", argv[k])) {
do_st = 1;
do_osst = 1;
do_all_s = 0;
} else if (0 == strcmp("-sr", argv[k])) {
do_sr = 1;
do_all_s = 0;
} else if (0 == strcmp("-scd", argv[k])) {
do_scd = 1;
do_all_s = 0;
} 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, 1);
} 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 = 1;
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, 0, 0, 0, buff, sizeof(buff),
1, 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, 1);
} 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, int 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);
}
}