chiark / gitweb /
vol_id: fix logging glue
[elogind.git] / extras / scsi_id / scsi_id.c
index 6bb41132de1e3b248a99b00a2c28d1c0f697131d..6334af386b0910c9ee1a09c7ded395cfb513b85e 100644 (file)
@@ -4,21 +4,14 @@
  * 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<patmans@us.ibm.com>
  *
- *  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 <stdio.h>
 #include <syslog.h>
 #include <stdarg.h>
 #include <ctype.h>
+#include <getopt.h>
 #include <sys/stat.h>
-#ifdef __KLIBC__
-/*
- * Assume built under udev with KLIBC
- */
-#include <libsysfs.h>
-#else
-#include <sysfs/libsysfs.h>
-#endif
-#include "scsi_id.h"
-
-#ifndef SCSI_ID_VERSION
-#warning No version
-#define SCSI_ID_VERSION        "unknown"
-#endif
-
-/*
- * temporary names for mknod.
- */
-#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";
-/*
- * Just duplicate per dev options.
- */
-static const char dev_short_options[] = "bc:gp:";
+#include "../../udev/udev.h"
+#include "scsi_id.h"
 
-char sysfs_mnt_path[SYSFS_PATH_MAX];
+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 char *default_callout;
 static int dev_specified;
-static int sys_specified;
-static char config_file[MAX_NAME_LEN] = CONFIG_FILE;
-static int display_bus_id;
-static int default_page_code;
+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;
-
-void log_message (int level, const char *format, ...)
-{
-       va_list args;
-
-       if (!debug && level == LOG_DEBUG)
-               return;
-
-       va_start (args, format);
-       if (!hotplug_mode || use_stderr) {
-               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);
-                       logging_init = 1;
-               }
-
-               vsyslog(level, format, args);
-       }
-       va_end (args);
-       return;
-}
-
-static int sysfs_get_actual_dev(const char *sysfs_path, char *dev, int len)
+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];
+
+static void log_fn(struct udev *udev, int priority,
+                  const char *file, int line, const char *fn,
+                  const char *format, va_list args)
 {
-       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;
+       vsyslog(priority, format, args);
 }
 
-/*
- * 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_str(char *to, const char *from, size_t count)
 {
-       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_NAME, 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;
-}
-
-static int get_major_minor(const char *devpath, int *major, int *minor)
-{
-       struct sysfs_class_device *class_dev;
-       char dev_value[SYSFS_NAME_LEN];
-       char *dev;
-
-       dprintf("%s\n", devpath);
-       class_dev = sysfs_open_class_device_path(devpath);
-       if (!class_dev) {
-               log_message(LOG_WARNING, "open class %s failed: %s\n", devpath,
-                           strerror(errno));
-               return -1;
-       }
-
-       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));
-               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);
-               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';
 }
 
-static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type)
+static void set_type(char *to, const char *from, size_t len)
 {
-       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);
-
-       if (mknod(tmpdev, 0600 | dev_type, makedev(major, minor))) {
-               log_message(LOG_WARNING, "mknod failed: %s\n", strerror(errno));
-               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;
+               }
        }
-       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;
+       strncpy(to, type, len);
+       to[len-1] = '\0';
 }
 
 /*
@@ -278,8 +195,9 @@ 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(struct udev *udev,
+                           const char *vendor, const char *model,
+                           int *argc, char ***newargv)
 {
        char *buffer;
        FILE *fd;
@@ -290,15 +208,14 @@ static int get_file_options(char *vendor, char *model, int *argc,
        int c;
        int retval = 0;
 
-       dprintf("vendor='%s'; model='%s'\n", vendor, model);
+       dbg(udev, "vendor='%s'; model='%s'\n", vendor, model);
        fd = fopen(config_file, "r");
        if (fd == NULL) {
-               dprintf("can't open %s\n", config_file);
+               dbg(udev, "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(udev, "can't open %s: %s\n", config_file, strerror(errno));
                        return -1;
                }
        }
@@ -310,7 +227,7 @@ static int get_file_options(char *vendor, char *model, int *argc,
         */
        buffer = malloc(MAX_BUFFER_LEN);
        if (!buffer) {
-               log_message(LOG_WARNING, "Can't allocate memory.\n");
+               err(udev, "can't allocate memory\n");
                return -1;
        }
 
