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=507fa27766fe2d5fd02055a8d05dd3d2fff96a0a;hp=d34d9284e50d2d06b22622d4043f358a92f28988;hb=07544a93d6c75361b4906d90945755e34fd07565;hpb=c521693b54956c1f2dd0c0947c819b8570f6edaa diff --git a/extras/scsi_id/scsi_id.c b/extras/scsi_id/scsi_id.c index d34d9284e..507fa2776 100644 --- a/extras/scsi_id/scsi_id.c +++ b/extras/scsi_id/scsi_id.c @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -33,12 +32,13 @@ #include #include #include -#include +#include +#include "scsi_id_version.h" #include "scsi_id.h" -#ifndef VERSION +#ifndef SCSI_ID_VERSION #warning No version -#define VERSION "unknown" +#define SCSI_ID_VERSION "unknown" #endif /* @@ -47,38 +47,16 @@ #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} -}; +/* + * 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[] = "bd:f:gip:s:uvV"; /* * 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} -}; +static const char dev_short_options[] = "bgp:"; char sysfs_mnt_path[SYSFS_PATH_MAX]; @@ -86,12 +64,13 @@ static int all_good; 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; void log_message (int level, const char *format, ...) { @@ -105,8 +84,13 @@ 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) { - openlog ("scsi_id", LOG_PID, LOG_DAEMON); + /* + * klibc does not have LOG_PID. + */ + snprintf(logname, 32, "scsi_id[%d]", getpid()); + openlog (logname, 0, LOG_DAEMON); logging_init = 1; } @@ -116,86 +100,37 @@ void log_message (int level, const char *format, ...) return; } -static int sysfs_get_actual_dev(const char *sysfs_path, char *dev, int len) -{ - 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; - } - return 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) +int sysfs_get_attr(const char *devpath, const char *attr, char *value, + size_t bufsize) { - char bus_dev_name[SYSFS_PATH_MAX]; - char bus_id[SYSFS_NAME_LEN]; - struct stat stat_buf; - ino_t dev_inode; + char attr_path[SYSFS_PATH_MAX]; - 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; - } - if (dev_inode == stat_buf.st_ino) - return 1; - else - return 0; + strncpy(attr_path, devpath, SYSFS_PATH_MAX); + strncat(attr_path, "/", SYSFS_PATH_MAX); + strncat(attr_path, attr, SYSFS_PATH_MAX); + dprintf("%s\n", attr_path); + return sysfs_read_attribute_value(attr_path, value, SYSFS_NAME_LEN); } -static int get_major_minor(const char *devpath, int *major, int *minor) +static int get_major_minor(const char *devpath, 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; - } + char dev_value[MAX_ATTR_LEN]; - dev = sysfs_get_attr(class_dev, "dev"); - if (dev) - strncpy(dev_value, dev, SYSFS_NAME_LEN); - sysfs_close_class_device(class_dev); - if (!dev) { + if (sysfs_get_attr(devpath, "dev", dev_value, MAX_ATTR_LEN)) { /* * 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", + log_message(LOG_DEBUG, "%s: could not get dev attribute: %s\n", 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) { + dprintf("dev value %s", dev_value); /* dev_value has a trailing \n */ + if (sscanf(dev_value, "%u:%u", maj, min) != 2) { log_message(LOG_WARNING, "%s: invalid dev major/minor\n", devpath); return -1; @@ -206,18 +141,18 @@ 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) { - int major, minor; + int maj, min; dprintf("(%s)\n", devpath); - if (get_major_minor(devpath, &major, &minor)) + if (get_major_minor(devpath, &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; } @@ -295,7 +230,7 @@ static int argc_count(char *opts) static int get_file_options(char *vendor, char *model, int *argc, char ***newargv) { - char buffer[256]; + char *buffer; FILE *fd; char *buf; char *str1; @@ -303,7 +238,6 @@ 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); fd = fopen(config_file, "r"); @@ -318,16 +252,31 @@ static int get_file_options(char *vendor, char *model, int *argc, } } + /* + * 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) { + log_message(LOG_WARNING, "Can't allocate memory.\n"); + 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') { + log_message(LOG_WARNING, + "Config file line %d too long.\n", lineno); + break; + } while (isspace(*buf)) buf++; @@ -419,18 +368,24 @@ 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"); + "Can't allocate memory.\n"); 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 { /* @@ -439,26 +394,26 @@ static int get_file_options(char *vendor, char *model, int *argc, 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_options); if (option == -1) break; @@ -515,13 +470,17 @@ 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 'v': debug++; break; case 'V': log_message(LOG_WARNING, "scsi_id version: %s\n", - VERSION); + SCSI_ID_VERSION); exit(0); break; @@ -535,17 +494,15 @@ 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; + char vendor[MAX_ATTR_LEN]; + char model[MAX_ATTR_LEN]; int option; - int option_ind; - *good_bad = all_good; *page_code = default_page_code; @@ -554,26 +511,23 @@ 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"); - if (!vendor) { - log_message(LOG_WARNING, "%s: no vendor attribute\n", + if (sysfs_get_attr(scsi_dev->path, "vendor", vendor, MAX_ATTR_LEN)) { + log_message(LOG_WARNING, "%s: cannot get vendor attribute\n", scsi_dev->name); return -1; } - model = sysfs_get_attr(scsi_dev, "model"); - if (!vendor) { - log_message(LOG_WARNING, "%s: no model attribute\n", + if (sysfs_get_attr(scsi_dev->path, "model", model, MAX_ATTR_LEN)) { + log_message(LOG_WARNING, "%s: cannot get model attribute\n", scsi_dev->name); return -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; @@ -616,11 +570,30 @@ static int per_dev_options(struct sysfs_class_device *scsi_dev, int *good_bad, } } - 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; + + while (*p != '\0') { + if (isspace(*p)) + *p = '_'; + p++; + } + return; +} + /* * 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 @@ -632,9 +605,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]; @@ -665,20 +639,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); + } - dprintf("full_dev_path %s\n", full_dev_path); + /* + * The close of scsi_dev will close class_dev or class_dev_parent. + */ /* - * Allow only scsi devices (those that have a matching device - * under /bus/scsi/devices). + * 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; + } + + + /* + * 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. @@ -688,14 +690,12 @@ 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. + * + * XXX pass down class_dev or class_dev_parent. */ if (!dev_specified && create_tmp_dev(target_path, maj_min_dev, dev_type)) { @@ -703,13 +703,6 @@ static int scsi_id(const char *target_path, char *maj_min_dev) 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. @@ -718,18 +711,18 @@ 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, @@ -739,16 +732,15 @@ static int scsi_id(const char *target_path, char *maj_min_dev) retval = 0; } if (!retval) { + if (reformat_serial) + format_serial(serial); if (display_bus_id) - printf("%s ", scsi_dev->name); - printf("%s", serial); - if (!hotplug_mode) - printf("\n"); + printf("%s: ", scsi_dev->name); + printf("%s\n", serial); dprintf("%s\n", serial); retval = 0; } - fflush(stdout); - sysfs_close_class_device(scsi_dev); + sysfs_close_device(scsi_dev); if (!dev_specified) unlink(maj_min_dev); @@ -768,11 +760,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", @@ -780,42 +767,37 @@ 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, long_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); 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) + if (set_options(newargc, newargv, short_options, target_path, + maj_min_dev) < 0) 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");