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=a86b004709a219c4fa8cb68125a2c7ff05b63e3a;hp=df18271b48834e2febabc33350f4b7a4ee206833;hb=c907c823c388dfbdfa7054cfb2f4e9b92eb31e2e;hpb=1bed1db4994aae37f4a11e90dabcd8b4e3592686 diff --git a/extras/scsi_id/scsi_id.c b/extras/scsi_id/scsi_id.c index df18271b4..a86b00470 100644 --- a/extras/scsi_id/scsi_id.c +++ b/extras/scsi_id/scsi_id.c @@ -32,14 +32,8 @@ #include #include #include -#ifdef __KLIBC__ -/* - * Assume built under udev with KLIBC - */ -#include -#else #include -#endif +#include "scsi_id_version.h" #include "scsi_id.h" #ifndef SCSI_ID_VERSION @@ -53,26 +47,36 @@ #define TMP_DIR "/tmp" #define TMP_PREFIX "scsi" -#define CONFIG_FILE "/etc/scsi_id.config" - -static const char short_options[] = "bc:d:ef:gip:s:vV"; +/* + * XXX Note the 'e' (send output to stderr in all cases), and 'c' (callout) + * options are not supported, but other code is still left in place for + * now. + */ +static const char short_options[] = "abd:f:gip:s:uvVx"; /* * Just duplicate per dev options. */ -static const char dev_short_options[] = "bc:gp:"; +static const char dev_short_options[] = "bgp:"; char sysfs_mnt_path[SYSFS_PATH_MAX]; static int all_good; +static int always_info; static char *default_callout; static int dev_specified; static int sys_specified; -static char config_file[MAX_NAME_LEN] = CONFIG_FILE; +static char config_file[MAX_NAME_LEN] = SCSI_ID_CONFIG_FILE; static int display_bus_id; static int default_page_code; static int use_stderr; static int debug; static int hotplug_mode; +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]; void log_message (int level, const char *format, ...) { @@ -86,13 +90,8 @@ void log_message (int level, const char *format, ...) vfprintf(stderr, format, args); } else { static int logging_init = 0; - static unsigned char logname[32]; if (!logging_init) { - /* - * klibc does not have LOG_PID. - */ - snprintf(logname, 32, "scsi_id[%d]", getpid()); - openlog (logname, 0, LOG_DAEMON); + openlog ("scsi_id", LOG_PID, LOG_DAEMON); logging_init = 1; } @@ -102,108 +101,120 @@ void log_message (int level, const char *format, ...) return; } -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 get_major_minor(struct sysfs_class_device *class_dev, int *maj, + int *min) { - 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; - } + struct sysfs_attribute *dev_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) { + dev_attr = sysfs_get_classdev_attr(class_dev, "dev"); + if (!dev_attr) { /* * XXX This happens a lot, since sg has no dev attr. - * Someday change this back to a LOG_WARNING. + * And now sysfsutils does not set a meaningful errno + * value. Someday change this back to a LOG_WARNING. + * And if sysfsutils changes, check for ENOENT and handle + * it separately. */ - log_message(LOG_DEBUG, "%s could not get dev attribute: %s\n", - devpath, strerror(errno)); + log_message(LOG_DEBUG, "%s: could not get dev attribute: %s\n", + class_dev->name, 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) { + dprintf("dev value %s", dev_attr->value); /* value has a trailing \n */ + if (sscanf(dev_attr->value, "%u:%u", maj, min) != 2) { log_message(LOG_WARNING, "%s: invalid dev major/minor\n", - devpath); + class_dev->name); return -1; } return 0; } -static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type) +static int create_tmp_dev(struct sysfs_class_device *class_dev, char *tmpdev, + int dev_type) { - int major, minor; + int maj, min; - dprintf("(%s)\n", devpath); + dprintf("(%s)\n", class_dev->name); - if (get_major_minor(devpath, &major, &minor)) + if (get_major_minor(class_dev, &maj, &min)) return -1; snprintf(tmpdev, MAX_NAME_LEN, "%s/%s-maj%d-min%d-%u", - TMP_DIR, TMP_PREFIX, major, minor, getpid()); + TMP_DIR, TMP_PREFIX, maj, min, getpid()); dprintf("tmpdev '%s'\n", tmpdev); - if (mknod(tmpdev, 0600 | dev_type, makedev(major, minor))) { + if (mknod(tmpdev, 0600 | dev_type, makedev(maj, min))) { log_message(LOG_WARNING, "mknod failed: %s\n", strerror(errno)); return -1; } @@ -464,7 +475,7 @@ static int set_options(int argc, char **argv, const char *short_opts, */ optind = 1; while (1) { - option = getopt(argc, argv, short_options); + option = getopt(argc, argv, short_opts); if (option == -1) break; @@ -474,6 +485,9 @@ static int set_options(int argc, char **argv, const char *short_opts, dprintf("option '%c'\n", option); switch (option) { + case 'a': + always_info = 1; + break; case 'b': all_good = 0; break; @@ -521,6 +535,14 @@ static int set_options(int argc, char **argv, const char *short_opts, strncat(target, optarg, MAX_NAME_LEN); break; + case 'u': + reformat_serial = 1; + break; + + case 'x': + export = 1; + break; + case 'v': debug++; break; @@ -541,14 +563,13 @@ static int set_options(int argc, char **argv, const char *short_opts, return 0; } -static int per_dev_options(struct sysfs_class_device *scsi_dev, int *good_bad, +static int per_dev_options(struct sysfs_device *scsi_dev, int *good_bad, int *page_code, char *callout) { int retval; int newargc; char **newargv = NULL; - char *vendor; - char *model; + struct sysfs_attribute *vendor, *model, *type; int option; *good_bad = all_good; @@ -558,21 +579,40 @@ static int per_dev_options(struct sysfs_class_device *scsi_dev, int *good_bad, else callout[0] = '\0'; - vendor = sysfs_get_attr(scsi_dev, "vendor"); + vendor = sysfs_get_device_attr(scsi_dev, "vendor"); if (!vendor) { - log_message(LOG_WARNING, "%s: no vendor attribute\n", + log_message(LOG_WARNING, "%s: cannot get vendor attribute\n", scsi_dev->name); return -1; } + set_str(vendor_str, vendor->value, sizeof(vendor_str)-1); - model = sysfs_get_attr(scsi_dev, "model"); + model = sysfs_get_device_attr(scsi_dev, "model"); if (!model) { - log_message(LOG_WARNING, "%s: no model attribute\n", + log_message(LOG_WARNING, "%s: cannot get model attribute\n", scsi_dev->name); return -1; } + set_str(model_str, model->value, sizeof(model_str)-1); - retval = get_file_options(vendor, model, &newargc, &newargv); + type = sysfs_get_device_attr(scsi_dev, "type"); + if (!type) { + log_message(LOG_WARNING, "%s: cannot get type attribute\n", + scsi_dev->name); + return -1; + } + set_type(type_str, type->value, sizeof(type_str)); + + type = sysfs_get_device_attr(scsi_dev, "rev"); + if (!type) { + log_message(LOG_WARNING, "%s: cannot get type attribute\n", + scsi_dev->name); + return -1; + } + set_str(revision_str, type->value, sizeof(revision_str)-1); + + retval = get_file_options(vendor->value, model->value, &newargc, + &newargv); optind = 1; /* reset this global extern */ while (retval == 0) { @@ -626,6 +666,22 @@ static int per_dev_options(struct sysfs_class_device *scsi_dev, int *good_bad, 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; + + while (*p != '\0') { + if (isspace(*p)) + *p = '_'; + p++; + } +} + /* * 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 @@ -637,9 +693,10 @@ static int scsi_id(const char *target_path, 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_class_device *class_dev; /* of target_path */ + struct sysfs_class_device *class_dev_parent; /* for partitions */ + struct sysfs_device *scsi_dev; /* the scsi_device */ int good_dev; int page_code; char callout[MAX_NAME_LEN]; @@ -670,20 +727,48 @@ static int scsi_id(const char *target_path, char *maj_min_dev) } } - if (sysfs_get_actual_dev(target_path, full_dev_path, MAX_NAME_LEN)) + class_dev = sysfs_open_class_device_path(target_path); + if (!class_dev) { + log_message(LOG_WARNING, "open class %s failed: %s\n", + target_path, strerror(errno)); + return 1; + } + class_dev_parent = sysfs_get_classdev_parent(class_dev); + dprintf("class_dev 0x%p; class_dev_parent 0x%p\n", class_dev, + class_dev_parent); + if (class_dev_parent) { + scsi_dev = sysfs_get_classdev_device(class_dev_parent); + } else { + scsi_dev = sysfs_get_classdev_device(class_dev); + } + + /* + * The close of scsi_dev will close class_dev or class_dev_parent. + */ + + /* + * We assume we are called after the device is completely ready, + * so we don't have to loop here like udev. (And we are usually + * called via udev.) + */ + if (!scsi_dev) { + /* + * errno is not set if we can't find the device link, so + * don't print it out here. + */ + log_message(LOG_WARNING, "Cannot find sysfs device associated with %s\n", + target_path); return 1; + } - dprintf("full_dev_path %s\n", full_dev_path); /* - * Allow only scsi devices (those that have a matching device - * under /bus/scsi/devices). + * Allow only 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 (strcmp(scsi_dev->bus, "scsi") != 0) { if (hotplug_mode) /* * Expected in some cases. @@ -693,28 +778,17 @@ static int scsi_id(const char *target_path, char *maj_min_dev) 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)); - return 1; } /* * mknod a temp dev to communicate with the device. */ - if (!dev_specified && create_tmp_dev(target_path, maj_min_dev, + if (!dev_specified && create_tmp_dev(class_dev, maj_min_dev, dev_type)) { dprintf("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 any per device (vendor + model) options from the config * file. @@ -723,36 +797,47 @@ static int scsi_id(const char *target_path, char *maj_min_dev) 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)); + dprintf("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 ;-) + * XXX Disabled for now ('c' is not in any options[]). */ retval = 1; } else if (scsi_get_serial(scsi_dev, 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"); + 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: ", scsi_dev->name); + printf("%s\n", serial); + } dprintf("%s\n", serial); retval = 0; } - sysfs_close_class_device(scsi_dev); + sysfs_close_device(scsi_dev); if (!dev_specified) unlink(maj_min_dev); @@ -772,11 +857,6 @@ int main(int argc, char **argv) if (getenv("DEBUG")) debug++; - if ((argc == 2) && (argv[1][0] != '-')) { - hotplug_mode = 1; - dprintf("hotplug assumed\n"); - } - 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", @@ -784,31 +864,19 @@ int main(int argc, char **argv) exit(1); } - if (hotplug_mode) { + devpath = getenv("DEVPATH"); + if (devpath) { /* - * There is a kernel race creating attributes, if called - * directly, uncomment the sleep. + * This implies that we were invoked via udev or hotplug. */ - /* sleep(1); */ - - devpath = getenv("DEVPATH"); - if (!devpath) { - log_message(LOG_WARNING, "DEVPATH is not set\n"); - exit(1); - } + 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, target_path, - maj_min_dev) < 0) - exit(1); } /* - * 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); @@ -820,6 +888,13 @@ int main(int argc, char **argv) exit(1); free(newargv); } + /* + * Get command line options (overriding any config file or DEVPATH + * settings). + */ + if (set_options(argc, argv, short_options, target_path, + maj_min_dev) < 0) + exit(1); if (!sys_specified) { log_message(LOG_WARNING, "-s must be specified\n");