#include <stdio.h>
#include <stdlib.h>
-#include <getopt.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/stat.h>
-#include <sys/libsysfs.h>
+#include <sysfs/libsysfs.h>
+#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
/*
#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];
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, ...)
{
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;
}
return;
}
-static int sysfs_get_actual_dev(const char *sysfs_path, char *dev, int len)
+int sysfs_get_attr(const char *devpath, const char *attr, char *value,
+ size_t bufsize)
{
- 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;
-}
+ char attr_path[SYSFS_PATH_MAX];
-/*
- * 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)
-{
- 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;
- }
- 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;
+ char dev_value[MAX_ATTR_LEN];
- 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;
- }
-
- 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;
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;
}
static int get_file_options(char *vendor, char *model, int *argc,
char ***newargv)
{
- char buffer[256];
+ char *buffer;
FILE *fd;
char *buf;
char *str1;
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");
}
}
+ /*
+ * 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++;
* 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 {
/*
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;
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;
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;
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;
}
}
- 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
{
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];
}
}
- 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.
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)) {
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.
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,
retval = 0;
}
if (!retval) {
+ if (reformat_serial)
+ format_serial(serial);
if (display_bus_id)
- printf("%s ", scsi_dev->name);
+ printf("%s: ", scsi_dev->name);
printf("%s", serial);
if (!hotplug_mode)
printf("\n");
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);
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);
}
/*
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);
}
+ if (!hotplug_mode) {
+ 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");