[PATCH] add scsi_id "extra" program from Patrick Mansfield <patmans@us.ibm.com>

This commit is contained in:
greg@kroah.com 2003-11-13 06:34:36 -08:00 committed by Greg KH
parent 04a091d47e
commit c521693b54
10 changed files with 2167 additions and 0 deletions

340
extras/scsi_id/COPYING Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
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; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

51
extras/scsi_id/Makefile Normal file
View File

@ -0,0 +1,51 @@
#
# Copyright (C) 2003 IBM
#
# 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.
#
# This program 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
VERSION=0.1
prefix =
sbindir = ${prefix}/sbin
INSTALL = /usr/bin/install -c
INSTALL_PROGRAM = ${INSTALL}
INSTALL_DATA = ${INSTALL} -m 644
CFLAGS=-DVERSION=\"$(VERSION)\" $(DEBUG) -Wall
PROG=scsi_id
LIBSYSFS=-lsysfs
STRIP=-s
LDFLAGS=$(STRIP) --static
OBJS= scsi_id.o \
scsi_serial.o \
all: $(PROG)
install: all
$(INSTALL_PROGRAM) -D $(PROG) $(sbindir)/$(PROG)
uninstall:
-rm $(sbindir)/$(PROG)
$(OBJS): scsi_id.h scsi.h
clean:
rm -f $(PROG) $(OBJS)
$(PROG): $(OBJS)
$(CC) $(OBJS) $(LDFLAGS) $(LIBSYSFS) -o $(PROG)

19
extras/scsi_id/README Normal file
View File

@ -0,0 +1,19 @@
scsi_id - generate a SCSI unique identifier for a given SCSI device
Primarily for use with udev callout config entries. This could also be
used by a multi-path configuration tool that requires SCSI id's.
Requires:
- Linux kernel 2.6
- libsysfs
No man page yet.
libsysfs 0_2_0 was not installing libsysfs.h or dlist.h, manually copy
those files to /usr/include/sys before compiling.
Build via make and make install.
Please send questions, comments or patches to patmans@us.ibm.com.

16
extras/scsi_id/TODO Normal file
View File

@ -0,0 +1,16 @@
- Investigate shrinking build size: use klibc or uClibc, or copy whatever
udev does
- write a man page
- send in kernel patch for REQ_BLOCK_PC, to always set sd and sr set
retries (scmd->allowed) to <= 1
- Pull SG_IO code into one .c file.
- implement callout to device specific serial id code. The "-c prog" is
not implemented.
This needs an implementation of a device specific callout before it can
be completed. Someone with hardware requiring this needs to send in a
patch.

1
extras/scsi_id/VERSION Normal file
View File

@ -0,0 +1 @@
0.1

96
extras/scsi_id/scsi.h Normal file
View File

@ -0,0 +1,96 @@
/*
* scsi.h
*
* General scsi and linux scsi specific defines and structs.
*
* Copyright (C) IBM Corp. 2003
*
* 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.
*
* 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
*/
#include <scsi/scsi.h>
struct scsi_ioctl_command {
unsigned int inlen; /* excluding scsi command length */
unsigned int outlen;
unsigned char data[1];
/* on input, scsi command starts here then opt. data */
};
/*
* Default 5 second timeout
*/
#define DEF_TIMEOUT 5000
#define SENSE_BUFF_LEN 32
/*
* SCSI INQUIRY vendor and model (really product) lengths.
*/
#define VENDOR_LENGTH 8
#define MODEL_LENGTH 16
#define INQUIRY_CMD 0x12
#define INQUIRY_CMDLEN 6
/*
* INQUIRY VPD page 0x83 identifier descriptor related values. Reference the
* SCSI Primary Commands specification for details.
*/
/*
* id type values of id descriptors. These are assumed to fit in 4 bits.
*/
#define SCSI_ID_VENDOR_SPECIFIC 0
#define SCSI_ID_T10_VENDOR 1
#define SCSI_ID_EUI_64 2
#define SCSI_ID_NAA 3
/*
* Supported NAA values. These fit in 4 bits, so the "don't care" value
* cannot conflict with real values.
*/
#define SCSI_ID_NAA_DONT_CARE 0xff
#define SCSI_ID_NAA_IEEE_REG 5
#define SCSI_ID_NAA_IEEE_REG_EXTENDED 6
/*
* Supported Code Set values.
*/
#define SCSI_ID_BINARY 1
#define SCSI_ID_ASCII 2
struct scsi_id_search_values {
u_char id_type;
u_char naa_type;
u_char code_set;
};
/*
* Following are the "true" SCSI status codes. Linux has traditionally
* used a 1 bit right and masked version of these. So now CHECK_CONDITION
* and friends (in <scsi/scsi.h>) are deprecated.
*/
#define SCSI_CHECK_CONDITION 0x2
#define SCSI_CONDITION_MET 0x4
#define SCSI_BUSY 0x8
#define SCSI_IMMEDIATE 0x10
#define SCSI_IMMEDIATE_CONDITION_MET 0x14
#define SCSI_RESERVATION_CONFLICT 0x18
#define SCSI_COMMAND_TERMINATED 0x22
#define SCSI_TASK_SET_FULL 0x28
#define SCSI_ACA_ACTIVE 0x30
#define SCSI_TASK_ABORTED 0x40

