X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=extras%2Fscsi_id%2Fscsi_id.c;h=21b7cbeaec2a36f128efce8e0011199aa25f3e87;hp=446df70949c010f297c3a660f258d25278f764b6;hb=18cff5c3b2e3591fa46107288ea2d7026a15ccf3;hpb=05ec6e7507d159e5a02a82927c6571b7e516c52e diff --git a/extras/scsi_id/scsi_id.c b/extras/scsi_id/scsi_id.c index 446df7094..21b7cbeae 100644 --- a/extras/scsi_id/scsi_id.c +++ b/extras/scsi_id/scsi_id.c @@ -24,29 +24,37 @@ #include #include #include +#include #include -#include "../../udev.h" +#include "../../udev/udev.h" #include "scsi_id.h" -#include "scsi_id_version.h" -/* temporary names for mknod */ -#define TMP_DIR "/dev" -#define TMP_PREFIX "tmp-scsi" - -static const char short_options[] = "abd:f:gip:s:uvVx"; +static const struct option options[] = { + { "device", required_argument, NULL, 'd' }, + { "config", required_argument, NULL, 'f' }, + { "page", required_argument, NULL, 'p' }, + { "blacklisted", no_argument, NULL, 'b' }, + { "whitelisted", no_argument, NULL, 'g' }, + { "replace-whitespace", no_argument, NULL, 'u' }, + { "sg-version", required_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { "version", no_argument, NULL, 'V' }, + { "export", no_argument, NULL, 'x' }, + { "help", no_argument, NULL, 'h' }, + {} +}; + +static const char short_options[] = "d:f:ghip:uvVx"; static const char dev_short_options[] = "bgp:"; static int all_good; -static int always_info; static int dev_specified; -static int sys_specified; -static char config_file[MAX_PATH_LEN] = SCSI_ID_CONFIG_FILE; -static int display_bus_id; +static char config_file[MAX_PATH_LEN] = SYSCONFDIR "/scsi_id.config"; static enum page_code default_page_code; +static int sg_version = 4; static int use_stderr; static int debug; -static int hotplug_mode; static int reformat_serial; static int export; static char vendor_str[64]; @@ -54,64 +62,14 @@ static char model_str[64]; static char revision_str[16]; static char type_str[16]; -#ifdef USE_LOG -void log_message(int priority, const char *format, ...) +static void log_fn(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) { - va_list args; - static int udev_log = -1; - - if (udev_log == -1) { - const char *value; - - value = getenv("UDEV_LOG"); - if (value) - udev_log = log_priority(value); - else - udev_log = LOG_ERR; - } - - if (priority > udev_log) - return; - - va_start(args, format); vsyslog(priority, format, args); - va_end(args); -} -#endif - -static void set_str(char *to, const char *from, size_t count) -{ - size_t i, j, len; - - /* strip trailing whitespace */ - len = strnlen(from, count); - while (len && isspace(from[len-1])) - len--; - - /* strip leading whitespace */ - i = 0; - while (isspace(from[i]) && (i < len)) - i++; - - j = 0; - while (i < len) { - /* substitute multiple whitespace */ - if (isspace(from[i])) { - while (isspace(from[i])) - i++; - to[j++] = '_'; - } - /* skip chars */ - if (from[i] == '/') { - i++; - continue; - } - to[j++] = from[i++]; - } - to[j] = '\0'; } -static void set_type(char *to, const char *from, size_t len) +static void set_type(const char *from, char *to, size_t len) { int type_num; char *eptr; @@ -145,37 +103,7 @@ static void set_type(char *to, const char *from, size_t len) break; } } - strncpy(to, type, len); - to[len-1] = '\0'; -} - -static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type) -{ - unsigned int maj, min; - const char *attr; - - dbg("%s", devpath); - attr = sysfs_attr_get_value(devpath, "dev"); - if (attr == NULL) { - dbg("%s: could not get dev attribute: %s", devpath, strerror(errno)); - return -1; - } - - dbg("dev value %s", attr); - if (sscanf(attr, "%u:%u", &maj, &min) != 2) { - err("%s: invalid dev major/minor", devpath); - return -1; - } - - snprintf(tmpdev, MAX_PATH_LEN, "%s/%s-maj%d-min%d-%u", - TMP_DIR, TMP_PREFIX, maj, min, getpid()); - - dbg("tmpdev '%s'", tmpdev); - if (mknod(tmpdev, 0600 | dev_type, makedev(maj, min))) { - err("mknod failed: %s", strerror(errno)); - return -1; - } - return 0; + util_strlcpy(to, type, len); } /* @@ -234,7 +162,8 @@ static int argc_count(char *opts) * * vendor and model can end in '\n'. */ -static int get_file_options(const char *vendor, const char *model, +static int get_file_options(struct udev *udev, + const char *vendor, const char *model, int *argc, char ***newargv) { char *buffer; @@ -246,14 +175,14 @@ static int get_file_options(const char *vendor, const char *model, int c; int retval = 0; - dbg("vendor='%s'; model='%s'\n", vendor, model); + dbg(udev, "vendor='%s'; model='%s'\n", vendor, model); fd = fopen(config_file, "r"); if (fd == NULL) { - dbg("can't open %s\n", config_file); + dbg(udev, "can't open %s\n", config_file); if (errno == ENOENT) { return 1; } else { - err("can't open %s: %s", config_file, strerror(errno)); + err(udev, "can't open %s: %s\n", config_file, strerror(errno)); return -1; } } @@ -265,7 +194,7 @@ static int get_file_options(const char *vendor, const char *model, */ buffer = malloc(MAX_BUFFER_LEN); if (!buffer) { - err("Can't allocate memory."); + err(udev, "can't allocate memory\n"); return -1; } @@ -279,7 +208,7 @@ static int get_file_options(const char *vendor, const char *model, break; lineno++; if (buf[strlen(buffer) - 1] != '\n') { - info("Config file line %d too long.\n", lineno); + err(udev, "Config file line %d too long\n", lineno); break; } @@ -294,7 +223,7 @@ static int get_file_options(const char *vendor, const char *model, if (*buf == '#') continue; - dbg("lineno %d: '%s'\n", lineno, buf); + dbg(udev, "lineno %d: '%s'\n", lineno, buf); str1 = strsep(&buf, "="); if (str1 && strcasecmp(str1, "VENDOR") == 0) { str1 = get_value(&buf); @@ -324,20 +253,20 @@ static int get_file_options(const char *vendor, const char *model, } options_in = str1; } - dbg("config file line %d:" + dbg(udev, "config file line %d:\n" " vendor '%s'; model '%s'; options '%s'\n", lineno, vendor_in, model_in, options_in); /* * Only allow: [vendor=foo[,model=bar]]options=stuff */ if (!options_in || (!vendor_in && model_in)) { - info("Error parsing config file line %d '%s'", lineno, buffer); + err(udev, "Error parsing config file line %d '%s'\n", lineno, buffer); retval = -1; break; } if (vendor == NULL) { if (vendor_in == NULL) { - dbg("matched global option\n"); + dbg(udev, "matched global option\n"); break; } } else if ((vendor_in && strncmp(vendor, vendor_in, @@ -351,10 +280,10 @@ static int get_file_options(const char *vendor, const char *model, * give a partial match (that is FOO * matches FOOBAR). */ - dbg("matched vendor/model\n"); + dbg(udev, "matched vendor/model\n"); break; } else { - dbg("no match\n"); + dbg(udev, "no match\n"); } } @@ -369,7 +298,7 @@ static int get_file_options(const char *vendor, const char *model, c = argc_count(buffer) + 2; *newargv = calloc(c, sizeof(**newargv)); if (!*newargv) { - err("Can't allocate memory."); + err(udev, "can't allocate memory\n"); retval = -1; } else { *argc = c; @@ -377,11 +306,11 @@ static int get_file_options(const char *vendor, const char *model, /* * argv[0] at 0 is skipped by getopt, but * store the buffer address there for - * alter freeing. + * later freeing */ (*newargv)[c] = buffer; for (c = 1; c < *argc; c++) - (*newargv)[c] = strsep(&buffer, " "); + (*newargv)[c] = strsep(&buffer, " \t"); } } else { /* No matches */ @@ -394,8 +323,9 @@ static int get_file_options(const char *vendor, const char *model, return retval; } -static int set_options(int argc, char **argv, const char *short_opts, - char *target, char *maj_min_dev) +static int set_options(struct udev *udev, + int argc, char **argv, const char *short_opts, + char *maj_min_dev) { int option; @@ -406,27 +336,23 @@ static int set_options(int argc, char **argv, const char *short_opts, */ optind = 1; while (1) { - option = getopt(argc, argv, short_opts); + option = getopt_long(argc, argv, short_opts, options, NULL); if (option == -1) break; if (optarg) - dbg("option '%c' arg '%s'\n", option, optarg); + dbg(udev, "option '%c' arg '%s'\n", option, optarg); else - dbg("option '%c'\n", option); + dbg(udev, "option '%c'\n", option); switch (option) { - case 'a': - always_info = 1; - break; case 'b': all_good = 0; break; case 'd': dev_specified = 1; - strncpy(maj_min_dev, optarg, MAX_PATH_LEN); - maj_min_dev[MAX_PATH_LEN-1] = '\0'; + util_strlcpy(maj_min_dev, optarg, MAX_PATH_LEN); break; case 'e': @@ -434,17 +360,27 @@ static int set_options(int argc, char **argv, const char *short_opts, break; case 'f': - strncpy(config_file, optarg, MAX_PATH_LEN); - config_file[MAX_PATH_LEN-1] = '\0'; + util_strlcpy(config_file, optarg, MAX_PATH_LEN); break; case 'g': all_good = 1; break; - case 'i': - display_bus_id = 1; - break; + case 'h': + printf("Usage: scsi_id OPTIONS \n" + " --device= device node for SG_IO commands\n" + " --config= location of config file\n" + " --page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n" + " --sg-version=3|4 use SGv3 or SGv4\n" + " --blacklisted threat device as blacklisted\n" + " --whitelisted threat device as whitelisted\n" + " --replace-whitespace replace all whitespaces by underscores\n" + " --verbose verbose logging\n" + " --version print version\n" + " --export print values as environment keys\n" + " --help print this help text\n\n"); + exit(0); case 'p': if (strcmp(optarg, "0x80") == 0) { @@ -454,15 +390,17 @@ static int set_options(int argc, char **argv, const char *short_opts, } else if (strcmp(optarg, "pre-spc3-83") == 0) { default_page_code = PAGE_83_PRE_SPC3; } else { - info("Unknown page code '%s'", optarg); + err(udev, "Unknown page code '%s'\n", optarg); return -1; } break; case 's': - sys_specified = 1; - strncpy(target, optarg, MAX_PATH_LEN); - target[MAX_PATH_LEN-1] = '\0'; + sg_version = atoi(optarg); + if (sg_version < 3 || sg_version > 4) { + err(udev, "Unknown SG version '%s'\n", optarg); + return -1; + } break; case 'u': @@ -478,69 +416,44 @@ static int set_options(int argc, char **argv, const char *short_opts, break; case 'V': - info("scsi_id version: %s\n", SCSI_ID_VERSION); + printf("%s\n", VERSION); exit(0); break; default: - info("Unknown or bad option '%c' (0x%x)", option, option); - return -1; + exit(1); } } + if (optind < argc && !dev_specified) { + dev_specified = 1; + util_strlcpy(maj_min_dev, argv[optind], MAX_PATH_LEN); + } return 0; } -static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *page_code) +static int per_dev_options(struct udev *udev, + struct scsi_id_device *dev_scsi, int *good_bad, int *page_code) { int retval; int newargc; char **newargv = NULL; - const char *vendor, *model, *type; int option; *good_bad = all_good; *page_code = default_page_code; - vendor = sysfs_attr_get_value(dev_scsi->devpath, "vendor"); - if (!vendor) { - info("%s: cannot get vendor attribute", dev_scsi->devpath); - return -1; - } - set_str(vendor_str, vendor, sizeof(vendor_str)-1); - - model = sysfs_attr_get_value(dev_scsi->devpath, "model"); - if (!model) { - info("%s: cannot get model attribute\n", dev_scsi->devpath); - return -1; - } - set_str(model_str, model, sizeof(model_str)-1); - - type = sysfs_attr_get_value(dev_scsi->devpath, "type"); - if (!type) { - info("%s: cannot get type attribute", dev_scsi->devpath); - return -1; - } - set_type(type_str, type, sizeof(type_str)); - - type = sysfs_attr_get_value(dev_scsi->devpath, "rev"); - if (!type) { - info("%s: cannot get type attribute\n", dev_scsi->devpath); - return -1; - } - set_str(revision_str, type, sizeof(revision_str)-1); - - retval = get_file_options(vendor, model, &newargc, &newargv); + retval = get_file_options(udev, vendor_str, model_str, &newargc, &newargv); optind = 1; /* reset this global extern */ while (retval == 0) { - option = getopt(newargc, newargv, dev_short_options); + option = getopt_long(newargc, newargv, dev_short_options, options, NULL); if (option == -1) break; if (optarg) - dbg("option '%c' arg '%s'\n", option, optarg); + dbg(udev, "option '%c' arg '%s'\n", option, optarg); else - dbg("option '%c'\n", option); + dbg(udev, "option '%c'\n", option); switch (option) { case 'b': @@ -559,13 +472,13 @@ static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *pa } else if (strcmp(optarg, "pre-spc3-83") == 0) { *page_code = PAGE_83_PRE_SPC3; } else { - info("Unknown page code '%s'", optarg); + err(udev, "Unknown page code '%s'\n", optarg); retval = -1; } break; default: - info("Unknown or bad option '%c' (0x%x)", option, option); + err(udev, "Unknown or bad option '%c' (0x%x)\n", option, option); retval = -1; break; } @@ -578,6 +491,26 @@ static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *pa return retval; } +static int set_inq_values(struct udev *udev, struct scsi_id_device *dev_scsi, const char *path) +{ + int retval; + + dev_scsi->use_sg = sg_version; + + retval = scsi_std_inquiry(udev, dev_scsi, path); + if (retval) + return retval; + + udev_util_replace_whitespace(dev_scsi->vendor, vendor_str, sizeof(vendor_str)); + udev_util_replace_chars(vendor_str, NULL); + udev_util_replace_whitespace(dev_scsi->model, model_str, sizeof(model_str)); + udev_util_replace_chars(model_str, NULL); + set_type(dev_scsi->type, type_str, sizeof(type_str)); + udev_util_replace_whitespace(dev_scsi->revision, revision_str, sizeof(revision_str)); + udev_util_replace_chars(revision_str, NULL); + return 0; +} + /* * format_serial: replace to whitespaces by underscores for calling * programs that use the serial for device naming (multipath, Suse @@ -605,134 +538,88 @@ static void format_serial(char *serial) /* * scsi_id: try to get an id, if one is found, printf it to stdout. - * returns a value passed to exit() - 0 if printed an id, else 1. This - * could be expanded, for example, if we want to report a failure like no - * memory etc. return 2, and return 1 for expected cases (like broken - * device found) that do not print an id. + * returns a value passed to exit() - 0 if printed an id, else 1. */ -static int scsi_id(const char *devpath, char *maj_min_dev) +static int scsi_id(struct udev *udev, char *maj_min_dev) { - int retval; - int dev_type = 0; - char *serial, *unaligned_buf; - struct sysfs_device *dev; - struct sysfs_device *dev_scsi; + struct scsi_id_device dev_scsi; int good_dev; int page_code; + int retval = 0; - dbg("devpath %s\n", devpath); - - dev = sysfs_device_get(devpath); - if (dev == NULL) { - err("unable to access '%s'", devpath); - return 1; - } - - if (strcmp(dev->subsystem, "block") == 0) - dev_type = S_IFBLK; - else - dev_type = S_IFCHR; - - /* get scsi parent device */ - dev_scsi = sysfs_device_get_parent_with_subsystem(dev, "scsi"); - if (dev_scsi == NULL) { - err("unable to access parent device of '%s'", devpath); - return 1; - } + memset(&dev_scsi, 0x00, sizeof(struct scsi_id_device)); - /* mknod a temp dev to communicate with the device */ - if (!dev_specified && create_tmp_dev(dev->devpath, maj_min_dev, dev_type)) { - dbg("create_tmp_dev failed\n"); - return 1; + if (set_inq_values(udev, &dev_scsi, maj_min_dev) < 0) { + retval = 1; + goto out; } /* get per device (vendor + model) options from the config file */ - retval = per_dev_options(dev_scsi, &good_dev, &page_code); - dbg("per dev options: good %d; page code 0x%x", good_dev, page_code); - -#define ALIGN 512 - unaligned_buf = malloc(MAX_SERIAL_LEN + ALIGN); - serial = (char*) (((unsigned long) unaligned_buf + (ALIGN - 1)) - & ~(ALIGN - 1)); - dbg("buffer unaligned 0x%p; aligned 0x%p\n", unaligned_buf, serial); -#undef ALIGN - + per_dev_options(udev, &dev_scsi, &good_dev, &page_code); + dbg(udev, "per dev options: good %d; page code 0x%x\n", good_dev, page_code); if (!good_dev) { retval = 1; - } else if (scsi_get_serial(dev_scsi, maj_min_dev, page_code, - serial, MAX_SERIAL_LEN)) { - retval = always_info?0:1; - } else { - retval = 0; + goto out; } - if (!retval) { - if (export) { - static char serial_str[64]; - printf("ID_VENDOR=%s\n", vendor_str); - printf("ID_MODEL=%s\n", model_str); - printf("ID_REVISION=%s\n", revision_str); - set_str(serial_str, serial, sizeof(serial_str)); + + /* read serial number from mode pages (no values for optical drives) */ + scsi_get_serial(udev, &dev_scsi, maj_min_dev, page_code, MAX_SERIAL_LEN); + + if (export) { + char serial_str[MAX_SERIAL_LEN]; + + printf("ID_VENDOR=%s\n", vendor_str); + printf("ID_MODEL=%s\n", model_str); + printf("ID_REVISION=%s\n", revision_str); + printf("ID_TYPE=%s\n", type_str); + if (dev_scsi.serial[0] != '\0') { + udev_util_replace_whitespace(dev_scsi.serial, serial_str, sizeof(serial_str)); + udev_util_replace_chars(serial_str, NULL); printf("ID_SERIAL=%s\n", serial_str); - printf("ID_TYPE=%s\n", type_str); - printf("ID_BUS=scsi\n"); - } else { - if (reformat_serial) - format_serial(serial); - if (display_bus_id) - printf("%s: ", dev_scsi->kernel_name); - printf("%s\n", serial); + udev_util_replace_whitespace(dev_scsi.serial_short, serial_str, sizeof(serial_str)); + udev_util_replace_chars(serial_str, NULL); + printf("ID_SERIAL_SHORT=%s\n", serial_str); } - dbg("%s\n", serial); - retval = 0; + goto out; } - if (!dev_specified) - unlink(maj_min_dev); - + if (dev_scsi.serial[0] == '\0') { + retval = 1; + goto out; + } + if (reformat_serial) + format_serial(dev_scsi.serial); + printf("%s\n", dev_scsi.serial); +out: return retval; } int main(int argc, char **argv) { + struct udev *udev; int retval = 0; - char devpath[MAX_PATH_LEN]; char maj_min_dev[MAX_PATH_LEN]; int newargc; - const char *env; char **newargv; + udev = udev_new(); + if (udev == NULL) + goto exit; + logging_init("scsi_id"); - sysfs_init(); - dbg("argc is %d\n", argc); - - /* sysfs path can be overridden for testing */ - env = getenv("SYSFS_PATH"); - if (env) { - strncpy(sysfs_path, env, sizeof(sysfs_path)); - sysfs_path[sizeof(sysfs_path)-1] = '\0'; - } else - strcpy(sysfs_path, "/sys"); - - env = getenv("DEVPATH"); - if (env) { - hotplug_mode = 1; - sys_specified = 1; - strncpy(devpath, env, MAX_PATH_LEN); - devpath[sizeof(devpath)-1] = '\0'; - } + udev_set_log_fn(udev, log_fn); /* * Get config file options. */ newargv = NULL; - retval = get_file_options(NULL, NULL, &newargc, &newargv); + retval = get_file_options(udev, NULL, NULL, &newargc, &newargv); if (retval < 0) { retval = 1; goto exit; } if (newargv && (retval == 0)) { - if (set_options(newargc, newargv, short_options, devpath, - maj_min_dev) < 0) { + if (set_options(udev, newargc, newargv, short_options, maj_min_dev) < 0) { retval = 2; goto exit; } @@ -740,22 +627,21 @@ int main(int argc, char **argv) } /* - * Get command line options (overriding any config file or DEVPATH - * settings). + * Get command line options (overriding any config file settings). */ - if (set_options(argc, argv, short_options, devpath, maj_min_dev) < 0) + if (set_options(udev, argc, argv, short_options, maj_min_dev) < 0) exit(1); - if (!sys_specified) { - info("-s must be specified\n"); + if (!dev_specified) { + err(udev, "no device specified\n"); retval = 1; goto exit; } - retval = scsi_id(devpath, maj_min_dev); + retval = scsi_id(udev, maj_min_dev); exit: - sysfs_cleanup(); + udev_unref(udev); logging_close(); return retval; }