X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=extras%2Fscsi_id%2Fscsi_id.c;h=2ffc95fbfed69605ad8caf15315f6a8b5857b703;hp=d34d9284e50d2d06b22622d4043f358a92f28988;hb=3d94fb8742847afe8396f6dd38b16ff4e836611d;hpb=c521693b54956c1f2dd0c0947c819b8570f6edaa diff --git a/extras/scsi_id/scsi_id.c b/extras/scsi_id/scsi_id.c index d34d9284e..2ffc95fbf 100644 --- a/extras/scsi_id/scsi_id.c +++ b/extras/scsi_id/scsi_id.c @@ -4,26 +4,18 @@ * Main section of the scsi_id program * * Copyright (C) IBM Corp. 2003 + * Copyright (C) SUSE Linux Products GmbH, 2006 * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of the - * License, or (at your option) any later version. + * Author: + * Patrick Mansfield * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * 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 version 2 of the License. */ #include #include -#include #include #include #include @@ -33,209 +25,159 @@ #include #include #include -#include + +#include "../../udev.h" #include "scsi_id.h" +#include "scsi_id_version.h" -#ifndef VERSION -#warning No version -#define VERSION "unknown" -#endif +/* temporary names for mknod */ +#define TMP_DIR "/dev" +#define TMP_PREFIX "tmp-scsi" -/* - * temporary names for mknod. - */ -#define TMP_DIR "/tmp" -#define TMP_PREFIX "scsi" - -#define CONFIG_FILE "/etc/scsi_id.config" - -#define MAX_NAME_LEN 72 - -#define MAX_SERIAL_LEN 128 - -static const char short_options[] = "bc:d:ef:gip:s:vV"; -static const struct option long_options[] = { - {"broken", no_argument, NULL, 'b'}, /* also per dev */ - {"callout", required_argument, NULL, 'c'}, /* also per dev */ - {"device", required_argument, NULL, 'd'}, - {"stderr", no_argument, NULL, 'e'}, - {"file", required_argument, NULL, 'f'}, - {"good", no_argument, NULL, 'g'}, /* also per dev */ - {"busid", no_argument, NULL, 'i'}, /* also per dev */ - {"page", required_argument, NULL, 'p'}, /* also per dev */ - {"devpath", required_argument, NULL, 's'}, - {"verbose", no_argument, NULL, 'v'}, - {"version", no_argument, NULL, 'V'}, - {0, 0, 0, 0} -}; -/* - * Just duplicate per dev options. - */ -static const char dev_short_options[] = "bc:gp:"; -static const struct option dev_long_options[] = { - {"broken", no_argument, NULL, 'b'}, /* also per dev */ - {"callout", required_argument, NULL, 'c'}, /* also per dev */ - {"good", no_argument, NULL, 'g'}, /* also per dev */ - {"page", required_argument, NULL, 'p'}, /* also per dev */ - {0, 0, 0, 0} -}; - -char sysfs_mnt_path[SYSFS_PATH_MAX]; +static const char short_options[] = "abd:f:gip:s:uvVx"; +static const char dev_short_options[] = "bgp:"; static int all_good; -static char *default_callout; +static int always_info; static int dev_specified; static int sys_specified; -static char config_file[MAX_NAME_LEN] = CONFIG_FILE; +static char config_file[MAX_PATH_LEN] = SCSI_ID_CONFIG_FILE; static int display_bus_id; -static int default_page_code; +static enum page_code default_page_code; static int use_stderr; static int debug; static int hotplug_mode; - -void log_message (int level, const char *format, ...) +static int reformat_serial; +static int export; +static char vendor_str[64]; +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, ...) { - va_list args; - - if (!debug && level == LOG_DEBUG) - return; + va_list args; + static int udev_log = -1; - va_start (args, format); - if (!hotplug_mode || use_stderr) { - vfprintf(stderr, format, args); - } else { - static int logging_init = 0; - if (!logging_init) { - openlog ("scsi_id", LOG_PID, LOG_DAEMON); - logging_init = 1; - } + if (udev_log == -1) { + const char *value; - vsyslog(level, format, args); + value = getenv("UDEV_LOG"); + if (value) + udev_log = log_priority(value); + else + udev_log = LOG_ERR; } - va_end (args); - return; + + if (priority > udev_log) + return; + + va_start(args, format); + vsyslog(priority, format, args); + va_end(args); } +#endif -static int sysfs_get_actual_dev(const char *sysfs_path, char *dev, int len) +static void set_str(char *to, const char *from, size_t count) { - dprintf("%s\n", sysfs_path); - strncpy(dev, sysfs_path, len); - strncat(dev, "/device", len); - if (sysfs_get_link(dev, dev, len)) { - if (!hotplug_mode) - log_message(LOG_WARNING, "%s: %s\n", dev, - strerror(errno)); - return -1; + 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++]; } - return 0; + to[j] = '\0'; } -/* - * sysfs_is_bus: Given the sysfs_path to a device, return 1 if sysfs_path - * is on bus, 0 if not on bus, and < 0 on error - */ -static int sysfs_is_bus(const char *sysfs_path, const char *bus) +static void set_type(char *to, const char *from, size_t len) { - char bus_dev_name[SYSFS_PATH_MAX]; - char bus_id[SYSFS_NAME_LEN]; - struct stat stat_buf; - ino_t dev_inode; - - dprintf("%s\n", sysfs_path); - - if (sysfs_get_name_from_path(sysfs_path, bus_id, SYSFS_NAME_LEN)) - return -1; - - snprintf(bus_dev_name, MAX_NAME_LEN, "%s/%s/%s/%s/%s", sysfs_mnt_path, - SYSFS_BUS_DIR, bus, SYSFS_DEVICES_NAME, bus_id); - - if (stat(sysfs_path, &stat_buf)) - return -1; - dev_inode = stat_buf.st_ino; - - if (stat(bus_dev_name, &stat_buf)) { - if (errno == ENOENT) - return 0; - else - return -1; + int type_num; + char *eptr; + char *type = "generic"; + + type_num = strtoul(from, &eptr, 0); + if (eptr != from) { + switch (type_num) { + case 0: + type = "disk"; + break; + case 1: + type = "tape"; + break; + case 4: + type = "optical"; + break; + case 5: + type = "cd"; + break; + case 7: + type = "optical"; + break; + case 0xe: + type = "disk"; + break; + case 0xf: + type = "optical"; + break; + default: + break; + } } - if (dev_inode == stat_buf.st_ino) - return 1; - else - return 0; + strncpy(to, type, len); + to[len-1] = '\0'; } -static int get_major_minor(const char *devpath, int *major, int *minor) +static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type) { - struct sysfs_class_device *class_dev; - char dev_value[SYSFS_NAME_LEN]; - char *dev; - - dprintf("%s\n", devpath); - class_dev = sysfs_open_class_device(devpath); - if (!class_dev) { - log_message(LOG_WARNING, "open class %s failed: %s\n", devpath, - strerror(errno)); - return -1; - } + unsigned int maj, min; + const char *attr; - dev = sysfs_get_attr(class_dev, "dev"); - if (dev) - strncpy(dev_value, dev, SYSFS_NAME_LEN); - sysfs_close_class_device(class_dev); - if (!dev) { - /* - * XXX This happens a lot, since sg has no dev attr. - * Someday change this back to a LOG_WARNING. - */ - log_message(LOG_DEBUG, "%s could not get dev attribute: %s\n", - devpath, strerror(errno)); + 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; } - dev = NULL; - dprintf("dev %s", dev_value); /* dev_value has a trailing \n */ - if (sscanf(dev_value, "%u:%u", major, minor) != 2) { - log_message(LOG_WARNING, "%s: invalid dev major/minor\n", - devpath); + dbg("dev value %s", attr); + if (sscanf(attr, "%u:%u", &maj, &min) != 2) { + err("%s: invalid dev major/minor", devpath); return -1; } - return 0; -} - -static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type) -{ - int major, minor; - - dprintf("(%s)\n", devpath); - - if (get_major_minor(devpath, &major, &minor)) - return -1; - snprintf(tmpdev, MAX_NAME_LEN, "%s/%s-maj%d-min%d-%u", - TMP_DIR, TMP_PREFIX, major, minor, getpid()); - - dprintf("tmpdev '%s'\n", tmpdev); + snprintf(tmpdev, MAX_PATH_LEN, "%s/%s-maj%d-min%d-%u", + TMP_DIR, TMP_PREFIX, maj, min, getpid()); - if (mknod(tmpdev, 0600 | dev_type, makedev(major, minor))) { - log_message(LOG_WARNING, "mknod failed: %s\n", strerror(errno)); + dbg("tmpdev '%s'", tmpdev); + if (mknod(tmpdev, 0600 | dev_type, makedev(maj, min))) { + err("mknod failed: %s", strerror(errno)); return -1; } return 0; } -static int has_sysfs_prefix(const char *path, const char *prefix) -{ - char match[MAX_NAME_LEN]; - - strncpy(match, sysfs_mnt_path, MAX_NAME_LEN); - strncat(match, prefix, MAX_NAME_LEN); - if (strncmp(path, match, strlen(match)) == 0) - return 1; - else - return 0; -} - /* * get_value: * @@ -292,10 +234,10 @@ static int argc_count(char *opts) * * vendor and model can end in '\n'. */ -static int get_file_options(char *vendor, char *model, int *argc, - char ***newargv) +static int get_file_options(const char *vendor, const char *model, + int *argc, char ***newargv) { - char buffer[256]; + char *buffer; FILE *fd; char *buf; char *str1; @@ -303,50 +245,56 @@ static int get_file_options(char *vendor, char *model, int *argc, int lineno; int c; int retval = 0; - static char *prog_string = "arg0"; - dprintf("vendor='%s'; model='%s'\n", vendor, model); + dbg("vendor='%s'; model='%s'\n", vendor, model); fd = fopen(config_file, "r"); if (fd == NULL) { - dprintf("can't open %s\n", config_file); + dbg("can't open %s\n", config_file); if (errno == ENOENT) { return 1; } else { - log_message(LOG_WARNING, "can't open %s: %s\n", - config_file, strerror(errno)); + err("can't open %s: %s", config_file, strerror(errno)); return -1; } } + /* + * Allocate a buffer rather than put it on the stack so we can + * keep it around to parse any options (any allocated newargv + * points into this buffer for its strings). + */ + buffer = malloc(MAX_BUFFER_LEN); + if (!buffer) { + err("Can't allocate memory."); + return -1; + } + *newargv = NULL; lineno = 0; - while (1) { vendor_in = model_in = options_in = NULL; - buf = fgets(buffer, sizeof(buffer), fd); + buf = fgets(buffer, MAX_BUFFER_LEN, fd); if (buf == NULL) break; lineno++; + if (buf[strlen(buffer) - 1] != '\n') { + info("Config file line %d too long.\n", lineno); + break; + } while (isspace(*buf)) buf++; + /* blank or all whitespace line */ if (*buf == '\0') - /* - * blank or all whitespace line - */ continue; + /* comment line */ if (*buf == '#') - /* - * comment line - */ continue; -#ifdef LOTS - dprintf("lineno %d: '%s'\n", lineno, buf); -#endif + dbg("lineno %d: '%s'\n", lineno, buf); str1 = strsep(&buf, "="); if (str1 && strcasecmp(str1, "VENDOR") == 0) { str1 = get_value(&buf); @@ -376,22 +324,20 @@ static int get_file_options(char *vendor, char *model, int *argc, } options_in = str1; } - dprintf("config file line %d:" + dbg("config file line %d:" " 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)) { - log_message(LOG_WARNING, - "Error parsing config file line %d '%s'\n", - lineno, buffer); + info("Error parsing config file line %d '%s'", lineno, buffer); retval = -1; break; } if (vendor == NULL) { if (vendor_in == NULL) { - dprintf("matched global option\n"); + dbg("matched global option\n"); break; } } else if ((vendor_in && strncmp(vendor, vendor_in, @@ -405,10 +351,10 @@ static int get_file_options(char *vendor, char *model, int *argc, * give a partial match (that is FOO * matches FOOBAR). */ - dprintf("matched vendor/model\n"); + dbg("matched vendor/model\n"); break; } else { - dprintf("no match\n"); + dbg("no match\n"); } } @@ -419,66 +365,69 @@ static int get_file_options(char *vendor, char *model, int *argc, * Something matched. Allocate newargv, and store * values found in options_in. */ - c = argc_count(options_in) + 2; + strcpy(buffer, options_in); + c = argc_count(buffer) + 2; *newargv = calloc(c, sizeof(**newargv)); if (!*newargv) { - log_message(LOG_WARNING, - "Can't allocate memory\n"); + err("Can't allocate memory."); retval = -1; } else { *argc = c; c = 0; - (*newargv)[c] = prog_string; /* nothing */ + /* + * argv[0] at 0 is skipped by getopt, but + * store the buffer address there for + * alter freeing. + */ + (*newargv)[c] = buffer; for (c = 1; c < *argc; c++) - (*newargv)[c] = strsep(&options_in, " "); + (*newargv)[c] = strsep(&buffer, " "); } } else { - /* - * No matches. - */ + /* No matches */ retval = 1; } } + if (retval != 0) + free(buffer); fclose(fd); return retval; } static int set_options(int argc, char **argv, const char *short_opts, - const struct option *long_opts, char *target, - char *maj_min_dev) + char *target, char *maj_min_dev) { int option; - int option_ind; /* - * optind is a global extern used by getopt_long. Since we can - * call set_options twice (once for command line, and once for - * config file) we have to reset this back to 0. + * optind is a global extern used by getopt. Since we can call + * set_options twice (once for command line, and once for config + * file) we have to reset this back to 1. [Note glibc handles + * setting this to 0, but klibc does not.] */ - optind = 0; + optind = 1; while (1) { - option = getopt_long(argc, argv, short_options, long_options, - &option_ind); + option = getopt(argc, argv, short_opts); if (option == -1) break; if (optarg) - dprintf("option '%c' arg '%s'\n", option, optarg); + dbg("option '%c' arg '%s'\n", option, optarg); else - dprintf("option '%c'\n", option); + dbg("option '%c'\n", option); switch (option) { + case 'a': + always_info = 1; + break; case 'b': all_good = 0; break; - case 'c': - default_callout = optarg; - break; - case 'd': dev_specified = 1; - strncpy(maj_min_dev, optarg, MAX_NAME_LEN); + strncpy(maj_min_dev, optarg, MAX_PATH_LEN); + maj_min_dev[MAX_PATH_LEN-1] = '\0'; break; case 'e': @@ -486,7 +435,8 @@ static int set_options(int argc, char **argv, const char *short_opts, break; case 'f': - strncpy(config_file, optarg, MAX_NAME_LEN); + strncpy(config_file, optarg, MAX_PATH_LEN); + config_file[MAX_PATH_LEN-1] = '\0'; break; case 'g': @@ -499,20 +449,29 @@ static int set_options(int argc, char **argv, const char *short_opts, case 'p': if (strcmp(optarg, "0x80") == 0) { - default_page_code = 0x80; + default_page_code = PAGE_80; } else if (strcmp(optarg, "0x83") == 0) { - default_page_code = 0x83; + default_page_code = PAGE_83; + } else if (strcmp(optarg, "pre-spc3-83") == 0) { + default_page_code = PAGE_83_PRE_SPC3; } else { - log_message(LOG_WARNING, - "Unknown page code '%s'\n", optarg); + info("Unknown page code '%s'", optarg); return -1; } break; case 's': sys_specified = 1; - strncpy(target, sysfs_mnt_path, MAX_NAME_LEN); - strncat(target, optarg, MAX_NAME_LEN); + strncpy(target, optarg, MAX_PATH_LEN); + target[MAX_PATH_LEN-1] = '\0'; + break; + + case 'u': + reformat_serial = 1; + break; + + case 'x': + export = 1; break; case 'v': @@ -520,107 +479,131 @@ static int set_options(int argc, char **argv, const char *short_opts, break; case 'V': - log_message(LOG_WARNING, "scsi_id version: %s\n", - VERSION); + info("scsi_id version: %s\n", SCSI_ID_VERSION); exit(0); break; default: - log_message(LOG_WARNING, - "Unknown or bad option '%c' (0x%x)\n", - option, option); + info("Unknown or bad option '%c' (0x%x)", option, option); return -1; } } return 0; } -static int per_dev_options(struct sysfs_class_device *scsi_dev, int *good_bad, - int *page_code, char *callout) +static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *page_code) { int retval; int newargc; char **newargv = NULL; - char *vendor; - char *model; + const char *vendor, *model, *type; int option; - int option_ind; - *good_bad = all_good; *page_code = default_page_code; - if (default_callout && (callout != default_callout)) - strncpy(callout, default_callout, MAX_NAME_LEN); - else - callout[0] = '\0'; - vendor = sysfs_get_attr(scsi_dev, "vendor"); + vendor = sysfs_attr_get_value(dev_scsi->devpath, "vendor"); if (!vendor) { - log_message(LOG_WARNING, "%s: no vendor attribute\n", - scsi_dev->name); + info("%s: cannot get vendor attribute", dev_scsi->devpath); return -1; } + set_str(vendor_str, vendor, sizeof(vendor_str)-1); - model = sysfs_get_attr(scsi_dev, "model"); - if (!vendor) { - log_message(LOG_WARNING, "%s: no model attribute\n", - scsi_dev->name); + 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); - optind = 0; /* global extern, reset to 0 */ + optind = 1; /* reset this global extern */ while (retval == 0) { - option = getopt_long(newargc, newargv, dev_short_options, - dev_long_options, &option_ind); + option = getopt(newargc, newargv, dev_short_options); if (option == -1) break; if (optarg) - dprintf("option '%c' arg '%s'\n", option, optarg); + dbg("option '%c' arg '%s'\n", option, optarg); else - dprintf("option '%c'\n", option); + dbg("option '%c'\n", option); switch (option) { case 'b': *good_bad = 0; break; - case 'c': - strncpy(callout, default_callout, MAX_NAME_LEN); - break; - case 'g': *good_bad = 1; break; case 'p': if (strcmp(optarg, "0x80") == 0) { - *page_code = 0x80; + *page_code = PAGE_80; } else if (strcmp(optarg, "0x83") == 0) { - *page_code = 0x83; + *page_code = PAGE_83; + } else if (strcmp(optarg, "pre-spc3-83") == 0) { + *page_code = PAGE_83_PRE_SPC3; } else { - log_message(LOG_WARNING, - "Unknown page code '%s'\n", optarg); + info("Unknown page code '%s'", optarg); retval = -1; } break; default: - log_message(LOG_WARNING, - "Unknown or bad option '%c' (0x%x)\n", - option, option); + info("Unknown or bad option '%c' (0x%x)", option, option); retval = -1; break; } } - if (newargv) + if (newargv) { + free(newargv[0]); free(newargv); + } return retval; } +/* + * format_serial: replace to whitespaces by underscores for calling + * programs that use the serial for device naming (multipath, Suse + * naming, etc...) + */ +static void format_serial(char *serial) +{ + char *p = serial, *q; + + q = p; + while (*p != '\0') { + if (isspace(*p)) { + if (q > serial && q[-1] != '_') { + *q = '_'; + q++; + } + } else { + *q = *p; + q++; + } + p++; + } + *q = '\0'; +} + /* * 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 @@ -628,127 +611,81 @@ static int per_dev_options(struct sysfs_class_device *scsi_dev, int *good_bad, * memory etc. return 2, and return 1 for expected cases (like broken * device found) that do not print an id. */ -static int scsi_id(const char *target_path, char *maj_min_dev) +static int scsi_id(const char *devpath, char *maj_min_dev) { int retval; int dev_type = 0; - char full_dev_path[MAX_NAME_LEN]; - char serial[MAX_SERIAL_LEN]; - struct sysfs_class_device *scsi_dev; /* of scsi_device full_dev_path */ + char *serial, *unaligned_buf; + struct sysfs_device *dev; + struct sysfs_device *dev_scsi; int good_dev; int page_code; - char callout[MAX_NAME_LEN]; - dprintf("target_path %s\n", target_path); + dbg("devpath %s\n", devpath); - /* - * Ugly: depend on the sysfs path to tell us whether this is a - * block or char device. This should probably be encoded in the - * "dev" along with the major/minor. - */ - if (has_sysfs_prefix(target_path, "/block")) { - dev_type = S_IFBLK; - } else if (has_sysfs_prefix(target_path, "/class")) { - dev_type = S_IFCHR; - } else { - if (!hotplug_mode) { - log_message(LOG_WARNING, - "Non block or class device '%s'\n", - target_path); - return 1; - } else { - /* - * Expected in some cases. - */ - dprintf("Non block or class device\n"); - return 0; - } - } - - if (sysfs_get_actual_dev(target_path, full_dev_path, MAX_NAME_LEN)) + dev = sysfs_device_get(devpath); + if (dev == NULL) { + err("unable to access '%s'", devpath); return 1; + } - dprintf("full_dev_path %s\n", full_dev_path); + if (strcmp(dev->subsystem, "block") == 0) + dev_type = S_IFBLK; + else + dev_type = S_IFCHR; - /* - * Allow only scsi devices (those that have a matching device - * under /bus/scsi/devices). - * - * Other block devices can support SG IO, but only ide-cd does, so - * for now, don't bother with anything else. - */ - retval = sysfs_is_bus(full_dev_path, "scsi"); - if (retval == 0) { - if (hotplug_mode) - /* - * Expected in some cases. - */ - dprintf("%s is not a scsi device\n", target_path); - else - log_message(LOG_WARNING, "%s is not a scsi device\n", - target_path); - return 1; - } else if (retval < 0) { - log_message(LOG_WARNING, "sysfs_is_bus failed: %s\n", - strerror(errno)); + /* 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; } - /* - * mknod a temp dev to communicate with the device. - */ - if (!dev_specified && create_tmp_dev(target_path, maj_min_dev, - dev_type)) { - dprintf("create_tmp_dev failed\n"); + /* 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; } - scsi_dev = sysfs_open_class_device(full_dev_path); - if (!scsi_dev) { - log_message(LOG_WARNING, "open class %s failed: %s\n", - full_dev_path, strerror(errno)); - return 1; - } + /* 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); - /* - * Get any per device (vendor + model) options from the config - * file. - */ - retval = per_dev_options(scsi_dev, &good_dev, &page_code, callout); - dprintf("per dev options: good %d; page code 0x%x; callout '%s'\n", - good_dev, page_code, callout); +#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 if (!good_dev) { retval = 1; - } else if (callout[0] != '\0') { - /* - * exec vendor callout, pass it only the "name" to be used - * for error messages, and the dev to open. - * - * This won't work if we need to pass on the original - * command line (when not hotplug mode) since the option - * parsing and per dev parsing modify the argv's. - * - * XXX Not implemented yet. And not fully tested ;-) - */ - retval = 1; - } else if (scsi_get_serial(scsi_dev, maj_min_dev, page_code, + } else if (scsi_get_serial(dev_scsi, maj_min_dev, page_code, serial, MAX_SERIAL_LEN)) { - retval = 1; + retval = always_info?0:1; } else { retval = 0; } if (!retval) { - if (display_bus_id) - printf("%s ", scsi_dev->name); - printf("%s", serial); - if (!hotplug_mode) - printf("\n"); - dprintf("%s\n", serial); + 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)); + 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); + } + dbg("%s\n", serial); retval = 0; } - fflush(stdout); - sysfs_close_class_device(scsi_dev); if (!dev_specified) unlink(maj_min_dev); @@ -758,70 +695,68 @@ static int scsi_id(const char *target_path, char *maj_min_dev) int main(int argc, char **argv) { - int retval; - char *devpath; - char target_path[MAX_NAME_LEN]; - char maj_min_dev[MAX_NAME_LEN]; + int retval = 0; + char devpath[MAX_PATH_LEN]; + char maj_min_dev[MAX_PATH_LEN]; int newargc; + const char *env; char **newargv; - if (getenv("DEBUG")) - debug++; + logging_init("scsi_id"); + sysfs_init(); + dbg("argc is %d\n", argc); - if ((argc == 2) && (argv[1][0] != '-')) { - hotplug_mode = 1; - dprintf("hotplug assumed\n"); - } + /* 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"); - dprintf("argc is %d\n", argc); - if (sysfs_get_mnt_path(sysfs_mnt_path, MAX_NAME_LEN)) { - log_message(LOG_WARNING, "sysfs_get_mnt_path failed: %s\n", - strerror(errno)); - exit(1); - } - - if (hotplug_mode) { - /* - * There is a kernel race creating attributes, if called - * directly, uncomment the sleep. - */ - /* sleep(1); */ - - devpath = getenv("DEVPATH"); - if (!devpath) { - log_message(LOG_WARNING, "DEVPATH is not set\n"); - exit(1); - } + env = getenv("DEVPATH"); + if (env) { + hotplug_mode = 1; sys_specified = 1; - - strncpy(target_path, sysfs_mnt_path, MAX_NAME_LEN); - strncat(target_path, devpath, MAX_NAME_LEN); - } else { - if (set_options(argc, argv, short_options, long_options, - target_path, maj_min_dev) < 0) - exit(1); + strncpy(devpath, env, MAX_PATH_LEN); + devpath[sizeof(devpath)-1] = '\0'; } /* - * Override any command line options set via the config file. This - * is the only way to set options when in hotplug mode. + * Get config file options. */ newargv = NULL; retval = get_file_options(NULL, NULL, &newargc, &newargv); if (retval < 0) { - exit(1); - } else if (newargv && (retval == 0)) { - if (set_options(newargc, newargv, short_options, long_options, - target_path, maj_min_dev) < 0) - exit(1); + retval = 1; + goto exit; + } + if (newargv && (retval == 0)) { + if (set_options(newargc, newargv, short_options, devpath, + maj_min_dev) < 0) { + retval = 2; + goto exit; + } free(newargv); } - if (!sys_specified) { - log_message(LOG_WARNING, "-s must be specified\n"); + /* + * Get command line options (overriding any config file or DEVPATH + * settings). + */ + if (set_options(argc, argv, short_options, devpath, maj_min_dev) < 0) exit(1); + + if (!sys_specified) { + info("-s must be specified\n"); + retval = 1; + goto exit; } - retval = scsi_id(target_path, maj_min_dev); - exit(retval); + retval = scsi_id(devpath, maj_min_dev); + +exit: + sysfs_cleanup(); + logging_close(); + return retval; }