chiark / gitweb /
scsi_id: initialize serial strings
[elogind.git] / extras / scsi_id / scsi_id.c
index 2ffc95fbfed69605ad8caf15315f6a8b5857b703..538108717dcfc8ac3f195b99734a5704e8d9c649 100644 (file)
@@ -24,6 +24,7 @@
 #include <syslog.h>
 #include <stdarg.h>
 #include <ctype.h>
+#include <getopt.h>
 #include <sys/stat.h>
 
 #include "../../udev.h"
 #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", 1, NULL, 'd' },
+       { "config", 1, NULL, 'f' },
+       { "page", 1, NULL, 'p' },
+       { "devpath", 1, NULL, 's' },
+       { "fallback-to-sysfs", 0, NULL, 'a' },
+       { "blacklisted", 0, NULL, 'b' },
+       { "whitelisted", 0, NULL, 'g' },
+       { "prefix-bus-id", 0, NULL, 'i' },
+       { "replace-whitespace", 0, NULL, 'u' },
+       { "ignore-sysfs", 0, NULL, 'n' },
+       { "verbose", 0, NULL, 'v' },
+       { "version", 0, NULL, 'V' },
+       { "export", 0, NULL, 'x' },
+       { "help", 0, NULL, 'h' },
+       {}
+};
+
+static const char short_options[] = "abd:f:ghinp:s:uvVx";
 static const char dev_short_options[] = "bgp:";
 
 static int all_good;
@@ -48,6 +67,7 @@ static int use_stderr;
 static int debug;
 static int hotplug_mode;
 static int reformat_serial;
+static int ignore_sysfs;
 static int export;
 static char vendor_str[64];
 static char model_str[64];
@@ -154,25 +174,25 @@ static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type)
        unsigned int maj, min;
        const char *attr;
 
-       dbg("%s", devpath);
+       dbg("%s\n", devpath);
        attr = sysfs_attr_get_value(devpath, "dev");
        if (attr == NULL) {
-               dbg("%s: could not get dev attribute: %s", devpath, strerror(errno));
+               dbg("%s: could not get dev attribute: %s\n", devpath, strerror(errno));
                return -1;
        }
 
-       dbg("dev value %s", attr);
+       dbg("dev value %s\n", attr);
        if (sscanf(attr, "%u:%u", &maj, &min) != 2) {
-               err("%s: invalid dev major/minor", devpath);
+               err("%s: invalid dev major/minor\n", 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);
+       dbg("tmpdev '%s'\n", tmpdev);
        if (mknod(tmpdev, 0600 | dev_type, makedev(maj, min))) {
-               err("mknod failed: %s", strerror(errno));
+               err("mknod failed: %s\n", strerror(errno));
                return -1;
        }
        return 0;
@@ -253,7 +273,7 @@ static int get_file_options(const char *vendor, const char *model,
                if (errno == ENOENT) {
                        return 1;
                } else {
-                       err("can't open %s: %s", config_file, strerror(errno));
+                       err("can't open %s: %s\n", config_file, strerror(errno));
                        return -1;
                }
        }
@@ -265,7 +285,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("can't allocate memory\n");
                return -1;
        }
 
@@ -279,7 +299,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);
+                       info("Config file line %d too long\n", lineno);
                        break;
                }
 
@@ -324,14 +344,14 @@ static int get_file_options(const char *vendor, const char *model,
                        }
                        options_in = str1;
                }
-               dbg("config file line %d:"
+               dbg("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);
+                       info("Error parsing config file line %d '%s'\n", lineno, buffer);
                        retval = -1;
                        break;
                }
@@ -369,7 +389,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("can't allocate memory\n");
                                retval = -1;
                        } else {
                                *argc = c;
@@ -377,11 +397,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  */
@@ -402,12 +422,11 @@ static int set_options(int argc, char **argv, const char *short_opts,
        /*
         * 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.]
+        * file) we have to reset this back to 1.
         */
        optind = 1;
        while (1) {
-               option = getopt(argc, argv, short_opts);
+               option = getopt_long(argc, argv, short_opts, options, NULL);
                if (option == -1)
                        break;
 
@@ -443,6 +462,24 @@ static int set_options(int argc, char **argv, const char *short_opts,
                        all_good = 1;
                        break;
 
+               case 'h':
+                       printf("Usage: scsi_id OPTIONS <device>\n"
+                              "  --device               device node for SG_IO commands\n"
+                              "  --devpath              sysfs devpath\n"
+                              "  --config               location of config file\n"
+                              "  --page                 SCSI page (0x80, 0x83, pre-spc3-83)\n"
+                              "  --fallback-to-sysfs    print sysfs values if inquiry fails\n"
+                              "  --ignore-sysfs         ignore sysfs entries\n"
+                              "  --blacklisted          threat device as blacklisted\n"
+                              "  --whitelisted          threat device as whitelisted\n"
+                              "  --prefix-bus-id        prefix SCSI bus id\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 'i':
                        display_bus_id = 1;
                        break;
@@ -455,11 +492,15 @@ 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);
+                               info("Unknown page code '%s'\n", optarg);
                                return -1;
                        }
                        break;
 
+               case 'n':
+                       ignore_sysfs = 1;
+                       break;
+
                case 's':
                        sys_specified = 1;
                        strncpy(target, optarg, MAX_PATH_LEN);
@@ -479,13 +520,12 @@ 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", SCSI_ID_VERSION);
                        exit(0);
                        break;
 
                default:
-                       info("Unknown or bad option '%c' (0x%x)", option, option);
-                       return -1;
+                       exit(1);
                }
        }
        return 0;
@@ -496,45 +536,16 @@ static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *pa
        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(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;
 
@@ -560,13 +571,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);
+                               info("Unknown page code '%s'\n", optarg);
                                retval = -1;
                        }
                        break;
 
                default:
-                       info("Unknown or bad option '%c' (0x%x)", option, option);
+                       info("Unknown or bad option '%c' (0x%x)\n", option, option);
                        retval = -1;
                        break;
                }
@@ -579,6 +590,58 @@ static int per_dev_options(struct sysfs_device *dev_scsi, int *good_bad, int *pa
        return retval;
 }
 
+static int set_sysfs_values(struct sysfs_device *dev_scsi)
+{
+       const char *vendor, *model, *type;
+
+       vendor = sysfs_attr_get_value(dev_scsi->devpath, "vendor");
+       if (!vendor) {
+               info("%s: cannot get vendor attribute\n", 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\n", 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);
+
+       return 0;
+}
+
+static int set_inq_values(struct sysfs_device *dev_scsi, const char *path)
+{
+       int retval;
+       char vendor[8], model[16], type[4], rev[4];
+
+       retval = scsi_std_inquiry(dev_scsi, path, vendor, model, rev, type);
+       if (retval)
+           return retval;
+
+       set_str(vendor_str, vendor, 8);
+       set_str(model_str, model, 16);
+       set_type(type_str, type, sizeof(type_str) - 1);
+       set_str(revision_str, rev, sizeof(revision_str) -1);
+
+       return 0;
+}
+
 /*
  * format_serial: replace to whitespaces by underscores for calling
  * programs that use the serial for device naming (multipath, Suse
@@ -615,17 +678,19 @@ static int scsi_id(const char *devpath, char *maj_min_dev)
 {
        int retval;
        int dev_type = 0;
-       char *serial, *unaligned_buf;
        struct sysfs_device *dev;
-       struct sysfs_device *dev_scsi;
+       struct sysfs_device *dev_scsi = NULL;
        int good_dev;
        int page_code;
+       char serial[MAX_SERIAL_LEN] = "";
+       char serial_short[MAX_SERIAL_LEN] = "";
+       const char *bus_str = NULL;
 
        dbg("devpath %s\n", devpath);
 
        dev = sysfs_device_get(devpath);
        if (dev == NULL) {
-               err("unable to access '%s'", devpath);
+               err("unable to access '%s'\n", devpath);
                return 1;
        }
 
@@ -634,53 +699,57 @@ static int scsi_id(const char *devpath, char *maj_min_dev)
        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;
-       }
-
        /* 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 (!ignore_sysfs) {
+               /* 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'\n", devpath);
+                       return 1;
+               }
+               set_sysfs_values(dev_scsi);
+               bus_str = "scsi";
+       } else {
+               dev_scsi = dev;
+               set_inq_values(dev_scsi, maj_min_dev);
+       }
+
        /* 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
+       dbg("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)) {
+                                  serial, serial_short, MAX_SERIAL_LEN)) {
                retval = always_info?0:1;
        } else {
                retval = 0;
        }
        if (!retval) {
                if (export) {
-                       static char serial_str[64];
+                       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);
                        set_str(serial_str, serial, sizeof(serial_str));
                        printf("ID_SERIAL=%s\n", serial_str);
+                       set_str(serial_str, serial_short, sizeof(serial_str));
+                       printf("ID_SERIAL_SHORT=%s\n", serial_str);
                        printf("ID_TYPE=%s\n", type_str);
-                       printf("ID_BUS=scsi\n");
+                       if (bus_str != NULL)
+                               printf("ID_BUS=%s\n", bus_str);
                } else {
                        if (reformat_serial)
                                format_serial(serial);
                        if (display_bus_id)
-                               printf("%s: ", dev_scsi->kernel_name);
+                               printf("%s: ", dev_scsi->kernel);
                        printf("%s\n", serial);
                }
                dbg("%s\n", serial);
@@ -748,7 +817,7 @@ int main(int argc, char **argv)
                exit(1);
 
        if (!sys_specified) {
-               info("-s must be specified\n");
+               info("--devpath=<path> must be specified\n");
                retval = 1;
                goto exit;
        }