827
extras/scsi_id/scsi_id.c Normal file
View File

@ -0,0 +1,827 @@
/*
* scsi_id.c
*
* Main section of the scsi_id program
*
* Copyright (C) IBM Corp. 2003
*
* 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.
*
* 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
*/
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <syslog.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/libsysfs.h>
#include "scsi_id.h"
#ifndef VERSION
#warning No version
#define VERSION "unknown"
#endif
/*
* temporary names for mknod.
*/
#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}
};
/*
* 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}
};
char sysfs_mnt_path[SYSFS_PATH_MAX];
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 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;
if (!logging_init) {
openlog ("scsi_id", LOG_PID, 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)
{
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_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;
}
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(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;
}
return 0;
}
static int create_tmp_dev(const char *devpath, char *tmpdev, int dev_type)
{
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;
}
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;
}
/*
* get_value:
*
* buf points to an '=' followed by a quoted string ("foo") or a string ending
* with a space or ','.
*
* Return a pointer to the NUL terminated string, returns NULL if no
* matches.
*/
static char *get_value(char **buffer)
{
static char *quote_string = "\"\n";
static char *comma_string = ",\n";
char *val;
char *end;
if (**buffer == '"') {
/*
* skip leading quote, terminate when quote seen
*/
(*buffer)++;
end = quote_string;
} else {
end = comma_string;
}
val = strsep(buffer, end);
if (val && end == quote_string)
/*
* skip trailing quote
*/
(*buffer)++;
while (isspace(**buffer))
(*buffer)++;
return val;
}
static int argc_count(char *opts)
{
int i = 0;
while (*opts != '\0')
if (*opts++ == ' ')
i++;
return i;
}
/*
* get_file_options:
*
* If vendor == NULL, find a line in the config file with only "OPTIONS=";
* if vendor and model are set find the first OPTIONS line in the config
* file that matches. Set argc and argv to match the OPTIONS string.
*
* vendor and model can end in '\n'.
*/
static int get_file_options(char *vendor, char *model, int *argc,
char ***newargv)
{
char buffer[256];
FILE *fd;
char *buf;
char *str1;
char *vendor_in, *model_in, *options_in; /* read in from file */
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");
if (fd == NULL) {
dprintf("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));
return -1;
}
}
*newargv = NULL;
lineno = 0;
while (1) {
vendor_in = model_in = options_in = NULL;
buf = fgets(buffer, sizeof(buffer), fd);
if (buf == NULL)
break;
lineno++;
while (isspace(*buf))
buf++;
if (*buf == '\0')
/*
* blank or all whitespace line
*/
continue;
if (*buf == '#')
/*
* comment line
*/
continue;
#ifdef LOTS
dprintf("lineno %d: '%s'\n", lineno, buf);
#endif
str1 = strsep(&buf, "=");
if (str1 && strcasecmp(str1, "VENDOR") == 0) {
str1 = get_value(&buf);
if (!str1) {
retval = -1;
break;
}
vendor_in = str1;
str1 = strsep(&buf, "=");
if (str1 && strcasecmp(str1, "MODEL") == 0) {
str1 = get_value(&buf);
if (!str1) {
retval = -1;
break;
}
model_in = str1;
str1 = strsep(&buf, "=");
}
}
if (str1 && strcasecmp(str1, "OPTIONS") == 0) {
str1 = get_value(&buf);
if (!str1) {
retval = -1;
break;
}
options_in = str1;
}
dprintf("config file line %d:"
" 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);
retval = -1;
break;
}
if (vendor == NULL) {
if (vendor_in == NULL) {
dprintf("matched global option\n");
break;
}
} else if ((vendor_in && strncmp(vendor, vendor_in,
strlen(vendor_in)) == 0) &&
(!model_in || (strncmp(model, model_in,
strlen(model_in)) == 0))) {
/*
* Matched vendor and optionally model.
*
* Note: a short vendor_in or model_in can
* give a partial match (that is FOO
* matches FOOBAR).
*/
dprintf("matched vendor/model\n");
break;
} else {
dprintf("no match\n");
}
}
if (retval == 0) {
if (vendor_in != NULL || model_in != NULL ||
options_in != NULL) {
/*
* Something matched. Allocate newargv, and store
* values found in options_in.
*/
c = argc_count(options_in) + 2;
*newargv = calloc(c, sizeof(**newargv));
if (!*newargv) {
log_message(LOG_WARNING,
"Can't allocate memory\n");
retval = -1;
} else {
*argc = c;
c = 0;
(*newargv)[c] = prog_string; /* nothing */
for (c = 1; c < *argc; c++)
(*newargv)[c] = strsep(&options_in, " ");
}
} else {
/*
* No matches.
*/
retval = 1;
}
}
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)
{
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 = 0;
while (1) {
option = getopt_long(argc, argv, short_options, long_options,
&option_ind);
if (option == -1)
break;
if (optarg)
dprintf("option '%c' arg '%s'\n", option, optarg);
else
dprintf("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);
break;
case 'e':
use_stderr = 1;
break;
case 'f':
strncpy(config_file, optarg, MAX_NAME_LEN);
break;
case 'g':
all_good = 1;
break;
case 'i':
display_bus_id = 1;
break;
case 'p':
if (strcmp(optarg, "0x80") == 0) {
default_page_code = 0x80;
} else if (strcmp(optarg, "0x83") == 0) {
default_page_code = 0x83;
} else {
log_message(LOG_WARNING,
"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);
break;
case 'v':
debug++;
break;
case 'V':
log_message(LOG_WARNING, "scsi_id version: %s\n",
VERSION);
exit(0);
break;
default:
log_message(LOG_WARNING,
"Unknown or bad option '%c' (0x%x)\n",
option, option);
return -1;
}
}
return 0;
}
static int per_dev_options(struct sysfs_class_device *scsi_dev, int *good_bad,
int *page_code, char *callout)
{
int retval;
int newargc;
char **newargv = NULL;
char *vendor;
char *model;
int option;
int option_ind;
*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 (!vendor) {
log_message(LOG_WARNING, "%s: no model attribute\n",
scsi_dev->name);
return -1;
}
retval = get_file_options(vendor, model, &newargc, &newargv);
optind = 0; /* global extern, reset to 0 */
while (retval == 0) {
option = getopt_long(newargc, newargv, dev_short_options,
dev_long_options, &option_ind);
if (option == -1)
break;
if (optarg)
dprintf("option '%c' arg '%s'\n", option, optarg);
else
dprintf("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;
} else if (strcmp(optarg, "0x83") == 0) {
*page_code = 0x83;
} else {
log_message(LOG_WARNING,
"Unknown page code '%s'\n", optarg);
retval = -1;
}
break;
default:
log_message(LOG_WARNING,
"Unknown or bad option '%c' (0x%x)\n",
option, option);
retval = -1;
break;
}
}
if (newargv)
free(newargv);
return retval;
}
/*
* 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
* could be expanded, for example, if we want to report a failure like no
* 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)
{
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 */
int good_dev;
int page_code;
char callout[MAX_NAME_LEN];
dprintf("target_path %s\n", target_path);
/*
* 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(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);
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)) {
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);
retval = 0;
}
fflush(stdout);
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];
int newargc;
char **newargv;
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",
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, 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.
*/
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)
exit(1);
free(newargv);
}
if (!sys_specified) {
log_message(LOG_WARNING, "-s must be specified\n");
exit(1);
}
retval = scsi_id(target_path, maj_min_dev);
exit(retval);
}

