diff options
Diffstat (limited to 'src/sg_map26.c')
-rw-r--r-- | src/sg_map26.c | 1285 |
1 files changed, 1285 insertions, 0 deletions
diff --git a/src/sg_map26.c b/src/sg_map26.c new file mode 100644 index 00000000..2ea8d691 --- /dev/null +++ b/src/sg_map26.c @@ -0,0 +1,1285 @@ +/* + * 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 <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <getopt.h> +#include <dirent.h> +#include <libgen.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> /* new location for major + minor */ +#ifndef major +#include <sys/types.h> +#endif +#include <linux/major.h> + +#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 <cgweav at email dot com> + 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<num>' 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<n>" */ + 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; +} |