This patch syncs the scsi_id in the udev tree to version 0.4.
+2004-feb-25:
+ * scsi_id.8: Add verbage about usage with udev, and running via
+ hotplug.
+
+2004-feb-25:
+ * scsi_id.c, scsi_id.8: Disable support for -e (all output to
+ stderr) as it cannot be used in any useful way, and the -c
+ (device specific callout) as the code is incomplete and has no
+ users.
+
+2004-feb-25:
+ * scsi_id.c: Don't print errno for NULL return from
+ sysfs_get_classdev_device.
+
+2004-feb-23:
+ * scsi_id.c: Get rid of dead/leftover code that checked
+ if we are on a scsi bus.
+
+2004-feb-23:
+ * scsi_serial.c, scsi_id.c: Use ":" consistently in output messages.
+
+2004-feb-23:
+ * scsi_serial.c: Add missing new lines for some error messages.
+
+2004-feb-23:
+ * scsi_serial.c: open O_NONBLOCK so we handle tape drives without
+ tapes loaded.
+
+2004-feb-20:
+ * scsi_id.h, scsi_id.c: Remove hacks based on KLIBC define to get
+ around problems when building with udev (udev libsysfs files
+ were rearranged).
+
+2004-feb-19:
+ * scsi_id.h, scsi_id.c, scsi_serial.c: As done in udev code, support
+ partitions via looking for a parent of the target path. Uses
+ libsysfs functions to do most of the work, and includes changing
+ a lot of variables to be struct sysfs_device instead of
+ sysfs_class_device.
+
+2004-feb-19:
+ * Makefile: Version 0.4
+
2004-jan-15:
* Makefile: Version 0.3
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-SCSI_ID_VERSION=0.3
+SCSI_ID_VERSION=0.4
prefix =
etcdir = ${prefix}/etc
-- add information about the config file to the man page
+- Document that config file options override command line options, or fix
+ the code. This means a '-b' in the config file disables any -g on the
+ command line.
-- change so non-KLIBC builds under udev don't use /usr/include/sysfs,
- but instead use the sysfs included with udev (needs udev change and/or
- sysfsutils changes).
+- Add code to check that SCSI commands get back enough data for their
+ intended usage, mainly callers of scsi_inquiry().
-- do something with callout code - remove or change to a tag?
-
- This needs an implementation of a device specific callout or device
- specific code (called via some special "tag" or such) before it can be
- completed. Someone with such hardware to send in a patch.
+- Document the config file in the man page
--- /dev/null
+#! /bin/sh
+
+# This script generates and sends to stdout a set of udev.rules for use
+# with all scsi block devices on your system. It creates a udev key NAME
+# with prefix defaulting to "disk-", and appends the current kernel name
+# and the udev kernel number (the partition number, empty for the entire
+# disk).
+#
+# Managing these is probably better done via a gui interface.
+#
+# You can edit and append the output to your /etc/udev/udev.rules file.
+# You probably want to to change names to be non-kernel defaults, so as to
+# avoid confusion if a configuration change modifies /sys/block/sd*
+# naming.
+#
+# /etc/scsi_id.config must be properly configured. If you are using this
+# script, you probably want a single line enabling scsi_id for all
+# devices as follows:
+#
+# options=-g
+#
+# The above assumes you will not attach block devices that do not
+# properly support the page codes used by scsi_id, this is especially true
+# of many USB mass storage devices (mainly flash card readers).
+#
+
+prefix=disk-
+scsi_id=/sbin/scsi_id
+
+dump_ids()
+{
+ cd ${sysfs_dir}/block
+ for b in sd*
+ do
+ echo -n "$b "
+ $scsi_id -s /block/$b
+ if [ $? != 0 ]
+ then
+ echo $0 failed for device $b >&2
+ exit 1
+ fi
+ done
+}
+
+sysfs_dir=$(mount | awk '$5 == "sysfs" {print $3}')
+
+c=$(ls /${sysfs_dir}/block/sd* 2>/dev/null | wc -l)
+if [ $c = 0 ]
+then
+ echo $0 no block devices present >&2
+ exit 1
+fi
+
+echo "#"
+echo "# Start of autogenerated scsi_id rules. Edit the NAME portions of these"
+echo "# rules to your liking."
+echo "#"
+first_line=yes
+dump_ids | while read in
+do
+ set $in
+ name=$1
+ shift
+ id="$*"
+ if [ $first_line = "yes" ]
+ then
+ first_line=no
+ echo "BUS=\"scsi\", PROGRAM=\"${scsi_id}\", RESULT=\"${id}\", NAME=\"${prefix}${name}%n\""
+ echo
+ echo "# Further RESULT keys use the result of the last PROGRAM rule."
+ echo "# Be careful not to add any rules containing PROGRAM key between here"
+ echo "# and the end of this section"
+ echo
+ else
+ # No PROGRAM, so just use the last result of PROGRAM. The
+ # following is the same as the above without the PROGRAM
+ # key.
+ echo "BUS=\"scsi\", RESULT=\"${id}\", NAME=\"${prefix}${name}%n\""
+ fi
+
+done
+
+echo "#"
+echo "# End of autogenerated scsi_id rules"
+echo "#"
-Version 0.3 of scsi_id is available at:
+Version 0.4 of scsi_id is available at:
-http://www-124.ibm.com/storageio/scsi_id/scsi_id-0.3.tar.gz
+http://www-124.ibm.com/storageio/scsi_id/scsi_id-0.4.tar.gz
-scsi_id is a program to generate a SCSI unique identifier for a given SCSI
+scsi_id is a program to generate a unique identifier for a given SCSI
device.
-It is primarily for use with udev callout config entries. It can also be
-used for automatic multi-path configuration or device mapper configuration.
+It is primarily for use with the udev callout key. It could also be used
+for automatic multi-path configuration or device mapper configuration.
-Version 0.3 requires:
+Version 0.4 requires:
- Linux kernel 2.6
- libsysfs 0.4.0
Major changes since the last release:
- - Changes to work with libsysfs 0.4.0
+ - Support block device partitions
-All changes:
+ - Added a script to auto-generate udev rules. The script is not
+ installed but is part of the tarball.
- - fix "prefix" usage for path to scsi_id.config
+Detailed changes:
- - install the sample scsi_id.config file.
+ - Add man page verbage about usage with udev, and running via
+ hotplug
- - Use DESTDIR in all install/uninstall rules, per problem reported
- by Svetoslav Slavtchev.
+ - Disable support for -e (all output to stderr) as it cannot be
+ used in any useful way, and the -c (device specific callout) as
+ the code is incomplete and has no users.
- - Fix to work with current sysfs 0.4, based on patch from Dan
- Stekloff, but uses sysfs_read_attribute_value instead of
- sysfs_get_value_from_attributes.
+ - Don't print errno for NULL return from sysfs_get_classdev_device.
- - SYSFS_BUS_DIR was replaced with SYSFS_BUS_NAME.
+ - Get rid of dead/leftover code that checked if we are on a scsi
+ bus.
- - Must now use sysfs_open_class_device_path instead of the
- previous sysfs_open_class_device.
+ - ":" consistently in output messages.
- - patch from Olaf Hering <olh@suse.de> remove DEBUG and
- add --fno-builtin
+ - Add missing new lines for some error messages.
+
+ - open O_NONBLOCK so we handle tape drives without tapes loaded.
+
+ - Remove hacks based on KLIBC define to get around problems when
+ building with udev (udev libsysfs files were rearranged).
+
+ - As done in udev code, support partitions via looking for a
+ parent of the target path. Uses libsysfs functions to do most of
+ the work, and includes changing a lot of variables to be struct
+ sysfs_device instead of sysfs_class_device.
.fi
.P
-
.SH OPTIONS
.TP
+.BI subsystem
+When called with only a single argument without a leading \-, runs in a hotplug
+mode, and expects the environment variable DEVPATH to specify the
+corresponding sysfs device. See section below on usage with \fBudev\fP.
+.TP
.BI \-b
The default behaviour - treat the device as black listed, and do nothing
unless a white listed device is found in the scsi_id config-file.
entry as done for the \fB-s\fP, send SG_IO commands to
\fBdevice\fP, such as \fB/dev/sdc\fP.
.TP
-.BI \-e
-Send all output to standard error even if
-.B scsi_id
-is running in hotplug mode.
-.TP
.BI \-f "\| config-file"
Read configuration and black/white list entries from
.B config-file
query the available VPD pages, and use page 0x83 if found, else page 0x80
if found, else nothing.
.TP
-.BI \-s "\|sysfs-device"
+.BI \-s "\| sysfs-device"
Generate an id for the
.B sysfs-device.
The sysfs mount point must not be included. For example, use /block/sd,
.BI \-V
Display version number and exit.
.RE
+
+.SH USAGE WITH UDEV
+If \fBscsi_id\fP is invoked with one argument without a leading \-, it
+assumes it is called for a hotplug event, and looks for the sysfs device
+in the DEVPATH environment variable.
+
+This mode is used when run via the \fBudev\fP PROGRAM key. Passing any
+arguments or options as part of the PROGRAM rule breaks this assumption,
+and the results will likely not be as expected.
+
+When in this mode, all errors and warnings are sent via syslog.
+
+To determine the specific value needed in a RESULT key, use the -s option,
+for example:
+
+.sp
+.nf
+/sbin/scsi_id -s /block/sda
+.fi
+.P
+
+An example \fBudev\fP rule using \fBscsi_id\fP, that will name a block
+device and any partitions for the device matching the \fBscsi_id\fP output
+of 312345:
+
+.sp
+.nf
+BUS="scsi", PROGRAM="/sbin/scsi_id", RESULT="312345", NAME="disk%n"
+.fi
+.P
+
.SH "FILES"
.nf
.ft B
.fi
.LP
.SH "SEE ALSO"
-.BR udev (8)
-, especially the CALLOUT method.
+.BR udev (8), hotplug (8)
.SH AUTHORS
Developed by Patrick Mansfield <patmans@us.ibm.com> based on SCSI ID
source included in earlier linux 2.5 kernels, sg_utils source, and SCSI
#include <ctype.h>
#include <sys/stat.h>
#include <sysfs/libsysfs.h>
-
#include "scsi_id_version.h"
#include "scsi_id.h"
#define TMP_DIR "/tmp"
#define TMP_PREFIX "scsi"
-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[] = "bd:f:gip:s:vV";
/*
* 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];
return sysfs_read_attribute_value(attr_path, value, SYSFS_NAME_LEN);
}
-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)
-{
- 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)
{
char dev_value[MAX_ATTR_LEN];
* 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;
}
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 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 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);
+ }
+
+ /*
+ * 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.
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_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 = 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,
}
if (!retval) {
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;
}
- sysfs_close_class_device(scsi_dev);
+ sysfs_close_device(scsi_dev);
if (!dev_specified)
unlink(maj_min_dev);
extern int sysfs_get_attr(const char *devpath, const char *attr, char *value,
size_t bufsize);
-extern int scsi_get_serial (struct sysfs_class_device *scsi_dev,
- const char *devname, int page_code, char *serial,
- int len);
+extern int scsi_get_serial (struct sysfs_device *scsi_dev, const char
+ *devname, int page_code, char *serial, int
+ len);
extern void log_message (int level, const char *format, ...)
__attribute__ ((format (printf, 2, 3)));
#include <syslog.h>
#include <scsi/sg.h>
#include <sysfs/libsysfs.h>
-
#include "scsi_id.h"
#include "scsi.h"
static const char hex_str[]="0123456789abcdef";
-/*
- * XXX maybe move all these to an sg_io.c file.
- *
- * From here ...
- */
-
/*
* Values returned in the result/status, only the ones used by the code
* are used here.
hp->sbp, hp->sb_len_wr);
}
-static int scsi_dump_sense(struct sysfs_class_device *scsi_dev,
- struct sg_io_hdr *io)
+static int scsi_dump_sense(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
{
unsigned char *sense_buffer;
int s;
scsi_dev->name, sense_buffer[0],
sense_buffer[2]);
log_message(LOG_WARNING,
- "%s: non-extended sense class %d code 0x%0x ",
+ "%s: non-extended sense class %d code 0x%0x\n",
scsi_dev->name, sense_class, code);
}
return -1;
}
-static int scsi_dump(struct sysfs_class_device *scsi_dev, struct sg_io_hdr *io)
+static int scsi_dump(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
{
if (!io->status && !io->host_status && !io->msg_status &&
!io->driver_status) {
return -1;
}
-static int scsi_inquiry(struct sysfs_class_device *scsi_dev, int fd,
- unsigned char evpd, unsigned char page, unsigned
- char *buf, unsigned int buflen)
+static int scsi_inquiry(struct sysfs_device *scsi_dev, int fd, unsigned
+ char evpd, unsigned char page, unsigned char *buf,
+ unsigned int buflen)
{
unsigned char inq_cmd[INQUIRY_CMDLEN] =
{ INQUIRY_CMD, evpd, page, 0, buflen, 0 };
io_hdr.timeout = DEF_TIMEOUT;
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
- log_message(LOG_WARNING, "%s ioctl failed: %s\n",
+ log_message(LOG_WARNING, "%s: ioctl failed: %s\n",
scsi_dev->name, strerror(errno));
return -1;
}
retval = scsi_dump(scsi_dev, &io_hdr);
}
- /*
- * XXX where is the length checked? That is, was our request
- * buffer long enough?
- */
-
if (!retval) {
retval = buflen;
memcpy(buf, buffer, retval);
return retval;
}
-/*
- * XXX maybe move all these to an sg_io.c file.
- *
- * Ending here.
- */
-
-static int do_scsi_page0_inquiry(struct sysfs_class_device *scsi_dev, int fd,
+static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
char *buffer, int len)
{
int retval;
return 1;
}
if (buffer[3] > len) {
- log_message(LOG_WARNING, "%s: page 0 buffer too long %d",
+ log_message(LOG_WARNING, "%s: page 0 buffer too long %d\n",
scsi_dev->name, buffer[3]);
return 1;
}
return 1;
}
if (!strncmp(&buffer[VENDOR_LENGTH], vendor, VENDOR_LENGTH)) {
- log_message(LOG_WARNING, "%s invalid page0 data\n",
+ log_message(LOG_WARNING, "%s: invalid page0 data\n",
scsi_dev->name);
return 1;
}
* The caller checks that serial is long enough to include the vendor +
* model.
*/
-static int prepend_vendor_model(struct sysfs_class_device *scsi_dev,
- char *serial)
+static int prepend_vendor_model(struct sysfs_device *scsi_dev, char *serial)
{
char attr[MAX_ATTR_LEN];
int ind;
* check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill
* serial number.
**/
-static int check_fill_0x83_id(struct sysfs_class_device *scsi_dev,
- char *page_83,
- const struct scsi_id_search_values *id_search,
- char *serial, int max_len)
+static int check_fill_0x83_id(struct sysfs_device *scsi_dev, char
+ *page_83, const struct scsi_id_search_values
+ *id_search, char *serial, int max_len)
{
int i, j, len;
return 0;
}
-static int do_scsi_page83_inquiry(struct sysfs_class_device *scsi_dev, int fd,
+static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
char *serial, int len)
{
int retval;
return 1;
}
-static int do_scsi_page80_inquiry(struct sysfs_class_device *scsi_dev, int fd,
+static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
char *serial, int max_len)
{
int retval;
return 0;
}
-int scsi_get_serial (struct sysfs_class_device *scsi_dev, const char *devname,
+int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
int page_code, char *serial, int len)
{
unsigned char page0[256];
}
memset(serial, 0, len);
dprintf("opening %s\n", devname);
- fd = open(devname, O_RDONLY);
+ fd = open(devname, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
- log_message(LOG_WARNING, "%s cannot open %s: %s\n",
+ log_message(LOG_WARNING, "%s: cannot open %s: %s\n",
scsi_dev->name, devname, strerror(errno));
return 1;
}
goto completed;
}
} else if (page_code != 0x00) {
- log_message(LOG_WARNING, "%s unsupported page code 0x%d\n",
+ log_message(LOG_WARNING, "%s: unsupported page code 0x%d\n",
scsi_dev->name, page_code);
return 1;
}
retval = 1;
completed:
if (close(fd) < 0)
- log_message(LOG_WARNING, "close failed: %s", strerror(errno));
+ log_message(LOG_WARNING, "%s: close failed: %s\n",
+ scsi_dev->name, strerror(errno));
return retval;
}