View File

@ -0,0 +1,40 @@
#
# Informational and example scsi_id.config file for use with scsi_id.
#
# General syntax is:
#
# lower or upper case has no affect on the left side. Quotes (") are
# required if you need spaces in values. Model is the same as the SCSI
# INQUIRY product identification field. Per the SCSI INQUIRY, the vendor
# is limited to 8 bytes, model to 16 bytes.
#
# The first maching line found is used. Short matches match longer ones,
# if you do not want such a match space fill the extra bytes. If no model
# is specified, only the vendor string need match.
#
# The "option" line is searched when scsi_id first starts up (for use with
# hotplug during boot).
#
# options=<any scsi_id command line options>
#
# vendor=string[,model=string],options=<per-device scsi_id options>
#
# If you normally don't need id's, black list everyone:
#
options=-b
#
# Then white list devices on your system that have correct and useful id's:
#
vendor=someone, model=nicedrive, options=-g
# If you have all good devices on your system use, mark all as good:
## options=-g
# Then black list any offenders. Missing entries here could be dangerous
# if you rely on the id for naming or multi-path configuration!
## vendor=ELBONIA, model=borken, options=-b

42
extras/scsi_id/scsi_id.h Normal file
View File

@ -0,0 +1,42 @@
/*
* scsi_id.h
*
* General defines and such for scsi_id
*
* Copyright (C) IBM Corp. 2003
*
* 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.
*
* 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
*/
#define dprintf(format, arg...) \
log_message(LOG_DEBUG, "%s: " format, __FUNCTION__, ## arg)
#define MAX_NAME_LEN 72
#define OFFSET (2 * sizeof(unsigned int))
static inline char *sysfs_get_attr(struct sysfs_class_device *dev,
const char *attr)
{
return sysfs_get_value_from_attributes(dev->directory->attributes,
attr);
}
extern int scsi_get_serial (struct sysfs_class_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)));

