/* * Copyright (c) 2005-2022 Douglas Gilbert. * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. * * SPDX-License-Identifier: BSD-2-Clause */ /* A utility program for the Linux OS SCSI subsystem. * * * This program maps a primary SCSI device node name to the corresponding * SCSI generic device node name (or vice versa). Targets Linux * kernel 2.6, 3 and 4 series. Sysfs device names can also be mapped. */ /* #define _XOPEN_SOURCE 500 */ /* needed to see DT_REG and friends when compiled with: c99 pedantic */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* new location for major + minor */ #ifndef major #include #endif #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" static const char * version_str = "1.19 20220117"; #define ME "sg_map26: " #define NT_NO_MATCH 0 #define NT_SD 1 #define NT_SR 2 #define NT_HD 3 #define NT_ST 4 #define NT_OSST 5 #define NT_SG 6 #define NT_CH 7 #define NT_REG 8 #define NT_DIR 9 #define NAME_LEN_MAX 256 #define D_NAME_LEN_MAX 520 #ifndef SCSI_CHANGER_MAJOR #define SCSI_CHANGER_MAJOR 86 #endif #ifndef OSST_MAJOR #define OSST_MAJOR 206 #endif /* scandir() and stat() categories */ #define FT_OTHER 0 #define FT_REGULAR 1 #define FT_BLOCK 2 #define FT_CHAR 3 #define FT_DIR 4 /* older major.h headers may not have these */ #ifndef SCSI_DISK8_MAJOR #define SCSI_DISK8_MAJOR 128 #define SCSI_DISK9_MAJOR 129 #define SCSI_DISK10_MAJOR 130 #define SCSI_DISK11_MAJOR 131 #define SCSI_DISK12_MAJOR 132 #define SCSI_DISK13_MAJOR 133 #define SCSI_DISK14_MAJOR 134 #define SCSI_DISK15_MAJOR 135 #endif /* st minor decodes from Kai Makisara 20081008 */ #define ST_NBR_MODE_BITS 2 #define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS) #define TAPE_NR(minor) ( (((minor) & ~255) >> (ST_NBR_MODE_BITS + 1)) | \ ((minor) & ~(UINT_MAX << ST_MODE_SHIFT)) ) static const char * sys_sg_dir = "/sys/class/scsi_generic/"; static const char * sys_sd_dir = "/sys/block/"; static const char * sys_sr_dir = "/sys/block/"; static const char * sys_hd_dir = "/sys/block/"; static const char * sys_st_dir = "/sys/class/scsi_tape/"; static const char * sys_sch_dir = "/sys/class/scsi_changer/"; static const char * sys_osst_dir = "/sys/class/onstream_tape/"; static const char * def_dev_dir = "/dev"; static struct option long_options[] = { {"dev_dir", required_argument, 0, 'd'}, {"given_is", required_argument, 0, 'g'}, {"help", no_argument, 0, 'h'}, {"result", required_argument, 0, 'r'}, {"symlink", no_argument, 0, 's'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0}, }; static const char * nt_names[] = { "No matching", "disk", "cd/dvd", "hd", "tape", "tape (osst)", "generic (sg)", "changer", "regular file", "directory", }; #if defined(__GNUC__) || defined(__clang__) static int pr2serr(const char * fmt, ...) __attribute__ ((format (printf, 1, 2))); #else static int pr2serr(const char * fmt, ...); #endif static int pr2serr(const char * fmt, ...) { va_list args; int n; va_start(args, fmt); n = vfprintf(stderr, fmt, args); va_end(args); return n; } static void usage() { pr2serr("Usage: sg_map26 [--dev_dir=DIR] [--given_is=0...1] [--help] " "[--result=0...3]\n" " [--symlink] [--verbose] [--version] " "DEVICE\n" " where:\n" " --dev_dir=DIR | -d DIR search in DIR for " "resulting special\n" " (def: directory of DEVICE " "or '/dev')\n" " --given_is=0...1 | -g 0...1 variety of given " "DEVICE\n" " 0->block or char special " "(or symlink to)\n" " 1->sysfs device, 'dev' or " "parent\n" " --help | -h print out usage message\n" " --result=0...3 | -r 0...3 variety of file(s) to " "find\n" " 0->mapped block or char " "special(def)\n" " 1->mapped sysfs path\n" " 2->matching block or " "char special\n" " 3->matching sysfs " "path\n" " --symlink | -s symlinks to special included in " "result\n" " --verbose | -v increase verbosity of output\n" " --version | -V print version string and exit\n\n" "Maps SCSI device node to corresponding generic node (and " "vv)\n" ); } /* ssafe_strerror() contributed by Clayton Weaver Allows for situation in which strerror() is given a wild value (or the C library is incomplete) and returns NULL. Still not thread safe. */ static char safe_errbuf[64] = {'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'e', 'r', 'r', 'n', 'o', ':', ' ', 0}; static char * ssafe_strerror(int errnum) { size_t len; char * errstr; errstr = strerror(errnum); if (NULL == errstr) { len = strlen(safe_errbuf); snprintf(safe_errbuf + len, sizeof(safe_errbuf) - len, "%i", errnum); safe_errbuf[sizeof(safe_errbuf) - 1] = '\0'; /* bombproof */ return safe_errbuf; } return errstr; } static int nt_typ_from_filename(const char * filename, int * majj, int * minn) { struct stat st; int ma, mi; if (stat(filename, &st) < 0) return -errno; ma = major(st.st_rdev); mi = minor(st.st_rdev); if (majj) *majj = ma; if (minn) *minn = mi; if (S_ISCHR(st.st_mode)) { switch(ma) { case OSST_MAJOR: return NT_OSST; case SCSI_GENERIC_MAJOR: return NT_SG; case SCSI_TAPE_MAJOR: return NT_ST; case SCSI_CHANGER_MAJOR: return NT_CH; default: return NT_NO_MATCH; } } else if (S_ISBLK(st.st_mode)) { switch(ma) { case SCSI_DISK0_MAJOR: case SCSI_DISK1_MAJOR: case SCSI_DISK2_MAJOR: case SCSI_DISK3_MAJOR: case SCSI_DISK4_MAJOR: case SCSI_DISK5_MAJOR: case SCSI_DISK6_MAJOR: case SCSI_DISK7_MAJOR: case SCSI_DISK8_MAJOR: case SCSI_DISK9_MAJOR: case SCSI_DISK10_MAJOR: case SCSI_DISK11_MAJOR: case SCSI_DISK12_MAJOR: case SCSI_DISK13_MAJOR: case SCSI_DISK14_MAJOR: case SCSI_DISK15_MAJOR: return NT_SD; case SCSI_CDROM_MAJOR: return NT_SR; case IDE0_MAJOR: case IDE1_MAJOR: case IDE2_MAJOR: case IDE3_MAJOR: case IDE4_MAJOR: case IDE5_MAJOR: case IDE6_MAJOR: case IDE7_MAJOR: case IDE8_MAJOR: case IDE9_MAJOR: return NT_HD; default: return NT_NO_MATCH; } } else if (S_ISREG(st.st_mode)) return NT_REG; else if (S_ISDIR(st.st_mode)) return NT_DIR; return NT_NO_MATCH; } static int nt_typ_from_major(int ma) { switch(ma) { case SCSI_DISK0_MAJOR: case SCSI_DISK1_MAJOR: case SCSI_DISK2_MAJOR: case SCSI_DISK3_MAJOR: case SCSI_DISK4_MAJOR: case SCSI_DISK5_MAJOR: case SCSI_DISK6_MAJOR: case SCSI_DISK7_MAJOR: case SCSI_DISK8_MAJOR: case SCSI_DISK9_MAJOR: case SCSI_DISK10_MAJOR: case SCSI_DISK11_MAJOR: case SCSI_DISK12_MAJOR: case SCSI_DISK13_MAJOR: case SCSI_DISK14_MAJOR: case SCSI_DISK15_MAJOR: return NT_SD; case SCSI_CDROM_MAJOR: return NT_SR; case IDE0_MAJOR: case IDE1_MAJOR: case IDE2_MAJOR: case IDE3_MAJOR: case IDE4_MAJOR: case IDE5_MAJOR: case IDE6_MAJOR: case IDE7_MAJOR: case IDE8_MAJOR: case IDE9_MAJOR: return NT_HD; case OSST_MAJOR: return NT_OSST; case SCSI_GENERIC_MAJOR: return NT_SG; case SCSI_TAPE_MAJOR: return NT_ST; case SCSI_CHANGER_MAJOR: return NT_CH; default: return NT_NO_MATCH; } return NT_NO_MATCH; } struct node_match_item { bool follow_symlink; int file_type; int majj; int minn; char dir_name[D_NAME_LEN_MAX]; }; static struct node_match_item nd_match; static int nd_match_scandir_select(const struct dirent * s) { bool symlnk = false; struct stat st; char name[D_NAME_LEN_MAX]; switch (s->d_type) { case DT_BLK: if (FT_BLOCK != nd_match.file_type) return 0; break; case DT_CHR: if (FT_CHAR != nd_match.file_type) return 0; break; case DT_DIR: return (FT_DIR == nd_match.file_type) ? 1 : 0; case DT_REG: return (FT_REGULAR == nd_match.file_type) ? 1 : 0; case DT_LNK: /* follow symlinks */ if (! nd_match.follow_symlink) return 0; symlnk = true; break; default: return 0; } if ((! symlnk) && (-1 == nd_match.majj) && (-1 == nd_match.minn)) return 1; snprintf(name, sizeof(name), "%.*s/%.*s", NAME_LEN_MAX, nd_match.dir_name, NAME_LEN_MAX, s->d_name); memset(&st, 0, sizeof(st)); if (stat(name, &st) < 0) return 0; if (symlnk) { if (S_ISCHR(st.st_mode)) { if (FT_CHAR != nd_match.file_type) return 0; } else if (S_ISBLK(st.st_mode)) { if (FT_BLOCK != nd_match.file_type) return 0; } else return 0; } return (((-1 == nd_match.majj) || ((unsigned)major(st.st_rdev) == (unsigned)nd_match.majj)) && ((-1 == nd_match.minn) || ((unsigned)minor(st.st_rdev) == (unsigned)nd_match.minn))) ? 1 : 0; } static int list_matching_nodes(const char * dir_name, int file_type, int majj, int minn, bool follow_symlink, int verbose) { struct dirent ** namelist; int num, k; strncpy(nd_match.dir_name, dir_name, D_NAME_LEN_MAX - 1); nd_match.file_type = file_type; nd_match.majj = majj; nd_match.minn = minn; nd_match.follow_symlink = follow_symlink; num = scandir(dir_name, &namelist, nd_match_scandir_select, NULL); if (num < 0) { if (verbose) pr2serr("scandir: %s %s\n", dir_name, ssafe_strerror(errno)); return -errno; } for (k = 0; k < num; ++k) { printf("%s/%s\n", dir_name, namelist[k]->d_name); free(namelist[k]); } free(namelist); return num; } struct sg_item_t { char name[NAME_LEN_MAX + 2]; int ft; int nt; int d_type; }; static struct sg_item_t for_first; static int first_scandir_select(const struct dirent * s) { if (FT_OTHER != for_first.ft) return 0; if ((DT_LNK != s->d_type) && ((DT_DIR != s->d_type) || ('.' == s->d_name[0]))) return 0; strncpy(for_first.name, s->d_name, NAME_LEN_MAX); for_first.ft = FT_CHAR; /* dummy */ for_first.d_type = s->d_type; return 1; } /* scan for directory entry that is either a symlink or a directory */ static int scan_for_first(const char * dir_name, int verbose) { char name[NAME_LEN_MAX]; struct dirent ** namelist; int num, k; for_first.ft = FT_OTHER; num = scandir(dir_name, &namelist, first_scandir_select, NULL); if (num < 0) { if (verbose > 0) { snprintf(name, NAME_LEN_MAX, "scandir: %s", dir_name); perror(name); } return -1; } for (k = 0; k < num; ++k) free(namelist[k]); free(namelist); return num; } static struct sg_item_t from_sg; static int from_sg_scandir_select(const struct dirent * s) { int len; if (FT_OTHER != from_sg.ft) return 0; if ((DT_LNK != s->d_type) && ((DT_DIR != s->d_type) || ('.' == s->d_name[0]))) return 0; from_sg.d_type = s->d_type; if (0 == strncmp("scsi_changer", s->d_name, 12)) { strncpy(from_sg.name, s->d_name, NAME_LEN_MAX); from_sg.ft = FT_CHAR; from_sg.nt = NT_CH; return 1; } else if (0 == strncmp("block", s->d_name, 5)) { strncpy(from_sg.name, s->d_name, NAME_LEN_MAX); from_sg.ft = FT_BLOCK; return 1; } else if (0 == strcmp("tape", s->d_name)) { strcpy(from_sg.name, s->d_name); from_sg.ft = FT_CHAR; from_sg.nt = NT_ST; return 1; } else if (0 == strncmp("scsi_tape:st", s->d_name, 12)) { len = strlen(s->d_name); if (isdigit(s->d_name[len - 1])) { /* want 'st' symlink only */ strcpy(from_sg.name, s->d_name); from_sg.ft = FT_CHAR; from_sg.nt = NT_ST; return 1; } else return 0; } else if (0 == strncmp("onstream_tape:os", s->d_name, 16)) { strcpy(from_sg.name, s->d_name); from_sg.ft = FT_CHAR; from_sg.nt = NT_OSST; return 1; } else return 0; } static int from_sg_scan(const char * dir_name, int verbose) { struct dirent ** namelist; int num, k; from_sg.ft = FT_OTHER; from_sg.nt = NT_NO_MATCH; num = scandir(dir_name, &namelist, from_sg_scandir_select, NULL); if (num < 0) { if (verbose) pr2serr("scandir: %s %s\n", dir_name, ssafe_strerror(errno)); return -errno; } if (verbose) { for (k = 0; k < num; ++k) pr2serr(" %s/%s\n", dir_name, namelist[k]->d_name); } for (k = 0; k < num; ++k) free(namelist[k]); free(namelist); return num; } static struct sg_item_t to_sg; static int to_sg_scandir_select(const struct dirent * s) { if (FT_OTHER != to_sg.ft) return 0; if (DT_LNK != s->d_type) return 0; if (0 == strncmp("scsi_generic", s->d_name, 12)) { strncpy(to_sg.name, s->d_name, NAME_LEN_MAX); to_sg.ft = FT_CHAR; to_sg.nt = NT_SG; return 1; } else return 0; } static int to_sg_scan(const char * dir_name) { struct dirent ** namelist; int num, k; to_sg.ft = FT_OTHER; to_sg.nt = NT_NO_MATCH; num = scandir(dir_name, &namelist, to_sg_scandir_select, NULL); if (num < 0) return -errno; for (k = 0; k < num; ++k) free(namelist[k]); free(namelist); return num; } /* Return 1 if directory, else 0 */ static int if_directory_chdir(const char * dir_name, const char * base_name) { char buff[D_NAME_LEN_MAX]; struct stat a_stat; strcpy(buff, dir_name); strcat(buff, "/"); strcat(buff, base_name); if (stat(buff, &a_stat) < 0) return 0; if (S_ISDIR(a_stat.st_mode)) { if (chdir(buff) < 0) return 0; return 1; } return 0; } /* Return 1 if directory, else 0 */ static int if_directory_ch2generic(const char * dir_name) { char buff[NAME_LEN_MAX]; struct stat a_stat; const char * old_name = "generic"; strcpy(buff, dir_name); strcat(buff, "/"); strcat(buff, old_name); if ((stat(buff, &a_stat) >= 0) && S_ISDIR(a_stat.st_mode)) { if (chdir(buff) < 0) return 0; return 1; } /* No "generic", so now look for "scsi_generic:sg" */ if (1 != to_sg_scan(dir_name)) return 0; strcpy(buff, dir_name); strcat(buff, "/"); strcat(buff, to_sg.name); if (stat(buff, &a_stat) < 0) return 0; if (S_ISDIR(a_stat.st_mode)) { if (chdir(buff) < 0) return 0; return 1; } return 0; } /* Return 1 if found, else 0 if problems */ static int get_value(const char * dir_name, const char * base_name, char * value, int max_value_len) { char buff[D_NAME_LEN_MAX]; FILE * f; int len; if ((NULL == dir_name) && (NULL == base_name)) return 0; if (dir_name) { strcpy(buff, dir_name); if (base_name && (strlen(base_name) > 0)) { strcat(buff, "/"); strcat(buff, base_name); } } else strcpy(buff, base_name); if (NULL == (f = fopen(buff, "r"))) { return 0; } if (NULL == fgets(value, max_value_len, f)) { fclose(f); return 0; } len = strlen(value); if ((len > 0) && (value[len - 1] == '\n')) value[len - 1] = '\0'; fclose(f); return 1; } static int map_hd(const char * device_dir, int ma, int mi, int result, bool follow_symlink, int verbose) { char c, num; if (2 == result) { num = list_matching_nodes(device_dir, FT_BLOCK, ma, mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } switch (ma) { case IDE0_MAJOR: c = 'a'; break; case IDE1_MAJOR: c = 'c'; break; case IDE2_MAJOR: c = 'e'; break; case IDE3_MAJOR: c = 'g'; break; case IDE4_MAJOR: c = 'i'; break; case IDE5_MAJOR: c = 'k'; break; case IDE6_MAJOR: c = 'm'; break; case IDE7_MAJOR: c = 'o'; break; case IDE8_MAJOR: c = 'q'; break; case IDE9_MAJOR: c = 's'; break; default: c = '?'; break; } if (mi > 63) ++c; printf("%shd%c\n", sys_hd_dir, c); return 0; } static int map_sd(const char * device_name, const char * device_dir, int ma, int mi, int result, bool follow_symlink, int verbose) { int index, m_mi, m_ma, num; char value[D_NAME_LEN_MAX]; char name[D_NAME_LEN_MAX]; if (2 == result) { num = list_matching_nodes(device_dir, FT_BLOCK, ma, mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } if (SCSI_DISK0_MAJOR == ma) index = mi / 16; else if (ma >= SCSI_DISK8_MAJOR) index = (mi / 16) + 128 + ((ma - SCSI_DISK8_MAJOR) * 16); else index = (mi / 16) + 16 + ((ma - SCSI_DISK1_MAJOR) * 16); if (index < 26) snprintf(name, sizeof(name), "%ssd%c", sys_sd_dir, 'a' + index % 26); else if (index < (26 + 1) * 26) snprintf(name, sizeof(name), "%ssd%c%c", sys_sd_dir, 'a' + index / 26 - 1,'a' + index % 26); else { const unsigned int m1 = (index / 26 - 1) / 26 - 1; const unsigned int m2 = (index / 26 - 1) % 26; const unsigned int m3 = index % 26; snprintf(name, sizeof(name), "%ssd%c%c%c", sys_sd_dir, 'a' + m1, 'a' + m2, 'a' + m3); } if (3 == result) { printf("%s\n", name); return 0; } if (! get_value(name, "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs match for device: %s\n", device_name); return 1; } if (verbose) pr2serr("sysfs sd dev: %s\n", value); if (! if_directory_chdir(name, "device")) { pr2serr("sysfs problem with device: %s\n", device_name); return 1; } if (if_directory_ch2generic(".")) { if (1 == result) { if (NULL == getcwd(value, sizeof(value))) value[0] = '\0'; printf("%s\n", value); return 0; } if (! get_value(".", "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs generic dev\n"); return 1; } if (verbose) printf("matching dev: %s\n", value); if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) { pr2serr("Couldn't decode mapped dev\n"); return 1; } num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } else { pr2serr("sd device: %s does not match any SCSI generic " "device\n", device_name); pr2serr(" perhaps sg module is not loaded\n"); return 1; } } static int map_sr(const char * device_name, const char * device_dir, int ma, int mi, int result, bool follow_symlink, int verbose) { int m_mi, m_ma, num; char value[D_NAME_LEN_MAX]; char name[D_NAME_LEN_MAX]; if (2 == result) { num = list_matching_nodes(device_dir, FT_BLOCK, ma, mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } snprintf(name, sizeof(name), "%ssr%d", sys_sr_dir, mi); if (3 == result) { printf("%s\n", name); return 0; } if (! get_value(name, "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs match for device: %s\n", device_name); return 1; } if (verbose) pr2serr("sysfs sr dev: %s\n", value); if (! if_directory_chdir(name, "device")) { pr2serr("sysfs problem with device: %s\n", device_name); return 1; } if (if_directory_ch2generic(".")) { if (1 == result) { if (NULL == getcwd(value, sizeof(value))) value[0] = '\0'; printf("%s\n", value); return 0; } if (! get_value(".", "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs generic dev\n"); return 1; } if (verbose) printf("matching dev: %s\n", value); if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) { pr2serr("Couldn't decode mapped dev\n"); return 1; } num = list_matching_nodes(device_dir, FT_BLOCK, m_ma, m_mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } else { pr2serr("sr device: %s does not match any SCSI generic " "device\n", device_name); pr2serr(" perhaps sg module is not loaded\n"); return 1; } } static int map_st(const char * device_name, const char * device_dir, int ma, int mi, int result, bool follow_symlink, int verbose) { int m_mi, m_ma, num; char value[D_NAME_LEN_MAX]; char name[D_NAME_LEN_MAX]; if (2 == result) { num = list_matching_nodes(device_dir, FT_CHAR, ma, mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } snprintf(name, sizeof(name), "%sst%d", sys_st_dir, TAPE_NR(mi)); if (3 == result) { printf("%s\n", name); return 0; } if (! get_value(name, "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs match for device: %s\n", device_name); return 1; } if (verbose) pr2serr("sysfs st dev: %s\n", value); if (! if_directory_chdir(name, "device")) { pr2serr("sysfs problem with device: %s\n", device_name); return 1; } if (if_directory_ch2generic(".")) { if (1 == result) { if (NULL == getcwd(value, sizeof(value))) value[0] = '\0'; printf("%s\n", value); return 0; } if (! get_value(".", "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs generic dev\n"); return 1; } if (verbose) printf("matching dev: %s\n", value); if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) { pr2serr("Couldn't decode mapped dev\n"); return 1; } num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } else { pr2serr("st device: %s does not match any SCSI generic " "device\n", device_name); pr2serr(" perhaps sg module is not loaded\n"); return 1; } } static int map_osst(const char * device_name, const char * device_dir, int ma, int mi, int result, bool follow_symlink, int verbose) { int m_mi, m_ma, num; char value[D_NAME_LEN_MAX]; char name[D_NAME_LEN_MAX]; if (2 == result) { num = list_matching_nodes(device_dir, FT_CHAR, ma, mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } snprintf(name, sizeof(name), "%sosst%d", sys_osst_dir, TAPE_NR(mi)); if (3 == result) { printf("%s\n", name); return 0; } if (! get_value(name, "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs match for device: %s\n", device_name); return 1; } if (verbose) pr2serr("sysfs osst dev: %s\n", value); if (! if_directory_chdir(name, "device")) { pr2serr("sysfs problem with device: %s\n", device_name); return 1; } if (if_directory_ch2generic(".")) { if (1 == result) { if (NULL == getcwd(value, sizeof(value))) value[0] = '\0'; printf("%s\n", value); return 0; } if (! get_value(".", "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs generic dev\n"); return 1; } if (verbose) printf("matching dev: %s\n", value); if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) { pr2serr("Couldn't decode mapped dev\n"); return 1; } num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } else { pr2serr("osst device: %s does not match any SCSI generic " "device\n", device_name); pr2serr(" perhaps sg module is not loaded\n"); return 1; } } static int map_ch(const char * device_name, const char * device_dir, int ma, int mi, int result, bool follow_symlink, int verbose) { int m_mi, m_ma, num; char value[D_NAME_LEN_MAX]; char name[D_NAME_LEN_MAX]; if (2 == result) { num = list_matching_nodes(device_dir, FT_CHAR, ma, mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } snprintf(name, sizeof(name), "%ssch%d", sys_sch_dir, mi); if (3 == result) { printf("%s\n", name); return 0; } if (! get_value(name, "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs match for device: %s\n", device_name); return 1; } if (verbose) pr2serr("sysfs sch dev: %s\n", value); if (! if_directory_chdir(name, "device")) { pr2serr("sysfs problem with device: %s\n", device_name); return 1; } if (if_directory_ch2generic(".")) { if (1 == result) { if (NULL == getcwd(value, sizeof(value))) value[0] = '\0'; printf("%s\n", value); return 0; } if (! get_value(".", "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs generic dev\n"); return 1; } if (verbose) printf("matching dev: %s\n", value); if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) { pr2serr("Couldn't decode mapped dev\n"); return 1; } num = list_matching_nodes(device_dir, FT_CHAR, m_ma, m_mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } else { pr2serr("sch device: %s does not match any SCSI generic " "device\n", device_name); pr2serr(" perhaps sg module is not loaded\n"); return 1; } } static int map_sg(const char * device_name, const char * device_dir, int ma, int mi, int result, bool follow_symlink, int verbose) { int m_mi, m_ma, num; char value[D_NAME_LEN_MAX]; char name[D_NAME_LEN_MAX]; if (2 == result) { num = list_matching_nodes(device_dir, FT_CHAR, ma, mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } snprintf(name, sizeof(name), "%ssg%d", sys_sg_dir, mi); if (3 == result) { printf("%s\n", name); return 0; } if (! get_value(name, "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs match for device: %s\n", device_name); return 1; } if (verbose) pr2serr("sysfs sg dev: %s\n", value); if (! if_directory_chdir(name, "device")) { pr2serr("sysfs problem with device: %s\n", device_name); return 1; } if ((1 == from_sg_scan(".", verbose)) && (if_directory_chdir(".", from_sg.name))) { if (DT_DIR == from_sg.d_type) { if ((1 == scan_for_first(".", verbose)) && (if_directory_chdir(".", for_first.name))) { ; } else { pr2serr("unexpected scan_for_first error\n"); } } if (1 == result) { if (NULL == getcwd(value, sizeof(value))) value[0] = '\0'; printf("%s\n", value); return 0; } if (! get_value(".", "dev", value, sizeof(value))) { pr2serr("Couldn't find sysfs block dev\n"); return 1; } if (verbose) printf("matching dev: %s\n", value); if (2 != sscanf(value, "%d:%d", &m_ma, &m_mi)) { pr2serr("Couldn't decode mapped dev\n"); return 1; } num = list_matching_nodes(device_dir, from_sg.ft, m_ma, m_mi, follow_symlink, verbose); return (num > 0) ? 0 : 1; } else { pr2serr("sg device: %s does not match any other SCSI " "device\n", device_name); return 1; } } int main(int argc, char * argv[]) { bool cont; int c, num, tt, res; int given_is = -1; int result = 0; int verbose = 0; int ret = 1; int ma, mi; bool do_dev_dir = false; bool follow_symlink = false; char device_name[D_NAME_LEN_MAX]; char device_dir[D_NAME_LEN_MAX]; char value[D_NAME_LEN_MAX]; memset(device_name, 0, sizeof(device_name)); memset(device_dir, 0, sizeof(device_dir)); while (1) { int option_index = 0; c = getopt_long(argc, argv, "d:hg:r:svV", long_options, &option_index); if (c == -1) break; switch (c) { case 'd': strncpy(device_dir, optarg, sizeof(device_dir) - 1); do_dev_dir = true; break; case 'g': num = sscanf(optarg, "%d", &res); if ((1 == num) && ((0 == res) || (1 == res))) given_is = res; else { pr2serr("value for '--given_to=' must be 0 " "or 1\n"); return SG_LIB_SYNTAX_ERROR; } break; case 'h': case '?': usage(); return 0; case 'r': num = sscanf(optarg, "%d", &res); if ((1 == num) && (res >= 0) && (res < 4)) result = res; else { pr2serr("value for '--result=' must be " "0..3\n"); return SG_LIB_SYNTAX_ERROR; } break; case 's': follow_symlink = true; break; case 'v': ++verbose; break; case 'V': pr2serr(ME "version: %s\n", version_str); return 0; default: pr2serr("unrecognised option code 0x%x ??\n", c); usage(); return SG_LIB_SYNTAX_ERROR; } } if (optind < argc) { if ('\0' == device_name[0]) { strncpy(device_name, argv[optind], sizeof(device_name) - 1); device_name[sizeof(device_name) - 1] = '\0'; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) pr2serr("Unexpected extra argument: %s\n", argv[optind]); usage(); return SG_LIB_SYNTAX_ERROR; } } if (0 == device_name[0]) { pr2serr("missing device name!\n"); usage(); return SG_LIB_SYNTAX_ERROR; } ma = 0; mi = 0; if (do_dev_dir) { if (if_directory_chdir(".", device_dir)) { if (getcwd(device_dir, sizeof(device_dir))) device_dir[sizeof(device_dir) - 1] = '\0'; else device_dir[0] = '\0'; if (verbose > 1) pr2serr("Absolute path to dev_dir: %s\n", device_dir); } else { pr2serr("dev_dir: %s invalid\n", device_dir); return SG_LIB_FILE_ERROR; } } else { strcpy(device_dir, device_name); dirname(device_dir); if (0 == strcmp(device_dir, device_name)) { if (NULL == getcwd(device_dir, sizeof(device_dir))) device_dir[0] = '\0'; } } ret = nt_typ_from_filename(device_name, &ma, &mi); if (ret < 0) { pr2serr("stat failed on %s: %s\n", device_name, ssafe_strerror(-ret)); return SG_LIB_FILE_ERROR; } if (verbose) pr2serr(" %s: %s device [maj=%d, min=%d]\n", device_name, nt_names[ret], ma, mi); res = 0; switch (ret) { case NT_SD: case NT_SR: case NT_HD: if (given_is > 0) { pr2serr("block special but '--given_is=' suggested " "sysfs device\n"); return SG_LIB_FILE_ERROR; } break; case NT_ST: case NT_OSST: case NT_CH: case NT_SG: if (given_is > 0) { pr2serr("character special but '--given_is=' " "suggested sysfs device\n"); return SG_LIB_FILE_ERROR; } break; case NT_REG: if (0 == given_is) { pr2serr("regular file but '--given_is=' suggested " "block or char special\n"); return SG_LIB_FILE_ERROR; } strcpy(device_dir, def_dev_dir); break; case NT_DIR: if (0 == given_is) { pr2serr("directory but '--given_is=' suggested " "block or char special\n"); return SG_LIB_FILE_ERROR; } strcpy(device_dir, def_dev_dir); break; default: break; } tt = NT_NO_MATCH; do { cont = false; switch (ret) { case NT_NO_MATCH: res = 1; break; case NT_SD: res = map_sd(device_name, device_dir, ma, mi, result, follow_symlink, verbose); break; case NT_SR: res = map_sr(device_name, device_dir, ma, mi, result, follow_symlink, verbose); break; case NT_HD: if (result < 2) { pr2serr("a hd device does not map to a sg " "device\n"); return SG_LIB_FILE_ERROR; } res = map_hd(device_dir, ma, mi, result, follow_symlink, verbose); break; case NT_ST: res = map_st(device_name, device_dir, ma, mi, result, follow_symlink, verbose); break; case NT_OSST: res = map_osst(device_name, device_dir, ma, mi, result, follow_symlink, verbose); break; case NT_CH: res = map_ch(device_name, device_dir, ma, mi, result, follow_symlink, verbose); break; case NT_SG: res = map_sg(device_name, device_dir, ma, mi, result, follow_symlink, verbose); break; case NT_REG: if (! get_value(NULL, device_name, value, sizeof(value))) { pr2serr("Couldn't fetch value from: %s\n", device_name); return SG_LIB_FILE_ERROR; } if (verbose) pr2serr("value: %s\n", value); if (2 != sscanf(value, "%d:%d", &ma, &mi)) { pr2serr("Couldn't decode value\n"); return SG_LIB_FILE_ERROR; } tt = nt_typ_from_major(ma); cont = true; break; case NT_DIR: if (! get_value(device_name, "dev", value, sizeof(value))) { pr2serr("Couldn't fetch value from: %s/dev\n", device_name); return SG_LIB_FILE_ERROR; } if (verbose) pr2serr("value: %s\n", value); if (2 != sscanf(value, "%d:%d", &ma, &mi)) { pr2serr("Couldn't decode value\n"); return SG_LIB_FILE_ERROR; } tt = nt_typ_from_major(ma); cont = true; break; default: break; } ret = tt; } while (cont); return res; }