@@ -324,29 +241,22 @@ static int get_file_options(char *vendor, char *model, int *argc,
                        break;
                lineno++;
                if (buf[strlen(buffer) - 1] != '\n') {
-                       log_message(LOG_WARNING,
-                                   "Config file line %d too long.\n", lineno);
+                       err(udev, "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(udev, "lineno %d: '%s'\n", lineno, buf);
                str1 = strsep(&buf, "=");
                if (str1 && strcasecmp(str1, "VENDOR") == 0) {
                        str1 = get_value(&buf);
@@ -376,22 +286,20 @@ static int get_file_options(char *vendor, char *model, int *argc,
                        }
                        options_in = str1;
                }
-               dprintf("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)) {
-                       log_message(LOG_WARNING,
-                                   "Error parsing config file line %d '%s'\n",
-                                   lineno, buffer);
+                       err(udev, "Error parsing config file line %d '%s'\n", lineno, buffer);
                        retval = -1;
                        break;
                }
                if (vendor == NULL) {
                        if (vendor_in == NULL) {
-                               dprintf("matched global option\n");
+                               dbg(udev, "matched global option\n");
                                break;
                        }
                } else if ((vendor_in && strncmp(vendor, vendor_in,
@@ -405,10 +313,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(udev, "matched vendor/model\n");
                                break;
                } else {
-                       dprintf("no match\n");
+                       dbg(udev, "no match\n");
                }
        }
 
@@ -423,8 +331,7 @@ static int get_file_options(char *vendor, char *model, int *argc,
                        c = argc_count(buffer) + 2;
                        *newargv = calloc(c, sizeof(**newargv));
                        if (!*newargv) {
-                               log_message(LOG_WARNING,
-                                           "Can't allocate memory.\n");
+                               err(udev, "can't allocate memory\n");
                                retval = -1;
                        } else {
                                *argc = c;
@@ -432,16 +339,14 @@ static int get_file_options(char *vendor, char *model, int *argc,
                                /*
                                 * 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.
-                        */
+                       /* No matches  */
                        retval = 1;
                }
        }
@@ -451,40 +356,37 @@ static int get_file_options(char *vendor, char *model, int *argc,
        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;
 
        /*
         * 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_options);
+               option = getopt_long(argc, argv, short_opts, options, NULL);
                if (option == -1)
                        break;
 
                if (optarg)
-                       dprintf("option '%c' arg '%s'\n", option, optarg);
+                       dbg(udev, "option '%c' arg '%s'\n", option, optarg);
                else
-                       dprintf("option '%c'\n", option);
+                       dbg(udev, "option '%c'\n", option);
 
                switch (option) {
                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':
@@ -492,33 +394,56 @@ 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':
                        all_good = 1;
                        break;
 
-               case 'i':
-                       display_bus_id = 1;
-                       break;
+               case 'h':
+                       printf("Usage: scsi_id OPTIONS <device>\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) {
-                               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);
+                               err(udev, "Unknown page code '%s'\n", optarg);
                                return -1;
                        }
                        break;
 
                case 's':
-                       sys_specified = 1;
-                       strncpy(target, sysfs_mnt_path, MAX_NAME_LEN);
-                       strncat(target, optarg, MAX_NAME_LEN);
+                       sg_version = atoi(optarg);
+                       if (sg_version < 3 || sg_version > 4) {
+                               err(udev, "Unknown SG version '%s'\n", optarg);
+                               return -1;
+                       }
+                       break;
+
+               case 'u':
+                       reformat_serial = 1;
+                       break;
+
+               case 'x':
+                       export = 1;
                        break;
 
                case 'v':
@@ -526,94 +451,70 @@ static int set_options(int argc, char **argv, const char *short_opts,
                        break;
 
                case 'V':
-                       log_message(LOG_WARNING, "scsi_id version: %s\n",
-                                   SCSI_ID_VERSION);
+                       printf("%s\n", VERSION);
                        exit(0);
                        break;
 
                default:
-                       log_message(LOG_WARNING,
-                                   "Unknown or bad option '%c' (0x%x)\n",
-                                   option, option);
-                       return -1;
+                       exit(1);
                }
        }
+       if (optind < argc && !dev_specified) {
+               dev_specified = 1;
+               strncpy(maj_min_dev, argv[optind], MAX_PATH_LEN);
+               maj_min_dev[MAX_PATH_LEN-1] = '\0';
+       }
        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 udev *udev,
+                          struct scsi_id_device *dev_scsi, int *good_bad, int *page_code)
 {
        int retval;
        int newargc;
        char **newargv = NULL;
-       char *vendor;
-       char *model;
        int option;
 
        *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");
-       if (!vendor) {
-               log_message(LOG_WARNING, "%s: no vendor attribute\n",
-                           scsi_dev->name);
-               return -1;
-       }
-
-       model = sysfs_get_attr(scsi_dev, "model");
-       if (!model) {
-               log_message(LOG_WARNING, "%s: no model attribute\n",
-                           scsi_dev->name);
-               return -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)
-                       dprintf("option '%c' arg '%s'\n", option, optarg);
+                       dbg(udev, "option '%c' arg '%s'\n", option, optarg);
                else
-                       dprintf("option '%c'\n", option);
+                       dbg(udev, "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);
+                               err(udev, "Unknown page code '%s'\n", optarg);
                                retval = -1;
                        }
                        break;
 
                default:
-                       log_message(LOG_WARNING,
-                                   "Unknown or bad option '%c' (0x%x)\n",
-                                   option, option);
+                       err(udev, "Unknown or bad option '%c' (0x%x)\n", option, option);
                        retval = -1;
                        break;
                }
@@ -626,6 +527,49 @@ static int per_dev_options(struct sysfs_class_device *scsi_dev, int *good_bad,
        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;
+
+       set_str(vendor_str, dev_scsi->vendor, sizeof(vendor_str));
+       set_str(model_str, dev_scsi->model, sizeof(model_str));
+       set_type(type_str, dev_scsi->type, sizeof(type_str));
+       set_str(revision_str, dev_scsi->revision, sizeof(revision_str));
+
+       return 0;
+}
+
+/*
+ * 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
@@ -633,199 +577,101 @@ 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(struct udev *udev, 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 */
+       struct scsi_id_device dev_scsi;
        int good_dev;
        int page_code;
-       char callout[MAX_NAME_LEN];
+       char serial_short[MAX_SERIAL_LEN] = "";
 
-       dprintf("target_path %s\n", target_path);
+       set_inq_values(udev, &dev_scsi, maj_min_dev);
 
-       /*
-        * 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))
-               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).
-        *
-        * 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));
-               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");
-               return 1;
-       }
-
-       scsi_dev = sysfs_open_class_device_path(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.
-        */
-       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);
+       /* get per device (vendor + model) options from the config file */
+       retval = 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 (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,
-                                  serial, MAX_SERIAL_LEN)) {
+       } else if (scsi_get_serial(udev,
+                                  &dev_scsi, maj_min_dev, page_code,
+                                  serial_short, MAX_SERIAL_LEN)) {
                retval = 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) {
+                       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, dev_scsi.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);
+               } else {
+                       if (reformat_serial)
+                               format_serial(dev_scsi.serial);
+                       printf("%s\n", dev_scsi.serial);
+               }
+               dbg(udev, "%s\n", dev_scsi.serial);
                retval = 0;
        }
-       sysfs_close_class_device(scsi_dev);
-
-       if (!dev_specified)
-               unlink(maj_min_dev);
 
        return retval;
 }
 
 int main(int argc, char **argv)
 {
-       int retval;
-       char *devpath;
-       char target_path[MAX_NAME_LEN];
-       char maj_min_dev[MAX_NAME_LEN];
+       struct udev *udev;
+       int retval = 0;
+       char maj_min_dev[MAX_PATH_LEN];
        int newargc;
        char **newargv;
 
-       if (getenv("DEBUG"))
-               debug++;
+       udev = udev_new();
+       if (udev == NULL)
+               goto exit;
 
-       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",
-                       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);
-               }
-               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);
-       }
+       logging_init("scsi_id");
+       udev_set_log_fn(udev, log_fn);
 
        /*
-        * 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);
+       retval = get_file_options(udev, NULL, NULL, &newargc, &newargv);
        if (retval < 0) {
-               exit(1);
-       } else if (newargv && (retval == 0)) {
-               if (set_options(newargc, newargv, short_options, target_path,
-                               maj_min_dev) < 0)
-                       exit(1);
+               retval = 1;
+               goto exit;
+       }
+       if (newargv && (retval == 0)) {
+               if (set_options(udev, newargc, newargv, short_options, 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 settings).
+        */
+       if (set_options(udev, argc, argv, short_options, maj_min_dev) < 0)
                exit(1);
+
+       if (!dev_specified) {
+               err(udev, "no device specified\n");
+               retval = 1;
+               goto exit;
        }
 
-       retval = scsi_id(target_path, maj_min_dev);
-       exit(retval);
+       retval = scsi_id(udev, maj_min_dev);
+
+exit:
+       udev_unref(udev);
+       logging_close();
+       return retval;
 }