View File

@ -0,0 +1,735 @@
/*
* scsi_serial.c
*
* Code related to requesting and getting an id from a scsi device
*
* Copyright (C) IBM Corp. 2003
*
* 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.
*
* 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
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <scsi/sg.h>
#include <sys/libsysfs.h>
#include "scsi_id.h"
#include "scsi.h"
/*
* A priority based list of id, naa, and binary/ascii for the identifier
* descriptor in VPD page 0x83.
*
* Brute force search for a match starting with the first value in the
* following id_search_list. This is not a performance issue, since there
* is normally one or some small number of descriptors.
*/
static const struct scsi_id_search_values id_search_list[] = {
{ SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY },
{ SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII },
{ SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY },
{ SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII },
/*
* Devices already exist using NAA values that are now marked
* reserved. These should not conflict with other values, or it is
* a bug in the device. As long as we find the IEEE extended one
* first, we really don't care what other ones are used. Using
* don't care here means that a device that returns multiple
* non-IEEE descriptors in a random order will get different
* names.
*/
{ SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
{ SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
{ SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
{ SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
{ SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
{ SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
{ SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY },
{ SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII },
};
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.
*/
#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
#define DRIVER_TIMEOUT 0x06
#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
/* The following "category" function returns one of the following */
#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */
#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */
#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */
#define SG_ERR_CAT_TIMEOUT 3
#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */
#define SG_ERR_CAT_SENSE 98 /* Something else in the sense buffer */
#define SG_ERR_CAT_OTHER 99 /* Some other error/warning */
static int sg_err_category_new(int scsi_status, int msg_status, int
host_status, int driver_status, const
unsigned char *sense_buffer, int sb_len)
{
scsi_status &= 0x7e;
/*
* XXX change to return only two values - failed or OK.
*/
/*
* checks msg_status
*/
if (!scsi_status && !msg_status && !host_status && !driver_status)
return SG_ERR_CAT_CLEAN;
if ((scsi_status == SCSI_CHECK_CONDITION) ||
(scsi_status == SCSI_COMMAND_TERMINATED) ||
((driver_status & 0xf) == DRIVER_SENSE)) {
if (sense_buffer && (sb_len > 2)) {
int sense_key;
unsigned char asc;
if (sense_buffer[0] & 0x2) {
sense_key = sense_buffer[1] & 0xf;
asc = sense_buffer[2];
} else {
sense_key = sense_buffer[2] & 0xf;
asc = (sb_len > 12) ? sense_buffer[12] : 0;
}
if (sense_key == RECOVERED_ERROR)
return SG_ERR_CAT_RECOVERED;
else if (sense_key == UNIT_ATTENTION) {
if (0x28 == asc)
return SG_ERR_CAT_MEDIA_CHANGED;
if (0x29 == asc)
return SG_ERR_CAT_RESET;
}
}
return SG_ERR_CAT_SENSE;
}
if (!host_status) {
if ((host_status == DID_NO_CONNECT) ||
(host_status == DID_BUS_BUSY) ||
(host_status == DID_TIME_OUT))
return SG_ERR_CAT_TIMEOUT;
}
if (!driver_status) {
if (driver_status == DRIVER_TIMEOUT)
return SG_ERR_CAT_TIMEOUT;
}
return SG_ERR_CAT_OTHER;
}
static int sg_err_category3(struct sg_io_hdr *hp)
{
return sg_err_category_new(hp->status, hp->msg_status,
hp->host_status, hp->driver_status,
hp->sbp, hp->sb_len_wr);
}
static int scsi_dump_sense(struct sysfs_class_device *scsi_dev,
struct sg_io_hdr *io)
{
unsigned char *sense_buffer;
int s;
int sb_len;
int code;
int sense_class;
int sense_key;
int descriptor_format;
int asc, ascq;
#ifdef DUMP_SENSE
char out_buffer[256];
int i, j;
#endif
/*
* Figure out and print the sense key, asc and ascq.
*
* If you want to suppress these for a particular drive model, add
* a black list entry in the scsi_id config file.
*
* XXX We probably need to: lookup the sense/asc/ascq in a retry
* table, and if found return 1 (after dumping the sense, asc, and
* ascq). So, if/when we get something like a power on/reset,
* we'll retry the command.
*/
dprintf("got check condition\n");
sb_len = io->sb_len_wr;
if (sb_len < 1) {
log_message(LOG_WARNING, "%s: sense buffer empty\n",
scsi_dev->name);
return -1;
}
sense_buffer = io->sbp;
sense_class = (sense_buffer[0] >> 4) & 0x07;
code = sense_buffer[0] & 0xf;
if (sense_class == 7) {
/*
* extended sense data.
*/
s = sense_buffer[7] + 8;
if (sb_len < s) {
log_message(LOG_WARNING,
"%s: sense buffer too small %d bytes,"
" %d bytes too short\n", scsi_dev->name,
sb_len, s - sb_len);
return -1;
}
if ((code == 0x0) || (code == 0x1)) {
descriptor_format = 0;
sense_key = sense_buffer[2] & 0xf;
if (s < 14) {
/*
* Possible?
*/
log_message(LOG_WARNING, "%s: sense result too"
" small %d bytes\n",
scsi_dev->name, s);
return -1;
}
asc = sense_buffer[12];
ascq = sense_buffer[13];
} else if ((code == 0x2) || (code == 0x3)) {
descriptor_format = 1;
sense_key = sense_buffer[1] & 0xf;
asc = sense_buffer[2];
ascq = sense_buffer[3];
} else {
log_message(LOG_WARNING,
"%s: invalid sense code 0x%x\n",
scsi_dev->name, code);
return -1;
}
log_message(LOG_WARNING,
"%s: sense key 0x%x ASC 0x%x ASCQ 0x%x\n",
scsi_dev->name, sense_key, asc, ascq);
} else {
if (sb_len < 4) {
log_message(LOG_WARNING,
"%s: sense buffer too small %d bytes, %d bytes too short\n",
scsi_dev->name, sb_len, 4 - sb_len);
return -1;
}
if (sense_buffer[0] < 15)
log_message(LOG_WARNING, "%s: old sense key: 0x%x\n",
scsi_dev->name, sense_buffer[0] & 0x0f);
else
log_message(LOG_WARNING, "%s: sense = %2x %2x\n",
scsi_dev->name, sense_buffer[0],
sense_buffer[2]);
log_message(LOG_WARNING,
"%s: non-extended sense class %d code 0x%0x ",
scsi_dev->name, sense_class, code);
}
#ifdef DUMP_SENSE
for (i = 0, j = 0; (i < s) && (j < 254); i++) {
dprintf("i %d, j %d\n", i, j);
out_buffer[j++] = hex_str[(sense_buffer[i] & 0xf0) >> 4];
out_buffer[j++] = hex_str[sense_buffer[i] & 0x0f];
out_buffer[j++] = ' ';
}
out_buffer[j] = '\0';
log_message(LOG_WARNING, "%s: sense dump:\n", scsi_dev->name);
log_message(LOG_WARNING, "%s: %s\n", scsi_dev->name, out_buffer);
#endif
return -1;
}
static int scsi_dump(struct sysfs_class_device *scsi_dev, struct sg_io_hdr *io)
{
if (!io->status && !io->host_status && !io->msg_status &&
!io->driver_status) {
/*
* Impossible, should not be called.
*/
log_message(LOG_WARNING, "%s: called with no error\n",
__FUNCTION__);
return -1;
}
log_message(LOG_WARNING, "%s: sg_io failed status 0x%x 0x%x 0x%x 0x%x\n",
scsi_dev->name, io->driver_status, io->host_status,
io->msg_status, io->status);
if (io->status == SCSI_CHECK_CONDITION)
return scsi_dump_sense(scsi_dev, io);
else
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)
{
unsigned char inq_cmd[INQUIRY_CMDLEN] =
{ INQUIRY_CMD, evpd, page, 0, buflen, 0 };
unsigned char sense[SENSE_BUFF_LEN];
struct sg_io_hdr io_hdr;
int retval;
unsigned char *inq;
unsigned char *buffer;
int retry = 3; /* rather random */
if (buflen > 255) {
log_message(LOG_WARNING, "buflen %d too long\n", buflen);
return -1;
}
inq = malloc(OFFSET + sizeof (inq_cmd) + 512);
memset(inq, 0, OFFSET + sizeof (inq_cmd) + 512);
buffer = inq + OFFSET;
resend:
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(inq_cmd);
io_hdr.mx_sb_len = sizeof(sense);
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = buflen;
io_hdr.dxferp = buffer;
io_hdr.cmdp = inq_cmd;
io_hdr.sbp = sense;
io_hdr.timeout = DEF_TIMEOUT;
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
log_message(LOG_WARNING, "%s ioctl failed: %s\n",
scsi_dev->name, strerror(errno));
return -1;
}
retval = sg_err_category3(&io_hdr);
switch (retval) {
case SG_ERR_CAT_CLEAN:
case SG_ERR_CAT_RECOVERED:
retval = 0;
break;
default:
retval = scsi_dump(scsi_dev, &io_hdr);
}
if (!retval) {
retval = buflen;
memcpy(buf, buffer, retval);
} else if (retval > 0) {
if (--retry > 0) {
dprintf("%s: Retrying ...\n", scsi_dev->name);
goto resend;
}
retval = -1;
}
free(inq);
return retval;
}
/*
* XXX maybe move all these to an sg_io.c file.
*
* Ending here.
*/
int do_scsi_page0_inquiry(struct sysfs_class_device *scsi_dev, int fd,
char *buffer, int len)
{
int retval;
char *vendor;
memset(buffer, 0, len);
retval = scsi_inquiry(scsi_dev, fd, 1, 0x0, buffer, len);
if (retval < 0)
return 1;
if (buffer[1] != 0) {
log_message(LOG_WARNING, "%s: page 0 not available.\n",
scsi_dev->name);
return 1;
}
if (buffer[3] > len) {
log_message(LOG_WARNING, "%s: page 0 buffer too long %d",
scsi_dev->name, buffer[3]);
return 1;
}
/*
* Following check is based on code once included in the 2.5.x
* kernel.
*
* Some ill behaved devices return the standard inquiry here
* rather than the evpd data, snoop the data to verify.
*/
if (buffer[3] > MODEL_LENGTH) {
/*
* If the vendor id appears in the page assume the page is
* invalid.
*/
vendor = sysfs_get_attr(scsi_dev, "vendor");
if (!vendor) {
log_message(LOG_WARNING, "%s: no vendor attribute\n",
scsi_dev->name);
return 1;
}
if (!strncmp(&buffer[VENDOR_LENGTH], vendor, VENDOR_LENGTH)) {
log_message(LOG_WARNING, "%s invalid page0 data\n",
scsi_dev->name);
return 1;
}
}
return 0;
}
/*
* 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)
{
char *attr;
int ind;
attr = sysfs_get_attr(scsi_dev, "vendor");
if (!attr) {
log_message(LOG_WARNING, "%s: no vendor attribute\n",
scsi_dev->name);
return 1;
}
strncpy(serial, attr, VENDOR_LENGTH);
ind = strlen(serial) - 1;
/*
* Remove sysfs added newlines.
*/
if (serial[ind] == '\n')
serial[ind] = '\0';
attr = sysfs_get_attr(scsi_dev, "model");
if (!attr) {
log_message(LOG_WARNING, "%s: no model attribute\n",
scsi_dev->name);
return 1;
}
strncat(serial, attr, MODEL_LENGTH);
ind = strlen(serial) - 1;
if (serial[ind] == '\n')
serial[ind] = '\0';
else
ind++;
/*
* This is not a complete check, since we are using strncat/cpy
* above, ind will never be too large.
*/
if (ind != (VENDOR_LENGTH + MODEL_LENGTH)) {
log_message(LOG_WARNING, "%s: expected length %d, got length %d\n",
scsi_dev->name, (VENDOR_LENGTH + MODEL_LENGTH),
ind);
return 1;
}
return 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)
{
int i, j, len;
/*
* ASSOCIATION must be with the device (value 0)
*/
if ((page_83[1] & 0x30) != 0)
return 1;
if ((page_83[1] & 0x0f) != id_search->id_type)
return 1;
/*
* Possibly check NAA sub-type.
*/
if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) &&
(id_search->naa_type != (page_83[4] & 0xf0) >> 4))
return 1;
/*
* Check for matching code set - ASCII or BINARY.
*/
if ((page_83[0] & 0x0f) != id_search->code_set)
return 1;
/*
* page_83[3]: identifier length
*/
len = page_83[3];
if ((page_83[0] & 0x0f) != SCSI_ID_ASCII)
/*
* If not ASCII, use two bytes for each binary value.
*/
len *= 2;
/*
* Add one byte for the NUL termination, and one for the id_type.
*/
len += 2;
if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
len += VENDOR_LENGTH + MODEL_LENGTH;
if (max_len < len) {
log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
scsi_dev->name, max_len, len);
return 1;
}
serial[0] = hex_str[id_search->id_type];
/*
* Prepend the vendor and model before the id since if it is not
* unique across all vendors and models.
*/
if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
if (prepend_vendor_model(scsi_dev, &serial[1]) < 0) {
dprintf("prepend failed\n");
return 1;
}
i = 4; /* offset to the start of the identifier */
j = strlen(serial);
if ((page_83[0] & 0x0f) == SCSI_ID_ASCII) {
/*
* ASCII descriptor.
*/
while (i < (4 + page_83[3]))
serial[j++] = page_83[i++];
} else {
/*
* Binary descriptor, convert to ASCII, using two bytes of
* ASCII for each byte in the page_83.
*/
while (i < (4 + page_83[3])) {
serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4];
serial[j++] = hex_str[page_83[i] & 0x0f];
i++;
}
}
return 0;
}
static int do_scsi_page83_inquiry(struct sysfs_class_device *scsi_dev, int fd,
char *serial, int len)
{
int retval;
int id_ind, j;
unsigned char page_83[256];
memset(page_83, 0, 256);
retval = scsi_inquiry(scsi_dev, fd, 1, 0x83, page_83, 255);
if (retval < 0)
return 1;
if (page_83[1] != 0x83) {
log_message(LOG_WARNING, "%s: Invalid page 0x83\n",
scsi_dev->name);
return 1;
}
/*
* Search for a match in the prioritized id_search_list.
*/
for (id_ind = 0;
id_ind < sizeof(id_search_list)/sizeof(id_search_list[0]);
id_ind++) {
/*
* Examine each descriptor returned. There is normally only
* one or a small number of descriptors.
*/
for (j = 4; j <= page_83[3] + 3;
j += page_83[j + 3] + 4) {
retval = check_fill_0x83_id(scsi_dev, &page_83[j],
&id_search_list[id_ind],
serial, len);
dprintf("%s id desc %d/%d/%d\n", scsi_dev->name,
id_search_list[id_ind].id_type,
id_search_list[id_ind].naa_type,
id_search_list[id_ind].code_set);
if (!retval) {
dprintf(" used\n");
return retval;
} else if (retval < 0) {
dprintf(" failed\n");
return retval;
} else {
dprintf(" not used\n");
}
}
}
return 1;
}
int do_scsi_page80_inquiry(struct sysfs_class_device *scsi_dev, int fd,
char *serial, int max_len)
{
int retval;
int ser_ind;
int i;
int len;
unsigned char buf[256];
memset(buf, 0, 256);
retval = scsi_inquiry(scsi_dev, fd, 1, 0x80, buf, 255);
if (retval < 0)
return retval;
if (buf[1] != 0x80) {
log_message(LOG_WARNING, "%s: Invalid page 0x80\n",
scsi_dev->name);
return 1;
}
len = 1 + VENDOR_LENGTH + MODEL_LENGTH + buf[3];
if (max_len < len) {
log_message(LOG_WARNING, "%s: length %d too short - need %d\n",
scsi_dev->name, max_len, len);
return 1;
}
/*
* Prepend 'S' to avoid unlikely collision with page 0x83 vendor
* specific type where we prepend '0' + vendor + model.
*/
serial[0] = 'S';
ser_ind = prepend_vendor_model(scsi_dev, &serial[1]);
if (ser_ind < 0)
return 1;
len = buf[3];
for (i = 4; i < len + 4; i++, ser_ind++)
serial[ser_ind] = buf[i];
return 0;
}
int scsi_get_serial (struct sysfs_class_device *scsi_dev, const char *devname,
int page_code, char *serial, int len)
{
unsigned char page0[256];
int fd;
int ind;
int retval;
if (len > 255) {
}
memset(serial, 0, len);
dprintf("opening %s\n", devname);
fd = open(devname, O_RDONLY);
if (fd < 0) {
log_message(LOG_WARNING, "%s cannot open %s: %s\n",
scsi_dev->name, devname, strerror(errno));
return 1;
}
if (page_code == 0x80) {
if (do_scsi_page80_inquiry(scsi_dev, fd, serial, len)) {
retval = 1;
goto completed;
} else {
retval = 0;
goto completed;
}
} else if (page_code == 0x83) {
if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
retval = 1;
goto completed;
} else {
retval = 0;
goto completed;
}
} else if (page_code != 0x00) {
log_message(LOG_WARNING, "%s unsupported page code 0x%d\n",
scsi_dev->name, page_code);
return 1;
}
/*
* Get page 0, the page of the pages. By default, try from best to
* worst of supported pages: 0x83 then 0x80.
*/
if (do_scsi_page0_inquiry(scsi_dev, fd, page0, 255)) {
/*
* Don't try anything else. Black list if a specific page
* should be used for this vendor+model, or maybe have an
* optional fall-back to page 0x80 or page 0x83.
*/
retval = 1;
goto completed;
}
dprintf("%s: Checking page0\n", scsi_dev->name);
for (ind = 4; ind <= page0[3] + 3; ind++)
if (page0[ind] == 0x83)
if (!do_scsi_page83_inquiry(scsi_dev, fd, serial,
len)) {
/*
* Success
*/
retval = 0;
goto completed;
}
for (ind = 4; ind <= page0[3] + 3; ind++)
if (page0[ind] == 0x80)
if (!do_scsi_page80_inquiry(scsi_dev, fd, serial,
len)) {
/*
* Success
*/
retval = 0;
goto completed;
}
retval = 1;
completed:
if (close(fd) < 0)
log_message(LOG_WARNING, "close failed: %s", strerror(errno));
return retval;
}