Camcontrol - A utility for configuring/manipulating the CAM subsystem
Submitted by: "Kenneth D. Merry" <ken@plutotech.com>
This commit is contained in:
parent
76babe507b
commit
525689f102
9
sbin/camcontrol/Makefile
Normal file
9
sbin/camcontrol/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
PROG= camcontrol
|
||||
SRCS= camcontrol.c modeedit.c
|
||||
MAN8= camcontrol.8
|
||||
|
||||
CFLAGS+= -I${.CURDIR}/../../sys
|
||||
DPADD= ${LIBCAM}
|
||||
LDADD+= -lcam
|
||||
|
||||
.include <bsd.prog.mk>
|
437
sbin/camcontrol/camcontrol.8
Normal file
437
sbin/camcontrol/camcontrol.8
Normal file
@ -0,0 +1,437 @@
|
||||
.\"
|
||||
.\" Copyright (c) 1998 Kenneth D. Merry.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\" 3. The name of the author may not be used to endorse or promote products
|
||||
.\" derived from this software without specific prior written permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $Id$
|
||||
.\"
|
||||
.Dd September 14, 1998
|
||||
.Dt CAMCONTROL 8
|
||||
.Os FreeBSD 3.0
|
||||
.Sh NAME
|
||||
.Nm camcontrol
|
||||
.Nd CAM control program
|
||||
.Sh SYNOPSIS
|
||||
.Nm camcontrol
|
||||
.Aq command
|
||||
.Op generic args
|
||||
.Op command args
|
||||
.Nm camcontrol
|
||||
devlist
|
||||
.Op Fl v
|
||||
.Nm camcontrol
|
||||
periphlist
|
||||
.Op Fl n Ar dev_name
|
||||
.Op Fl u Ar unit_number
|
||||
.Nm camcontrol
|
||||
tur
|
||||
.Op generic args
|
||||
.Nm camcontrol
|
||||
inquiry
|
||||
.Op generic args
|
||||
.Op Fl D
|
||||
.Op Fl S
|
||||
.Op Fl R
|
||||
.Nm camcontrol
|
||||
start
|
||||
.Op generic args
|
||||
.Nm camcontrol
|
||||
stop
|
||||
.Op generic args
|
||||
.Nm camcontrol
|
||||
eject
|
||||
.Op generic args
|
||||
.Nm camcontrol
|
||||
rescan
|
||||
.Aq bus Ns Op :target:lun
|
||||
.Nm camcontrol
|
||||
defects
|
||||
.Op generic args
|
||||
.Aq Fl f Ar format
|
||||
.Op Fl P
|
||||
.Op Fl G
|
||||
.Nm camcontrol
|
||||
modepage
|
||||
.Op generic args
|
||||
.Aq Fl m Ar page
|
||||
.Op Fl P Ar pagectl
|
||||
.Op Fl e
|
||||
.Op Fl d
|
||||
.Nm camcontrol
|
||||
cmd
|
||||
.Op generic args
|
||||
.Aq Fl c Ar cmd Op args
|
||||
.Op Fl i Ar len Ar fmt
|
||||
.Bk -words
|
||||
.Op Fl o Ar len Ar fmt Op args
|
||||
.Ek
|
||||
.Nm camcontrol
|
||||
debug
|
||||
.Op Fl I
|
||||
.Op Fl T
|
||||
.Op Fl S
|
||||
.Aq all|off|bus Ns Op :target Ns Op :lun
|
||||
.Sh DESCRIPTION
|
||||
.Nm camcontrol
|
||||
is a utility designed to provide a way for users to access and control the
|
||||
.Tn FreeBSD
|
||||
CAM subsystem.
|
||||
.Pp
|
||||
.Nm camcontrol
|
||||
can cause a loss of data and/or system crashes if used improperly. Even
|
||||
expert users are encouraged to excercise caution when using this command.
|
||||
Novice users should stay away from this utility.
|
||||
.Pp
|
||||
.Nm camcontrol
|
||||
has a number of primary functions, most of which take some generic
|
||||
arguments:
|
||||
.Bl -tag -width 01234567890123
|
||||
.It Fl C Ar count
|
||||
SCSI command retry count. In order for this to work, error recovery
|
||||
.Po
|
||||
.Fl E
|
||||
.Pc
|
||||
must be turned on.
|
||||
.It Fl E
|
||||
Instruct the kernel to perform generic SCSI error recovery for the given
|
||||
command. This is needed in order for the retry count
|
||||
.Po
|
||||
.Fl C
|
||||
.Pc
|
||||
to be honored. Other than retrying commands, the generic error recovery in
|
||||
the code will generally attempt to spin up drives that are not spinning.
|
||||
It may take some other actions, depending upon the sense code returned from
|
||||
the command.
|
||||
.It Fl n Ar dev_name
|
||||
Specify the device type to operate on. The default is
|
||||
.Em da .
|
||||
.It Fl t Ar timeout
|
||||
SCSI command timeout in seconds. This overrides the default timeout for
|
||||
any given command.
|
||||
.It Fl u Ar unit_number
|
||||
Specify the device unit number. The default is 0.
|
||||
.It Fl v
|
||||
Be verbose, print out sense information for failed SCSI commands.
|
||||
.El
|
||||
.Pp
|
||||
Primay command functions:
|
||||
.Bl -tag -width periphlist
|
||||
.It devlist
|
||||
List all physical devices (logical units) attached to the CAM subsystem.
|
||||
This also includes a list of peripheral drivers attached to each device.
|
||||
With the
|
||||
.Fl v
|
||||
argument, SCSI bus number, adapter name and unit numbers are printed as
|
||||
well.
|
||||
.It periphlist
|
||||
List all peripheral drivers attached to a given physical device (logical
|
||||
unit).
|
||||
.It tur
|
||||
Send the SCSI test unit ready (0x00) command to the given device.
|
||||
.Nm camcontrol
|
||||
will report whether the device is ready or not.
|
||||
.It inquiry
|
||||
Send a SCSI inquiry command (0x12) to a device. By default,
|
||||
.Nm camcontrol
|
||||
will print out the standard inquiry data, device serial number, and
|
||||
transfer rate information. The user can specify that only certain types of
|
||||
inquiry data be printed:
|
||||
.Bl -tag -width 1234
|
||||
.It Fl D
|
||||
Get the standard inquiry data.
|
||||
.It Fl S
|
||||
Print out the serial number. If this flag is the only one specified,
|
||||
.Nm camcontrol
|
||||
will not print out "Serial Number" before the value returned by the drive.
|
||||
This is to aid in script writing.
|
||||
.It Fl R
|
||||
Print out transfer rate information.
|
||||
.El
|
||||
.It start
|
||||
Send the SCSI Start/Stop Unit (0x1B) command to the given device with the
|
||||
start bit set.
|
||||
.It stop
|
||||
Send the SCSI Start/Stop Unit (0x1B) command to the given device with the
|
||||
start bit cleared.
|
||||
.It eject
|
||||
Send the SCSI Start/Stop Unit (0x1B) command to the given device with the
|
||||
start bit cleared and the eject bit set.
|
||||
.It rescan
|
||||
Tell the kernel to scan the given bus (XPT_SCAN_BUS), or bus:target:lun
|
||||
(XPT_SCAN_LUN) for new devices or devices that have gone away. The user
|
||||
may only specify a bus to scan, or a lun. Scanning all luns on a target
|
||||
isn't supported.
|
||||
.It defects
|
||||
Send the SCSI READ DEFECT DATA (10) command (0x37) to the given device, and
|
||||
print out any combination of: the total number of defects, the primary
|
||||
defect list (PLIST), and the grown defect list (GLIST).
|
||||
.Bl -tag -width 01234567890
|
||||
.It Fl f Ar format
|
||||
The three format options are:
|
||||
.Em block ,
|
||||
to print out the list as logical blocks,
|
||||
.Em bfi ,
|
||||
to print out the list in bytes from index format, and
|
||||
.Em phys ,
|
||||
to print out the list in physical sector format. The format argument is
|
||||
required. Most drives support the physical sector format. Some drives
|
||||
support the logical block format. Many drives, if they don't support the
|
||||
requested format, return the data in an alternate format, along with sense
|
||||
information indicating that the requested data format isn't supported.
|
||||
.Nm camcontrol
|
||||
attempts to detect this, and print out whatever format the drive returns.
|
||||
If the drive uses a non-standard sense code to report that it doesn't
|
||||
support the requested format,
|
||||
.Nm camcontrol
|
||||
will probably see the error as a failure to complete the request.
|
||||
.It Fl G
|
||||
Print out the grown defect list. This is a list of bad blocks that have
|
||||
been remapped since the disk left the factory.
|
||||
.It Fl P
|
||||
Print out the primary defect list.
|
||||
.El
|
||||
.Pp
|
||||
If neither
|
||||
.Fl P
|
||||
nor
|
||||
.Fl G
|
||||
is specified,
|
||||
.Nm camcontrol
|
||||
will print out the number of defects given in the READ DEFECT DATA header
|
||||
returned from the drive.
|
||||
.It modepage
|
||||
Allows the user to display and optionally edit a SCSI mode page. The mode
|
||||
page formats are located in
|
||||
.Pa /usr/share/misc/scsi_modes .
|
||||
This can be overridden by specifying a different file in the
|
||||
.Ev SCSI_MODES
|
||||
environment variable. The modepage command takes several arguments:
|
||||
.Bl -tag -width 012345678901
|
||||
.It Fl B
|
||||
Disable block descriptors for mode sense.
|
||||
.It Fl e
|
||||
This flag allows the user to edit values in the mode page.
|
||||
.It Fl m Ar mode_page
|
||||
This specifies the number of the mode page the user would like to view
|
||||
and/or edit. This argument is mandatory.
|
||||
.It Fl P page_ctl
|
||||
This allows the user to specify the page control field. Possible values are:
|
||||
.Bl -tag -width xxx -compact
|
||||
.It 1
|
||||
Current values
|
||||
.It 2
|
||||
Changeable values
|
||||
.It 3
|
||||
Default values
|
||||
.It 4
|
||||
Saved values
|
||||
.El
|
||||
.El
|
||||
.It cmd
|
||||
Allows the user to send an arbitrary SCSI CDB to any device. The cmd
|
||||
function requires the
|
||||
.Fl c
|
||||
argument to specify the CDB. Other arguments are optional, depending on
|
||||
the command type. The command and data specification syntax is documented
|
||||
in
|
||||
.Xr cam 3 .
|
||||
NOTE: If the CDB specified causes data to be transfered to or from the
|
||||
SCSI device in question, you MUST specify either
|
||||
.Fl i
|
||||
or
|
||||
.Fl o .
|
||||
.Bl -tag -width 01234567890123456
|
||||
.It Fl c Ar cmd Op args
|
||||
This specifies the SCSI CDB. CDBs may be 6, 10, 12 or 16 bytes.
|
||||
.It Fl i Ar len Ar fmt
|
||||
This specifies the amount of data to read, and how it should be displayed.
|
||||
If the format is
|
||||
.Sq - ,
|
||||
.Ar len
|
||||
bytes of data will be read from the device and written to standard output.
|
||||
.It Fl o Ar len Ar fmt Op args
|
||||
This specifies the amount of data to be written to a device, and the data
|
||||
that is to be written. If the format is
|
||||
.Sq - ,
|
||||
.Ar len
|
||||
bytes of data will be read from standard input and written to the device.
|
||||
.El
|
||||
.It debug
|
||||
Turn on CAM debugging printfs in the kernel. This requires options CAMDEBUG
|
||||
in your kernel config file. WARNING: enabling debugging printfs currently
|
||||
causes an EXTREME number of kernel printfs. You may have difficulty
|
||||
turning off the debugging printfs once they start, since the kernel will be
|
||||
busy printing messages and unable to service other requests quickly.
|
||||
The debug function takes a number of arguments:
|
||||
.Bl -tag -width 012345678901234567
|
||||
.It Fl I
|
||||
Enable CAM_DEBUG_INFO printfs.
|
||||
.It Fl T
|
||||
Enable CAM_DEBUG_TRACE printfs.
|
||||
.It Fl S
|
||||
Enable CAM_DEBUG_SUBTRACE printfs.
|
||||
.It all
|
||||
Enable debugging for all devices.
|
||||
.It off
|
||||
Turn off debugging for all devices
|
||||
.It bus Ns Op :target Ns Op :lun
|
||||
Turn on debugging for the given bus, target or lun. If the lun or target
|
||||
and lun are not specified, they are wildcarded. (i.e., just specifying a
|
||||
bus turns on debugging printfs for all devices on that bus.)
|
||||
.El
|
||||
.El
|
||||
.Sh ENVIRONMENT
|
||||
The
|
||||
.Ev SCSI_MODES
|
||||
variable allows the user to specify an alternate mode page format file.
|
||||
.Pp
|
||||
The
|
||||
.Ev EDITOR
|
||||
variable determines which text editor
|
||||
.Nm camcontrol
|
||||
starts when editing mode pages.
|
||||
.Sh FILES
|
||||
.Bl -tag -width /usr/share/misc/scsi_modes -compact
|
||||
.It Pa /usr/share/misc/scsi_modes
|
||||
is the SCSI mode format database.
|
||||
.It Pa /dev/xpt0
|
||||
is the transport layer device.
|
||||
.It Pa /dev/pass*
|
||||
are the CAM application passthrough devices.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
.Dl camcontrol eject -n cd -u 1 -v
|
||||
.Pp
|
||||
Eject the CD from cd1, and print SCSI sense information if the command
|
||||
fails.
|
||||
.Pp
|
||||
.Dl camcontrol tur
|
||||
.Pp
|
||||
Send the SCSI test unit ready command to da0.
|
||||
.Nm camcontrol
|
||||
will report whether the disk is ready, but will not display sense
|
||||
information if the command fails since the
|
||||
.Fl v
|
||||
switch was not specified.
|
||||
.Pp
|
||||
.Bd -literal -offset foobar
|
||||
camcontrol tur -n da -u 1 -E -C 4 -t 50 -v
|
||||
.Ed
|
||||
.Pp
|
||||
Send a test unit ready command to da1. Enable kernel error recovery.
|
||||
Specify a retry count of 4, and a timeout of 50 seconds. Enable sense
|
||||
printing (with the
|
||||
.Fl v
|
||||
flag) if the command fails. Since error recovery is turned on, the
|
||||
disk will be spun up if it is not currently spinning.
|
||||
.Nm camcontrol
|
||||
will report whether or the disk is ready.
|
||||
.Bd -literal -offset foobar
|
||||
camcontrol cmd -n cd -u 1 -v -c "3C 00 00 00 00 00 00 00 0e 00" \e
|
||||
-i 0xe "s1 i3 i1 i1 i1 i1 i1 i1 i1 i1 i1 i1"
|
||||
.Ed
|
||||
.Pp
|
||||
Issue a READ BUFFER command (0x3C) to cd1. Display the buffer size of cd1,
|
||||
and display the first 10 bytes from the cache on cd1. Display SCSI sense
|
||||
information if the command fails.
|
||||
.Pp
|
||||
.Bd -literal -offset foobar
|
||||
camcontrol cmd -n cd -u u -v -c "3B 00 00 00 00 00 00 00 0e 00" \e
|
||||
-o 14 "00 00 00 00 1 2 3 4 5 6 v v v v" 7 8 9 8
|
||||
.Ed
|
||||
.Pp
|
||||
Issue a WRITE BUFFER (0x3B) command to cd1. Write out 10 bytes of data,
|
||||
not including the (reserved) 4 byte header. Print out sense information if
|
||||
the command fails. Be very careful with this command, improper use may
|
||||
cause data corruption.
|
||||
.Pp
|
||||
.Dl camcontrol modepage -n da -u 3 -m 1 -e -P 3
|
||||
.Pp
|
||||
Edit mode page 1 (the Read-Write Error Recover page) for da3, and save the
|
||||
settings on the drive. Mode page 1 contains a disk drive's auto read and
|
||||
write reallocation settings, among other things.
|
||||
.Pp
|
||||
.Dl camcontrol rescan 0
|
||||
.Pp
|
||||
Rescan SCSI bus 0 for devices that have been added, removed or changed.
|
||||
.Pp
|
||||
.Dl camcontrol rescan 0:1:0
|
||||
.Pp
|
||||
Rescan SCSI bus 0, target 1, lun 0 to see if it has been added, removed, or
|
||||
changed.
|
||||
.Sh SEE ALSO
|
||||
.Xr cam 3 ,
|
||||
.Xr pass 4 ,
|
||||
.Xr cam 9 ,
|
||||
.Xr xpt 9
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm camcontrol
|
||||
command first appeared in
|
||||
.Fx 3.0 .
|
||||
.Pp
|
||||
The mode page editing code and arbitrary SCSI command code are based upon
|
||||
code in the old
|
||||
.Xr scsi 8
|
||||
utility and
|
||||
.Xr scsi 3
|
||||
library, written by Julian Ellischer and Peter Dufault. The
|
||||
.Xr scsi 8
|
||||
first appeared in 386BSD 0.1.2.4, and first appeared in
|
||||
.Tn FreeBSD
|
||||
in
|
||||
.Fx 2.0.5 .
|
||||
.Sh AUTHORS
|
||||
.An Kenneth Merry Aq ken@FreeBSD.ORG
|
||||
.Sh BUGS
|
||||
Most of the man page cross references don't exist yet. This will be fixed
|
||||
soon.
|
||||
.Pp
|
||||
The code that parses the generic command line arguments doesn't know that
|
||||
some of the subcommands take multiple arguments. So if, for instance, you
|
||||
tried something like this:
|
||||
.Bd -literal -offset foobar
|
||||
camcontrol -n da -u 1 -c "00 00 00 00 00 v" 0x00 -v
|
||||
.Ed
|
||||
.Pp
|
||||
The sense information from the test unit ready command would not get
|
||||
printed out, since the first
|
||||
.Xr getopt 3
|
||||
call in
|
||||
.Nm camcontrol
|
||||
bails out when it sees the second argument to
|
||||
.Fl c
|
||||
.Po
|
||||
0x00
|
||||
.Pc ,
|
||||
above. Fixing this behavior would take some gross code, or changes to the
|
||||
.Xr getopt 3
|
||||
interface. The best way to circumvent this problem is to always make sure
|
||||
to specify generic
|
||||
.Nm camcontrol
|
||||
arguments before any command-specific arguments.
|
||||
.Pp
|
||||
It might be nice to add a way to allow users to specify devices by
|
||||
bus/target/lun or by device string (e.g. "da1").
|
2048
sbin/camcontrol/camcontrol.c
Normal file
2048
sbin/camcontrol/camcontrol.c
Normal file
File diff suppressed because it is too large
Load Diff
54
sbin/camcontrol/camcontrol.h
Normal file
54
sbin/camcontrol/camcontrol.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 1998 Kenneth D. Merry.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef _CAMCONTROL_H
|
||||
#define _CAMCONTROL_H
|
||||
/*
|
||||
* get_hook: Structure for evaluating args in a callback.
|
||||
*/
|
||||
struct get_hook
|
||||
{
|
||||
int argc;
|
||||
char **argv;
|
||||
int got;
|
||||
};
|
||||
|
||||
void mode_sense(struct cam_device *device, int mode_page, int page_control,
|
||||
int dbd, int retry_count, int timeout, u_int8_t *data,
|
||||
int datalen);
|
||||
void mode_select(struct cam_device *device, int save_pages, int retry_count,
|
||||
int timeout, u_int8_t *data, int datalen);
|
||||
void mode_edit(struct cam_device *device, int page, int page_control, int dbd,
|
||||
int edit, int retry_count, int timeout);
|
||||
char *cget(void *hook, char *name);
|
||||
int iget(void *hook, char *name);
|
||||
void arg_put(void *hook, int letter, void *arg, int count, char *name);
|
||||
void usage(void);
|
||||
#endif /* _CAMCONTROL_H */
|
500
sbin/camcontrol/modeedit.c
Normal file
500
sbin/camcontrol/modeedit.c
Normal file
@ -0,0 +1,500 @@
|
||||
/*
|
||||
* Written By Julian ELischer
|
||||
* Copyright julian Elischer 1993.
|
||||
* Permission is granted to use or redistribute this file in any way as long
|
||||
* as this notice remains. Julian Elischer does not guarantee that this file
|
||||
* is totally correct for any given task and users of this file must
|
||||
* accept responsibility for any damage that occurs from the application of this
|
||||
* file.
|
||||
*
|
||||
* (julian@tfs.com julian@dialix.oz.au)
|
||||
*
|
||||
* User SCSI hooks added by Peter Dufault:
|
||||
*
|
||||
* Copyright (c) 1994 HD Associates
|
||||
* (contact: dufault@hda.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of HD Associates
|
||||
* may not be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* Taken from the original scsi(8) program.
|
||||
* from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $";
|
||||
*/
|
||||
#ifndef lint
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/file.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cam/cam.h>
|
||||
#include <cam/cam_ccb.h>
|
||||
#include <camlib.h>
|
||||
#include "camcontrol.h"
|
||||
|
||||
int verbose = 0;
|
||||
|
||||
/* iget: Integer argument callback
|
||||
*/
|
||||
int
|
||||
iget(void *hook, char *name)
|
||||
{
|
||||
struct get_hook *h = (struct get_hook *)hook;
|
||||
int arg;
|
||||
|
||||
if (h->got >= h->argc)
|
||||
{
|
||||
fprintf(stderr, "Expecting an integer argument.\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
arg = strtol(h->argv[h->got], 0, 0);
|
||||
h->got++;
|
||||
|
||||
if (verbose && name && *name)
|
||||
printf("%s: %d\n", name, arg);
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
/* cget: char * argument callback
|
||||
*/
|
||||
char *
|
||||
cget(void *hook, char *name)
|
||||
{
|
||||
struct get_hook *h = (struct get_hook *)hook;
|
||||
char *arg;
|
||||
|
||||
if (h->got >= h->argc)
|
||||
{
|
||||
fprintf(stderr, "Expecting a character pointer argument.\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
arg = h->argv[h->got];
|
||||
h->got++;
|
||||
|
||||
if (verbose && name)
|
||||
printf("cget: %s: %s", name, arg);
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
/* arg_put: "put argument" callback
|
||||
*/
|
||||
void
|
||||
arg_put(void *hook, int letter, void *arg, int count, char *name)
|
||||
{
|
||||
if (verbose && name && *name)
|
||||
printf("%s: ", name);
|
||||
|
||||
switch(letter)
|
||||
{
|
||||
case 'i':
|
||||
case 'b':
|
||||
printf("%d ", (int)arg);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
case 'z':
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = malloc(count + 1);
|
||||
|
||||
bzero(p, count +1);
|
||||
strncpy(p, (char *)arg, count);
|
||||
if (letter == 'z')
|
||||
{
|
||||
int i;
|
||||
for (i = count - 1; i >= 0; i--)
|
||||
if (p[i] == ' ')
|
||||
p[i] = 0;
|
||||
else
|
||||
break;
|
||||
}
|
||||
printf("%s ", p);
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unknown format letter: '%c'\n", letter);
|
||||
}
|
||||
if (verbose)
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
#define START_ENTRY '{'
|
||||
#define END_ENTRY '}'
|
||||
|
||||
static void
|
||||
skipwhite(FILE *f)
|
||||
{
|
||||
int c;
|
||||
|
||||
skip_again:
|
||||
|
||||
while (isspace(c = getc(f)))
|
||||
;
|
||||
|
||||
if (c == '#') {
|
||||
while ((c = getc(f)) != '\n' && c != EOF)
|
||||
;
|
||||
goto skip_again;
|
||||
}
|
||||
|
||||
ungetc(c, f);
|
||||
}
|
||||
|
||||
/* mode_lookup: Lookup a format description for a given page.
|
||||
*/
|
||||
char *mode_db = "/usr/share/misc/scsi_modes";
|
||||
static char *
|
||||
mode_lookup(int page)
|
||||
{
|
||||
char *new_db;
|
||||
FILE *modes;
|
||||
int match, next, found, c;
|
||||
static char fmt[4096]; /* XXX This should be with strealloc */
|
||||
int page_desc;
|
||||
new_db = getenv("SCSI_MODES");
|
||||
|
||||
if (new_db)
|
||||
mode_db = new_db;
|
||||
|
||||
modes = fopen(mode_db, "r");
|
||||
if (modes == 0)
|
||||
return 0;
|
||||
|
||||
next = 0;
|
||||
found = 0;
|
||||
|
||||
while (!found) {
|
||||
|
||||
skipwhite(modes);
|
||||
|
||||
if (fscanf(modes, "%i", &page_desc) != 1)
|
||||
break;
|
||||
|
||||
if (page_desc == page)
|
||||
found = 1;
|
||||
|
||||
skipwhite(modes);
|
||||
if (getc(modes) != START_ENTRY)
|
||||
errx(1, "expected %c", START_ENTRY);
|
||||
|
||||
match = 1;
|
||||
while (match != 0) {
|
||||
c = getc(modes);
|
||||
if (c == EOF) {
|
||||
warnx("expected %c", END_ENTRY);
|
||||
}
|
||||
|
||||
if (c == START_ENTRY) {
|
||||
match++;
|
||||
}
|
||||
if (c == END_ENTRY) {
|
||||
match--;
|
||||
if (match == 0)
|
||||
break;
|
||||
}
|
||||
if (found && c != '\n') {
|
||||
if (next >= sizeof(fmt))
|
||||
errx(1, "buffer overflow");
|
||||
|
||||
fmt[next++] = (u_char)c;
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt[next] = 0;
|
||||
|
||||
return (found) ? fmt : 0;
|
||||
}
|
||||
|
||||
/* -------- edit: Mode Select Editor ---------
|
||||
*/
|
||||
struct editinfo
|
||||
{
|
||||
int can_edit;
|
||||
int default_value;
|
||||
} editinfo[64]; /* XXX Bogus fixed size */
|
||||
|
||||
static int editind;
|
||||
volatile int edit_opened;
|
||||
static FILE *edit_file;
|
||||
static char edit_name[L_tmpnam];
|
||||
|
||||
static inline void
|
||||
edit_rewind(void)
|
||||
{
|
||||
editind = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
edit_done(void)
|
||||
{
|
||||
int opened;
|
||||
|
||||
sigset_t all, prev;
|
||||
sigfillset(&all);
|
||||
|
||||
(void)sigprocmask(SIG_SETMASK, &all, &prev);
|
||||
|
||||
opened = (int)edit_opened;
|
||||
edit_opened = 0;
|
||||
|
||||
(void)sigprocmask(SIG_SETMASK, &prev, 0);
|
||||
|
||||
if (opened)
|
||||
{
|
||||
if (fclose(edit_file))
|
||||
warn("%s", edit_name);
|
||||
if (unlink(edit_name))
|
||||
warn("%s", edit_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
edit_init(void)
|
||||
{
|
||||
edit_rewind();
|
||||
if (tmpnam(edit_name) == 0)
|
||||
errx(1, "tmpnam failed");
|
||||
if ((edit_file = fopen(edit_name, "w")) == 0)
|
||||
err(1, "%s", edit_name);
|
||||
edit_opened = 1;
|
||||
|
||||
atexit(edit_done);
|
||||
}
|
||||
|
||||
static void
|
||||
edit_check(void *hook, int letter, void *arg, int count, char *name)
|
||||
{
|
||||
if (letter != 'i' && letter != 'b')
|
||||
errx(1, "can't edit format %c", letter);
|
||||
|
||||
if (editind >= sizeof(editinfo) / sizeof(editinfo[0]))
|
||||
errx(1, "edit table overflow");
|
||||
|
||||
editinfo[editind].can_edit = ((int)arg != 0);
|
||||
editind++;
|
||||
}
|
||||
|
||||
static void
|
||||
edit_defaults(void *hook, int letter, void *arg, int count, char *name)
|
||||
{
|
||||
if (letter != 'i' && letter != 'b')
|
||||
errx(1, "can't edit format %c", letter);
|
||||
|
||||
editinfo[editind].default_value = ((int)arg);
|
||||
editind++;
|
||||
}
|
||||
|
||||
static void
|
||||
edit_report(void *hook, int letter, void *arg, int count, char *name)
|
||||
{
|
||||
if (editinfo[editind].can_edit) {
|
||||
if (letter != 'i' && letter != 'b')
|
||||
errx(1, "can't report format %c", letter);
|
||||
|
||||
fprintf(edit_file, "%s: %d\n", name, (int)arg);
|
||||
}
|
||||
|
||||
editind++;
|
||||
}
|
||||
|
||||
static int
|
||||
edit_get(void *hook, char *name)
|
||||
{
|
||||
int arg = editinfo[editind].default_value;
|
||||
|
||||
if (editinfo[editind].can_edit) {
|
||||
char line[80];
|
||||
if (fgets(line, sizeof(line), edit_file) == 0)
|
||||
err(1, "fgets");
|
||||
|
||||
line[strlen(line) - 1] = 0;
|
||||
|
||||
if (strncmp(name, line, strlen(name)) != 0)
|
||||
errx(1, "expected \"%s\" and read \"%s\"", name, line);
|
||||
|
||||
arg = strtoul(line + strlen(name) + 2, 0, 0);
|
||||
}
|
||||
|
||||
editind++;
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void
|
||||
edit_edit(void)
|
||||
{
|
||||
char *system_line;
|
||||
char *editor = getenv("EDITOR");
|
||||
if (!editor)
|
||||
editor = "vi";
|
||||
|
||||
fclose(edit_file);
|
||||
|
||||
system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
|
||||
sprintf(system_line, "%s %s", editor, edit_name);
|
||||
system(system_line);
|
||||
free(system_line);
|
||||
|
||||
if ((edit_file = fopen(edit_name, "r")) == 0)
|
||||
err(1, "%s", edit_name);
|
||||
}
|
||||
|
||||
void
|
||||
mode_edit(struct cam_device *device, int page, int page_control, int dbd,
|
||||
int edit, int retry_count, int timeout)
|
||||
{
|
||||
int i;
|
||||
u_char data[255];
|
||||
u_char *mode_pars;
|
||||
struct mode_header
|
||||
{
|
||||
u_char mdl; /* Mode data length */
|
||||
u_char medium_type;
|
||||
u_char dev_spec_par;
|
||||
u_char bdl; /* Block descriptor length */
|
||||
};
|
||||
|
||||
struct mode_page_header
|
||||
{
|
||||
u_char page_code;
|
||||
u_char page_length;
|
||||
};
|
||||
|
||||
struct mode_header *mh;
|
||||
struct mode_page_header *mph;
|
||||
|
||||
char *fmt = mode_lookup(page);
|
||||
if (!fmt && verbose) {
|
||||
fprintf(stderr,
|
||||
"No mode data base entry in \"%s\" for page %d; "
|
||||
" binary %s only.\n",
|
||||
mode_db, page, (edit ? "edit" : "display"));
|
||||
}
|
||||
|
||||
if (edit) {
|
||||
if (!fmt)
|
||||
errx(1, "can't edit without a format");
|
||||
|
||||
if (page_control != 0 && page_control != 3)
|
||||
errx(1, "it only makes sense to edit page 0 "
|
||||
"(current) or page 3 (saved values)");
|
||||
|
||||
verbose = 1;
|
||||
|
||||
mode_sense(device, page, 1, dbd, retry_count, timeout,
|
||||
data, sizeof(data));
|
||||
|
||||
mh = (struct mode_header *)data;
|
||||
mph = (struct mode_page_header *)
|
||||
(((char *)mh) + sizeof(*mh) + mh->bdl);
|
||||
|
||||
mode_pars = (char *)mph + sizeof(*mph);
|
||||
|
||||
edit_init();
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0);
|
||||
|
||||
mode_sense(device, page, 0, dbd, retry_count, timeout,
|
||||
data, sizeof(data));
|
||||
|
||||
edit_rewind();
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0);
|
||||
|
||||
edit_rewind();
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0);
|
||||
|
||||
edit_edit();
|
||||
|
||||
edit_rewind();
|
||||
buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0);
|
||||
|
||||
/* Eliminate block descriptors:
|
||||
*/
|
||||
bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
|
||||
sizeof(*mph) + mph->page_length);
|
||||
|
||||
mh->bdl = mh->dev_spec_par = 0;
|
||||
mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
|
||||
mode_pars = ((char *)mph) + 2;
|
||||
|
||||
#if 0
|
||||
/* Turn this on to see what you're sending to the
|
||||
* device:
|
||||
*/
|
||||
edit_rewind();
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0);
|
||||
#endif
|
||||
|
||||
edit_done();
|
||||
|
||||
/* Make it permanent if pageselect is three.
|
||||
*/
|
||||
|
||||
mph->page_code &= ~0xC0; /* Clear PS and RESERVED */
|
||||
mh->mdl = 0; /* Reserved for mode select */
|
||||
|
||||
mode_select(device, (page_control == 3), retry_count,
|
||||
timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl +
|
||||
sizeof(*mph) + mph->page_length);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mode_sense(device, page, page_control, dbd, retry_count, timeout,
|
||||
data, sizeof(data));
|
||||
|
||||
/* Skip over the block descriptors.
|
||||
*/
|
||||
mh = (struct mode_header *)data;
|
||||
mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
|
||||
mode_pars = (char *)mph + sizeof(*mph);
|
||||
|
||||
if (!fmt) {
|
||||
for (i = 0; i < mh->mdl; i++) {
|
||||
printf("%02x%c",mode_pars[i],
|
||||
(((i + 1) % 8) == 0) ? '\n' : ' ');
|
||||
}
|
||||
putc('\n', stdout);
|
||||
} else {
|
||||
verbose = 1;
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, NULL);
|
||||
}
|
||||
}
|
500
sbin/camcontrol/util.c
Normal file
500
sbin/camcontrol/util.c
Normal file
@ -0,0 +1,500 @@
|
||||
/*
|
||||
* Written By Julian ELischer
|
||||
* Copyright julian Elischer 1993.
|
||||
* Permission is granted to use or redistribute this file in any way as long
|
||||
* as this notice remains. Julian Elischer does not guarantee that this file
|
||||
* is totally correct for any given task and users of this file must
|
||||
* accept responsibility for any damage that occurs from the application of this
|
||||
* file.
|
||||
*
|
||||
* (julian@tfs.com julian@dialix.oz.au)
|
||||
*
|
||||
* User SCSI hooks added by Peter Dufault:
|
||||
*
|
||||
* Copyright (c) 1994 HD Associates
|
||||
* (contact: dufault@hda.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of HD Associates
|
||||
* may not be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* Taken from the original scsi(8) program.
|
||||
* from: scsi.c,v 1.17 1998/01/12 07:57:57 charnier Exp $";
|
||||
*/
|
||||
#ifndef lint
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/file.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cam/cam.h>
|
||||
#include <cam/cam_ccb.h>
|
||||
#include <camlib.h>
|
||||
#include "camcontrol.h"
|
||||
|
||||
int verbose = 0;
|
||||
|
||||
/* iget: Integer argument callback
|
||||
*/
|
||||
int
|
||||
iget(void *hook, char *name)
|
||||
{
|
||||
struct get_hook *h = (struct get_hook *)hook;
|
||||
int arg;
|
||||
|
||||
if (h->got >= h->argc)
|
||||
{
|
||||
fprintf(stderr, "Expecting an integer argument.\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
arg = strtol(h->argv[h->got], 0, 0);
|
||||
h->got++;
|
||||
|
||||
if (verbose && name && *name)
|
||||
printf("%s: %d\n", name, arg);
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
/* cget: char * argument callback
|
||||
*/
|
||||
char *
|
||||
cget(void *hook, char *name)
|
||||
{
|
||||
struct get_hook *h = (struct get_hook *)hook;
|
||||
char *arg;
|
||||
|
||||
if (h->got >= h->argc)
|
||||
{
|
||||
fprintf(stderr, "Expecting a character pointer argument.\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
arg = h->argv[h->got];
|
||||
h->got++;
|
||||
|
||||
if (verbose && name)
|
||||
printf("cget: %s: %s", name, arg);
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
/* arg_put: "put argument" callback
|
||||
*/
|
||||
void
|
||||
arg_put(void *hook, int letter, void *arg, int count, char *name)
|
||||
{
|
||||
if (verbose && name && *name)
|
||||
printf("%s: ", name);
|
||||
|
||||
switch(letter)
|
||||
{
|
||||
case 'i':
|
||||
case 'b':
|
||||
printf("%d ", (int)arg);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
case 'z':
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = malloc(count + 1);
|
||||
|
||||
bzero(p, count +1);
|
||||
strncpy(p, (char *)arg, count);
|
||||
if (letter == 'z')
|
||||
{
|
||||
int i;
|
||||
for (i = count - 1; i >= 0; i--)
|
||||
if (p[i] == ' ')
|
||||
p[i] = 0;
|
||||
else
|
||||
break;
|
||||
}
|
||||
printf("%s ", p);
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unknown format letter: '%c'\n", letter);
|
||||
}
|
||||
if (verbose)
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
#define START_ENTRY '{'
|
||||
#define END_ENTRY '}'
|
||||
|
||||
static void
|
||||
skipwhite(FILE *f)
|
||||
{
|
||||
int c;
|
||||
|
||||
skip_again:
|
||||
|
||||
while (isspace(c = getc(f)))
|
||||
;
|
||||
|
||||
if (c == '#') {
|
||||
while ((c = getc(f)) != '\n' && c != EOF)
|
||||
;
|
||||
goto skip_again;
|
||||
}
|
||||
|
||||
ungetc(c, f);
|
||||
}
|
||||
|
||||
/* mode_lookup: Lookup a format description for a given page.
|
||||
*/
|
||||
char *mode_db = "/usr/share/misc/scsi_modes";
|
||||
static char *
|
||||
mode_lookup(int page)
|
||||
{
|
||||
char *new_db;
|
||||
FILE *modes;
|
||||
int match, next, found, c;
|
||||
static char fmt[4096]; /* XXX This should be with strealloc */
|
||||
int page_desc;
|
||||
new_db = getenv("SCSI_MODES");
|
||||
|
||||
if (new_db)
|
||||
mode_db = new_db;
|
||||
|
||||
modes = fopen(mode_db, "r");
|
||||
if (modes == 0)
|
||||
return 0;
|
||||
|
||||
next = 0;
|
||||
found = 0;
|
||||
|
||||
while (!found) {
|
||||
|
||||
skipwhite(modes);
|
||||
|
||||
if (fscanf(modes, "%i", &page_desc) != 1)
|
||||
break;
|
||||
|
||||
if (page_desc == page)
|
||||
found = 1;
|
||||
|
||||
skipwhite(modes);
|
||||
if (getc(modes) != START_ENTRY)
|
||||
errx(1, "expected %c", START_ENTRY);
|
||||
|
||||
match = 1;
|
||||
while (match != 0) {
|
||||
c = getc(modes);
|
||||
if (c == EOF) {
|
||||
warnx("expected %c", END_ENTRY);
|
||||
}
|
||||
|
||||
if (c == START_ENTRY) {
|
||||
match++;
|
||||
}
|
||||
if (c == END_ENTRY) {
|
||||
match--;
|
||||
if (match == 0)
|
||||
break;
|
||||
}
|
||||
if (found && c != '\n') {
|
||||
if (next >= sizeof(fmt))
|
||||
errx(1, "buffer overflow");
|
||||
|
||||
fmt[next++] = (u_char)c;
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt[next] = 0;
|
||||
|
||||
return (found) ? fmt : 0;
|
||||
}
|
||||
|
||||
/* -------- edit: Mode Select Editor ---------
|
||||
*/
|
||||
struct editinfo
|
||||
{
|
||||
int can_edit;
|
||||
int default_value;
|
||||
} editinfo[64]; /* XXX Bogus fixed size */
|
||||
|
||||
static int editind;
|
||||
volatile int edit_opened;
|
||||
static FILE *edit_file;
|
||||
static char edit_name[L_tmpnam];
|
||||
|
||||
static inline void
|
||||
edit_rewind(void)
|
||||
{
|
||||
editind = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
edit_done(void)
|
||||
{
|
||||
int opened;
|
||||
|
||||
sigset_t all, prev;
|
||||
sigfillset(&all);
|
||||
|
||||
(void)sigprocmask(SIG_SETMASK, &all, &prev);
|
||||
|
||||
opened = (int)edit_opened;
|
||||
edit_opened = 0;
|
||||
|
||||
(void)sigprocmask(SIG_SETMASK, &prev, 0);
|
||||
|
||||
if (opened)
|
||||
{
|
||||
if (fclose(edit_file))
|
||||
warn("%s", edit_name);
|
||||
if (unlink(edit_name))
|
||||
warn("%s", edit_name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
edit_init(void)
|
||||
{
|
||||
edit_rewind();
|
||||
if (tmpnam(edit_name) == 0)
|
||||
errx(1, "tmpnam failed");
|
||||
if ((edit_file = fopen(edit_name, "w")) == 0)
|
||||
err(1, "%s", edit_name);
|
||||
edit_opened = 1;
|
||||
|
||||
atexit(edit_done);
|
||||
}
|
||||
|
||||
static void
|
||||
edit_check(void *hook, int letter, void *arg, int count, char *name)
|
||||
{
|
||||
if (letter != 'i' && letter != 'b')
|
||||
errx(1, "can't edit format %c", letter);
|
||||
|
||||
if (editind >= sizeof(editinfo) / sizeof(editinfo[0]))
|
||||
errx(1, "edit table overflow");
|
||||
|
||||
editinfo[editind].can_edit = ((int)arg != 0);
|
||||
editind++;
|
||||
}
|
||||
|
||||
static void
|
||||
edit_defaults(void *hook, int letter, void *arg, int count, char *name)
|
||||
{
|
||||
if (letter != 'i' && letter != 'b')
|
||||
errx(1, "can't edit format %c", letter);
|
||||
|
||||
editinfo[editind].default_value = ((int)arg);
|
||||
editind++;
|
||||
}
|
||||
|
||||
static void
|
||||
edit_report(void *hook, int letter, void *arg, int count, char *name)
|
||||
{
|
||||
if (editinfo[editind].can_edit) {
|
||||
if (letter != 'i' && letter != 'b')
|
||||
errx(1, "can't report format %c", letter);
|
||||
|
||||
fprintf(edit_file, "%s: %d\n", name, (int)arg);
|
||||
}
|
||||
|
||||
editind++;
|
||||
}
|
||||
|
||||
static int
|
||||
edit_get(void *hook, char *name)
|
||||
{
|
||||
int arg = editinfo[editind].default_value;
|
||||
|
||||
if (editinfo[editind].can_edit) {
|
||||
char line[80];
|
||||
if (fgets(line, sizeof(line), edit_file) == 0)
|
||||
err(1, "fgets");
|
||||
|
||||
line[strlen(line) - 1] = 0;
|
||||
|
||||
if (strncmp(name, line, strlen(name)) != 0)
|
||||
errx(1, "expected \"%s\" and read \"%s\"", name, line);
|
||||
|
||||
arg = strtoul(line + strlen(name) + 2, 0, 0);
|
||||
}
|
||||
|
||||
editind++;
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void
|
||||
edit_edit(void)
|
||||
{
|
||||
char *system_line;
|
||||
char *editor = getenv("EDITOR");
|
||||
if (!editor)
|
||||
editor = "vi";
|
||||
|
||||
fclose(edit_file);
|
||||
|
||||
system_line = malloc(strlen(editor) + strlen(edit_name) + 6);
|
||||
sprintf(system_line, "%s %s", editor, edit_name);
|
||||
system(system_line);
|
||||
free(system_line);
|
||||
|
||||
if ((edit_file = fopen(edit_name, "r")) == 0)
|
||||
err(1, "%s", edit_name);
|
||||
}
|
||||
|
||||
void
|
||||
mode_edit(struct cam_device *device, int page, int page_control, int dbd,
|
||||
int edit, int retry_count, int timeout)
|
||||
{
|
||||
int i;
|
||||
u_char data[255];
|
||||
u_char *mode_pars;
|
||||
struct mode_header
|
||||
{
|
||||
u_char mdl; /* Mode data length */
|
||||
u_char medium_type;
|
||||
u_char dev_spec_par;
|
||||
u_char bdl; /* Block descriptor length */
|
||||
};
|
||||
|
||||
struct mode_page_header
|
||||
{
|
||||
u_char page_code;
|
||||
u_char page_length;
|
||||
};
|
||||
|
||||
struct mode_header *mh;
|
||||
struct mode_page_header *mph;
|
||||
|
||||
char *fmt = mode_lookup(page);
|
||||
if (!fmt && verbose) {
|
||||
fprintf(stderr,
|
||||
"No mode data base entry in \"%s\" for page %d; "
|
||||
" binary %s only.\n",
|
||||
mode_db, page, (edit ? "edit" : "display"));
|
||||
}
|
||||
|
||||
if (edit) {
|
||||
if (!fmt)
|
||||
errx(1, "can't edit without a format");
|
||||
|
||||
if (page_control != 0 && page_control != 3)
|
||||
errx(1, "it only makes sense to edit page 0 "
|
||||
"(current) or page 3 (saved values)");
|
||||
|
||||
verbose = 1;
|
||||
|
||||
mode_sense(device, page, 1, dbd, retry_count, timeout,
|
||||
data, sizeof(data));
|
||||
|
||||
mh = (struct mode_header *)data;
|
||||
mph = (struct mode_page_header *)
|
||||
(((char *)mh) + sizeof(*mh) + mh->bdl);
|
||||
|
||||
mode_pars = (char *)mph + sizeof(*mph);
|
||||
|
||||
edit_init();
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, edit_check, 0);
|
||||
|
||||
mode_sense(device, page, 0, dbd, retry_count, timeout,
|
||||
data, sizeof(data));
|
||||
|
||||
edit_rewind();
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, edit_defaults, 0);
|
||||
|
||||
edit_rewind();
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, edit_report, 0);
|
||||
|
||||
edit_edit();
|
||||
|
||||
edit_rewind();
|
||||
buff_encode_visit(mode_pars, mh->mdl, fmt, edit_get, 0);
|
||||
|
||||
/* Eliminate block descriptors:
|
||||
*/
|
||||
bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
|
||||
sizeof(*mph) + mph->page_length);
|
||||
|
||||
mh->bdl = mh->dev_spec_par = 0;
|
||||
mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
|
||||
mode_pars = ((char *)mph) + 2;
|
||||
|
||||
#if 0
|
||||
/* Turn this on to see what you're sending to the
|
||||
* device:
|
||||
*/
|
||||
edit_rewind();
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, 0);
|
||||
#endif
|
||||
|
||||
edit_done();
|
||||
|
||||
/* Make it permanent if pageselect is three.
|
||||
*/
|
||||
|
||||
mph->page_code &= ~0xC0; /* Clear PS and RESERVED */
|
||||
mh->mdl = 0; /* Reserved for mode select */
|
||||
|
||||
mode_select(device, (page_control == 3), retry_count,
|
||||
timeout, (u_int8_t *)mh, sizeof(*mh) + mh->bdl +
|
||||
sizeof(*mph) + mph->page_length);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mode_sense(device, page, page_control, dbd, retry_count, timeout,
|
||||
data, sizeof(data));
|
||||
|
||||
/* Skip over the block descriptors.
|
||||
*/
|
||||
mh = (struct mode_header *)data;
|
||||
mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
|
||||
mode_pars = (char *)mph + sizeof(*mph);
|
||||
|
||||
if (!fmt) {
|
||||
for (i = 0; i < mh->mdl; i++) {
|
||||
printf("%02x%c",mode_pars[i],
|
||||
(((i + 1) % 8) == 0) ? '\n' : ' ');
|
||||
}
|
||||
putc('\n', stdout);
|
||||
} else {
|
||||
verbose = 1;
|
||||
buff_decode_visit(mode_pars, mh->mdl, fmt, arg_put, NULL);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user