mirror of
https://github.com/AuxXxilium/eudev.git
synced 2025-01-22 05:32:42 +07:00
[PATCH] add scsi_id "extra" program from Patrick Mansfield <patmans@us.ibm.com>
This commit is contained in:
parent
04a091d47e
commit
c521693b54
340
extras/scsi_id/COPYING
Normal file
340
extras/scsi_id/COPYING
Normal 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
51
extras/scsi_id/Makefile
Normal 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
19
extras/scsi_id/README
Normal 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
16
extras/scsi_id/TODO
Normal 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
1
extras/scsi_id/VERSION
Normal file
@ -0,0 +1 @@
|
||||
0.1
|
96
extras/scsi_id/scsi.h
Normal file
96
extras/scsi_id/scsi.h
Normal 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
827
extras/scsi_id/scsi_id.c
Normal 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);
|
||||
}
|
40
extras/scsi_id/scsi_id.config
Normal file
40
extras/scsi_id/scsi_id.config
Normal 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
42
extras/scsi_id/scsi_id.h
Normal 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)));
|
||||
|
735
extras/scsi_id/scsi_serial.c
Normal file
735
extras/scsi_id/scsi_serial.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user