MTX original from Sourceforge

This commit is contained in:
Alexander Moibenko
2018-07-31 15:07:09 -05:00
commit 1fa4053be5
84 changed files with 26963 additions and 0 deletions

373
mtx-1.3.12/CHANGES Normal file
View File

@@ -0,0 +1,373 @@
CHANGES, mtx 1.3.12
- Incorporate debian packaging
- Remove strip of binaries
- Remove unsupported nsmhack from list of binaries built by default
- Add support for building outside of source tree
- Update copyrights
- Fix typo in mtx.1 man page
- Clear outstanding UNIT ATTENTION state at start
CHANGES, mtx 1.3.11
- loaderinfo: Print RequestSense information if an operation fails.
- loaderinfo: Print all information from the Device Configuration Page.
- mtx: Fix problems with a lot of loaders determining the number of elements.
- mtx: Implement Previous command.
- mtx: Fix bugs in Last command.
- scsieject: New tool that handles start/stop, load/unload.
- tapeinfo: Fix inconsistencies in output.
- Fix timeout handling on Linux.
- Merge in fixes from the Debian and FreeBSD distributions.
CHANGES, mtx 1.3.10
- Add support for Microsoft Windows.
- Add support for Sony VGP-XL1B(2) Media Changer.
Thanks to Will (nodenet at hotmail dot com)
- Add build support for Microsoft Windows using MinGW native and Linux
cross-compile.
- Add build support for Microsoft Windows using Microsoft Visual Studio 2005.
- Add support for building on x86_64.
- Add more debugging information.
- Eliminate compiler warnings.
CHANGES, mtx 1.3.9
- Cleaned up scsi_linux.c a little to eliminate around 40 lines of code.
- Change to using SG_IO ioctl rather than write-read interface. This
should make us a little safer, as well as (on 2.6.x) letting us issue SCSI
commands to regular block devices as well as to /dev/sgXX devices.
WARNING: Can cause the system to *CRASH* if the SCSI device is in use, due
to brokenness inside the Linux kernel! It's always preferable to use the
/dev/sgXX device, which has specialness in its buffer handling that bypasses
some of the brokenness elsewhere in the SCSI subsystem.
- Check SG version before using SG_IO interface, so that if we're run on a
Linux 2.2 system and we were compiled on Linux 2.4 or above,
- If a tape is in the drive, make tapeinfo print out its partition info. Seems
to work on my DAT drive, anyhow (shrug).
- Minor cleanups in mtxl.c (other cleanups necessary in tapeinfo and etc.).
CHANGES, mtx 1.3.8
- Forgot about 8 bytes for header
- Handle pedantic loader whose firmware writer spends too much time
parsing phrases in the SCSI spec and not enough time in the
real world.
CHANGES, mtx 1.3.6
- The You Gotta Be F'ing Kidding Release (see rant on mailing list)
- Added AIX support with http://fz.eryx.net/aix/ GSC driver, courtesy
of Steve Heck.
- Make __WEIRD_CHAR_SUPPRESS the default to stop barcode garbling
- Fix core dump on invalid inputs in mtx.c
- Add retry with bigger ALLOCATION_LENGTH if
BYTE_COUNT_OF_REPORT_AVAILABLE is > than our original estimate. (see rant
on mailing list).
- Shut up the low-level SCSI sg_read and sg_write routines, which were poofting
all over the place on innocuous things like no EAAP.
CHANGES, mtx 1.3.5
- The I-Need-A-Job Release (see http://badtux.org) :-}
- Change Makefile so that it works w/systems whose 'install' program
accepts only one argument. That's it.
- Uncomment the Exabyte hack in the drive parsing code so that it works
with ATL loaders again.
- Add hack for ATL stacked loaders to keep them from reporting ghost slots
CHANGES, mtx 1.3.4
- Forward-ported the ATL/Exabyte patches from mtx 1.2
- Added progress indicator to 'tapeinfo' for DAT drives. (PLEASE TEST)
- Purged a few more estinc.com out of the documentation, point to bru.com.
CHANGES, mtx 1.3.3
- Reversed James' long descriptor patch until can figure out what's wrong with
it.
- Fixed some debug declarations, courtesy of Kevin Wang.
- Point COMPATIBILITY file at mtx.sourceforge.net.
CHANGES, mtx 1.3.2
- Merged Makefile changes from mtx-1.2 code
- Changes from James Dugal to get all info for newer loaders, fix debugging
CHANGES, mtx 1.3.1
- add an Exchange command
CHANGES, mtx 1.3.0
- New Linux SCSI subsystem interface for 2.4 kernels, ripped boldly
from sgtools by Doug Gilbert
- mtx 'next' now skips blank slots, courtesy of Christopher McCrory
- mtx 'unload' now prints an error message telling you that you need to
eject the tape in the drive prior to unloading it, under certain
conditions.
- Started work on 'nsmhack' for controlling NSM jukeboxes.
- copy_barcode was off by one.
- 'position to element' command now added, courtesy of Mahlon Stacy
CHANGES, mtx 1.2.15:
- Some Solaris fixes, courtesy of Matt Ward
- Fix URL in .spec file
CHANGES, mtx 1.2.14:
- Fix so it'll work if 0 is result of SCSI open (e.g., in cron jobs on Linux)
- Move changelog to end of .spec file for easier reading
- Added a bit of text to beginning of COMPATIBILITY file
CHANGES, mtx 1.2.13:
- Fixed some autoloader bugs w/autoloaders that don't report an arm.
- Fixed barcode backoff.
- Added "nobarcode" option
- Increased timeout for 'mtx inventory' to 30 minutes
(note: may increase this even more if needed, please let me know!)
- Shortened timeout for 'mtx inquiry' to 30 seconds
- tapeinfo now prints SCSI ID/LUN info if available (only on Linux at the
moment, sigh).
- update documentation w/new email addresses, updated compile directions,
various fixes.
CHANGES, mtx 1.2.12:
- Fix FreeBSD compile bugs
- Fix SGI compile bugs
- Add HP/UX port (I hope!), courtesy of William Smith.
- Re-wrote ReadElementStatus to make work for %!@# brain dead firmware that
reports non-existent drives (I hope!). Also has side-effect of now working
with multiple-arm libraries (though it only sees first arm!).
- Cleaned up all -Wall messages.
- Cleaned up Linux Sparc, installs loaderinfo.1, courtesy of Matt Dainty.
- tapeinfo now reports status of CheckUnitReady.
- tapeinfo no longer puts out Block Position if CheckUnitReady says 'no'.
- tapeinfo now puts out Density Code and Medium Type/Not Loaded (modification
of patches sent in by Bob Rahe)
CHANGES, mtx 1.2.11:
- Added a GNU autoconf Makefile.in (still provide a Makefile for your use)
*WARNING* autoconf is not yet working on all supported OS's! You may need
to do the old-fashioned 'edit Makefile' bit!
- Changed mtx.h and mtxl.c to include and define various things based upon
what features autoconf detected (e.g., if 'camlib.h', use FreeBSD-style
'cam', if 'scsi/sg.h' use Linux-style 'sg', etc.). If I ever port to a
Unix that has same SCSI interface as one of the existing ports, autoconf
will handle it without me having to add another set of #if's or #ifdefs.
- Went ahead and tossed mtxctl into contrib.
- In 'tapeinfo', skip \0 characters in serial numbers (some use \0
terminator, some do not, skip it if this one does).
- in 'tapeinfo', dump out the block position and (if at BOP) the "BOP: Yes"
flag. Also dump out other info such as block limits.
- Put file 'sparc-patch1' contributed by Chaskiel M Grundman, and applied
it (sigh)
- Added tapeinfo.py to 'contrib' directory
- Updated mtx.py in 'contrib' directory
- Created 'loaderinfo' program to report some misc. info about loaders.
- Created 'scsitape' program so that I don't have to keep messing with
#@$%@! tape ioctls on the various Unixes that I'm porting tape software
to. (But see the warnings!).
- Applied the Solaris patch to the read_element_status command (sigh).
- Added timeout adjustment to the SCSI subsystem.
- WARNING: DIGITAL UNIX AND VMS ARE PROBABLY IRREPERABLY BROKEN, due to the
timeout changes to the SCSI subsystem. If anybody wishes to fix them,
feel free to send me patches.
- added contrib program "mtx-changer" (an Amanda tape changer script for
?Solaris? that uses mtx rather than stc)
- Jiggered Linux SCSI module for smarter error conditions handling (there are
some error conditions that are normal for READ of tape drives).
- Added contrib program "config_sgen_solaris.sh" which should ease
setting up the 'sgen' driver on Solaris 8 (still no easy Solaris 7 or
below config).
CHANGES, mtx 1.2.10:
- Added FAQ and COMPATIBILITY (feel free to send me patches to these files!)
- Added LICENSE
- Added serial number to 'tapeinfo' output.
- Fixed stupid syntax error in mtx.c (compiled with gcc, not with others!)
- Fixed spec file for building rpms (maybe).
- Added an 'erase' command (undocumented) for use on Linux for doing
short erases on tapes (the Linux device driver defaults to a long erase).
- Made mtx inventory return an error code if the inventory
fails, so that we can wait for inventory to be completed at system
startup for libraries that auto-inventory (sigh).
CHANGES, mtx 1.2.9:
- Added an 'eject' command that, if directed to a tape drive, will eject the
tape, and for some autoloaders, if directed to LUN 1, will eject the entire
magazine.
- Fixed the 'transfer' command to be 1 based rather than 0 based (sigh)
- Now properly reports bar code for the tape that's in the tape drive.
- Added some miscellaneous Python and Perl scripts to 'contrib'. Thanks
to Frank Samuelson for the Perl scripts.
CHANGES, mtx 1.2.8:
- Spec file has been changed to use the "portable" patch supplied by Red
Hat so it should work on Linux Alpha and Linux SPARC too... maybe...
- Now will accept 4-byte element status for most element types, despite fact
that these don't comply with SCSI standards :-(. This should make many
older changers work, including HP optical changers.
- Fixed PeripheralDeviceType table, courtesy of Rob Turk.
- Now looks for CHANGER environment variable if a device is not specified
on the command line. If can't find CHANGER, then tries TAPE environment
variable.
- Properly sets TransportElementAddress in the CDB for the MOVE MEDIUM command
with what was discovered via the READ_ELEMENT_STATUS command, rather
than setting them to zero (SCSI spec says that zero should be the default
arm, but at least one changer out there didn't like it).
- Added a '--version' command (sigh).
- Added an 'inventory' command for Breece Hill libraries that don't
automatically do an inventory at powerup.
CHANGES, mtx 1.2.7:
- Fixed problem w/single drive Exabyte 220 reporting element status data for
both drives (sigh).
- some general cleanup in the barcode fallback code (what a cruft!). Discovered
that ADIC DAT AUTOCHANGER does not work w/mtx because it produces
gibberish (will apparently only produce one element status page per request).
- Fixed the RPM .spec file to have updated file locations, doc locations.
- Fixed MoveMedium to say 'Output' for direction, to make it work with
Solaris 8
- Some changes to the Solaris low-level module to report more errors (though
it still doesn't work as well as the Linux low-level module). Should now
work properly with Solaris 2.6/7/8. (Solaris changes courtesy of Richard
Fish of Enhanced Software Technologies).
CHANGES, mtx 1.2.6:
- Fixed 'eepos' stuff to use | rather than || (whoops!)
- Accept a 4-byte element descriptor for the robot arm for certain older
autochangers.
CHANGES, mtx 1.2.5:
- Added 'noattach' command. If on command line prior to other commands, forces
them to use the regular SCSI changer API rather than the _ATTACHED API,
no matter what the _ATTACHED bit in the Inquiry page said.
- Created 'tapeinfo' program.
CHANGES, mtx 1.2.4:
- Major overhaul of element guts to dynamically allocate the arrays
using the result of a MODE_SENSE on the Element Address Assignment
Page. If mtx 1.2.3 works for you and mtx 1.2.4 does NOT work for you,
please un-comment the '#define DEBUG_MODE_SENSE' in file 'mtxl.c' and
EMAIL me the results.
CHANGES, mtx 1.2.3:
- Fixed the source storage element number stuff (again, sigh)
- Because of above fix, 'next' etc. ought to work right again.
CHANGES, mtx 1.2.2:
- Fixed that it was saying everything was an Import/Export element (oops!)
- Properly update the Import/Export element count.
CHANGES, mtx 1.2.1:
- Now explicitly output that a Storage element is in fact an Import/Export
element.
- Added 'transfer' command to transfer between two Storage elements (so that
you can get a tape to an Import/Export element.
- Added 'eepos' command for controlling tray retraction on the Breecehill
import/export trays. (Works with "load" and "unload" commands too, though
that is not documented on "mtx -h").
CHANGES, mtx 1.2.0:
- Re-numbered now that Leonard has asked me to take over maintenance of the
'mtx' program.
- Temporarily treat Import/Export elements the same as Storage elements. Need
to fix this eventually so that the GUI knows what kind of element we're
talking about.
- Removed quotes from the source element # to make it easier to parse
from Perl or Python (just do a split on spaces).
- Added sample program, 'mam2debug', showing how to use mtxl library for
your own programs (this happens to dump the Exabyte Mammoth 2's internal
debug buffer to a file, using the Mammoth2-specific SCSI commands to do so).
CHANGES, mtxl 1.4.8:
- Whoops, report logical rather than physical when I have to scan for
open slots :-).
CHANGES, mtxl 1.4.7:
- Update comment to reflect mtxl 1.4.6 stuff :-).
- Fix the part of the code that scans for open slots as sources for media.
CHANGES, mtxl 1.4.6:
- Don't use _ATTACHED interface if it reports itself as a Medium Changer!
CHANGES, mtxl 1.4.5:
- Changed "whoops" compile error on Linux (teach me to release w/o testing on
the most popular platform!)
- Changed declarations to remove compile-time warnings.
CHANGES, mtxl 1.4.4:
- Added support for FreeBSD. (uses pass device, NOT native FreeBSD ch device).
- Change all 'int' DeviceFD to DEVICE_TYPE DeviceFD. Note that SGI and FreeBSD
use a struct * to access the CAM SCSI layer, rather than a file fd.
- Fixed goof where I'd hard-wired max # of elements to 127 for testing
purposes (it should be sum of MAX_STORAGE_ELEMENTS + MAX_TRANSFER_ELEMENTS
+ MAX_TRANSPORT_ELEMENTS from mtx.h -- change those if you need more
elements, bearing in mind that the code for ReadElementStatus in
mtxl.c maxes out at 255 elements unless you fix that too).
- Cleaned some cruft out of the MOVE_MEDIUM code.
- Must have GNU Make to process Makefile. In reality, I don't know of
any machine where we voluntarily use the native 'make' command, because
a) there's a half dozen native 'make' all with their own perverted
syntaxes, and b) most of them are brain dead beyond belief.
CHANGES, mtxl 1.4.3:
- Do an INQUIRY prior to doing a MOVE_MEDIUM or READ_ELEMENT_STATUS so that I
can detect the MChanger bit and use MOVE_MEDIUM_ATTACHED or
READ_ELEMENT_STATUS_ATTACHED commands instead.
- Successfully tested with dual drives!
- first, next, last now working
- Created a man page
- Created a 'make install', edit Makefile to alter destinations.
CHANGES, mtxl 1.4.2:
- Found the problem with the DAT changer! It was burping on the
'bar code' bit... so I intercept that sense key, re-issue w/out the
'bar code' bit, and success!
- Added a 'TODO' file...
CHANGES, mtxl 1.4.1:
- Added 'invert' qualifier to 'load' and 'unload' commands to invert
the media (for HP optical jukeboxes). Type './mtx' by itself to
see the syntax.
- Figured out why my code wasn't properly detecting errors --
turns out the 'sg' device can return ok
status even when there is sense data to be reported!
- Still to fix: *still* isn't working right with my Seagate
6-tape DDS-4 DAT changer... also need to put the
second drive into the Exabyte 220 to make sure the dual-drive stuff
works properly (!).
CHANGES, mtxl 1.4:
- Have now tested the barcode (volume tag) stuff. It works! (Well, there was
an index-by-one problem that I had to squash, but after that...)
- Changed to use SCSI Generic device API on Linux rather than
SCSI_IOCTL_SEND_COMMAND API, which cut things off at 4095 bytes on i386
Linux.
- Added a bunch of debugging output that needs to be ripped out :-(.
Make sure you remove the -DDEBUG from the Makefile, and probably
-DLONG_PRINT_REQUEST_SENSE too (unless you LIKE sense results that make
sense!)
- Still have annoying bug on Linux of only reading 1st 16 bytes of sense
data. Alas, this appears to be a problem in the Linux 2.2 kernel, not in
anything that we're doing :-(. Hmm... cdrecord has the same problem, Mr.
Schilling says he's been saying it's a problem since 1997. Sigh.
- Still need to test the dual-drive stuff!
CHANGES, mtxl 1.3:
- Hacked in the barcode (volume tag) stuff. NEED SOMEONE TO TEST
WHETHER IT WORKS!
- started issuing redundant initial READ_ELEMENT_STATUS with Allocation Length
of 8 in order to get a BYTE_COUNT_OF_REPORT_AVAILABLE in order to calculate a
better Allocation Length for the "real" READ_ELEMENT_STATUS. Trying to send a
query to a small 6-tape changer with an Allocation Length suited for a
200-element tape library was resulting in some errors and lockups on the
part of the tape changer device.
- first, last, next, previous are STILL broken. Sorry :-(.
CHANGES, mtxl 1.2:
- Changed many output messages to make them more easily parsed by
scripts written in awk/perl/python
- Extracted out a library of SCSI tape library routines usable by "C"
programs (must be GPL'ed). Extensive re-arranging of code.
- Added support for multiple drives.
- Started adding support for tape changers that use the "MCHangr"
bit rather than a separate ID or LUN.
- Increased limits so we could deal with LARGE tape libraries.
- Started adding support for barcode readers in said tape libraries
- broke first, last, next, previous. Sorry :-(.
- Added ability to chain commands on command line. Thus could say 'mtx -f
/dev/sg4 unload 4 0 load 7 0' to unload a tape from drive 0 and load the
tape in slot 7 into drive 0.

163
mtx-1.3.12/COMPATABILITY Normal file
View File

@@ -0,0 +1,163 @@
WARNING: THIS FILE IS OBSOLETE AND FOR HISTORICAL USE ONLY. Please
see http://mtx.sourceforge.net for a more up-to-date compatibility list.
Operating Systems:
-----------------
mtx 1.2 is currently known to work flawlessly under the following
operating systems:
Linux (2.2.5 kernel and above)
FreeBSD (tested with versions 3.2 and above, but should work for all 3.x
and 4.x).
Various people have reported that they have managed to make mtx work
on various versions of Solaris. Please check the mtx list archives (
http://mtx.sourceforge.net ) to see how they've done it.
IRIX 6.5 apparently works flawlessly:
(Richard.Lefebvre(@AROBAS)cerca(.POINT)umontreal(.POINT)ca):
I just wanted to send you 1 or 2 comments on mtx. I just downloaded it
last friday to use with and SGI Origin2000 and an 8 slot DLT autoloader.
The whole thing compiled fine (under gcc).
From Dan Wright (dtwright at uiuc dot uiuc.edu):
I just wanted to send an e-mail to let you know that mtx 1.2.9
works great for me on IRIX 6.5 with an ADIC FastStor DLT8000.
IRIX 6.5 has a generic scsi interface installed by default, so to
access the autoloader I use "mtx -f /dev/scsi/sc1d1l0" (bus 1 id 1
lun 0).
----------------
Changer Devices
----------------
The following has been directly tested:
* Exabyte 220 with 1 drive (21 slots)
* Exabyte 220 with 2 drives (21 slots)
* Exabyte EZ-17 (7 slots)
Known quirks: Must eject tape using 'mt' or 'tapectl' prior to using
the 'unload' command.
* Seagate DDS-4 DAT AutoLoader (1 drive, 6 slots)
Product ID: 'DAT 06241-XXX'
Known quirks: Uses LUN 1 for robot. On Linux, must compile kernel with
"Scan SCSI LUNs" or add the following line to your /etc/lilo.conf (and
re-run /sbin/lilo):
append="max_scsi_luns=2"
* DISC D-40 optical library (2 drives, 40 slots, 1 import/export): Yes,
it really does report that it's a Maxoptix!
Product Type: Medium Changer
Vendor ID: 'Maxoptix'
Product ID: 'MAXLYB '
Revision: '3.04'
Attached Changer: No
The following have been tested by others, and information may be
incomplete or in error even.
* Compaq DLT Library 20/40, 1 drive 15 slots (antony at elizatravel dot com)
Vendor: HP Model: C5173-4000
* Vendor ID: 'ADIC ' (boris dot klug at ibs dash ag dot de)
Product ID: 'VLS DLT ' 7 slot 1 import/export 1 drive.
* Unknown Overland changer (phreno at pacbell dot net):
> > > Vendor ID: 'OVERLAND'
> > > Product ID: 'LXB '
* "old Exabyte 10e tape loaders" (drew at pctc dot com)
* Unknown HP DLT changer: (eric at collab dot net)
> > Storage Changer /dev/sgd:1 Drives, 15 Slots ( 0 Import/Export )
> > Vendor ID: 'HP '
> > Product ID: 'C5173-4000 '
* "Lago Sys LS-380L/StorageTek 9704/Imation ITL-2225 (mine's a
StorageTek labeled)" 25-slot, 2-drive, 1-arm, 1-import/export
(eswan at lips dot net)
* HP SureStore Optical 80fx. (mmarchione at nyhomes dot org )
Vendor: HP Model: C1160F Rev: 0.40
Type: Medium Changer ANSI SCSI revision: 02
* Breece Hill tape library (unknown configuration, unknown reporter):
Vendor ID: 'BHTI'
Product ID: 'Q2 '
Oddities: Must either turn on auto-inventory, or run 'mtx inventory'
prior to running any other mtx command. Reads bar codes by yanking
tapes out of slots and waving them in front of bar code reader, so
inventory of tapes with no bar code is VERY slow (bar code your tapes!).
* Seagate Scorpion DDS-3 4-tape autochanger
(jsled at normandy dot smarttouch dot com):
Vendor ID: 'ARCHIVE '
Product ID: 'Python 04377-XXX'
* Overland 15 tape 1 drive DLT changer: (asmith at umdgrb dot umd dot edu)
Vendor ID: 'OVERLAND'
Product ID: 'LXB '
* ADIC DAT AutoChanger -- 1 drive, 12 slots, 1 import-export:
Vendor ID: 'ADIC ' (andrew_gray at irobotics dot com)
Product ID: 'DAT AutoChanger '
Quirks: firmware version 0357 does not appear to work w/mtx (request
from ADIC that they upgrade your firmeware to 0361, which does
seem to work), firmware version revision `0462` does work. Use
'mtx inquiry' to detirmine which firmware you have.
* HP 1/20 DLT loader: "We have a HP 1/20, but I think it's all the
same hardware. mtx works fine with it." (jo2y at midnightlinux dot com)
* Exabyte 120 (116 slots, 4 tape drives): (wsb at paralleldata dot com)
Quirks: This loader does not properly support the loader slot assignment
page used to allocate element structures, so the #defines must be
changed in mtx.h for MAX_STORAGE_ELEMENTS, MAX_TRANSFER_ELEMENTS, and
MAX_TRANSPORT_ELEMENTS. May also need to increase the SCSI timeout to
do inventories. Takes a LONG time to do inventories if you don't have
bar codes!
* Exabyte 210 (wsb at paralleldata dot com)
* Adic FastStor DLT4000 (7-Slot 1 drive):
Vendor ID: 'ADIC '
Product ID: 'FastStor DLT '
* Ecrix AutoPak VXA (15-slot 1 drive) in configuration mode 0 (mark
at commerceengine dot com):
"This drive can be configured to emulate several types, but I've
only tested it in this one mode."
Vendor ID: 'SPECTRA '
Product ID: '215 '
Revision: '1008'
Attached Changer: No
SerialNumber: '023201'
* ATL P1000 2 drive, 31 slot, 1 import/output: (dna plus mtx at clas.ufl.edu)
# ./mtx -f /dev/scsi/changer/c2t0d0 inquiry
Product Type: Medium Changer
Vendor ID: 'ATL '
Product ID: 'P1000 6220070'
Revision: '2.01'
Attached Changer: No
* NSM DVD Jukebox:
Storage Changer /dev/sg4:4 Drives, 561 Slots ( 1 Import/Export )
Product Type: Medium Changer
Vendor ID: 'NSM '
Product ID: 'NSM6000 '
Revision: '1120'
Attached Changer: No
* Spectralogic Treefrog/Bullfrog:
Storage Changer /dev/sg0:2 Drives, 15 Slots ( 0 Import/Export )
Product Type: Medium Changer
Vendor ID: 'SPECTRA '
Product ID: '10000 '
Revision: 'X010'
Attached Changer: No

144
mtx-1.3.12/FAQ Normal file
View File

@@ -0,0 +1,144 @@
Frequently Asked Questions List, v 1.0.1
Index:
I. Compiling
II. Finding the correct device
III. Operational Issues
Part I: Compiling.
Q: Where is the Makefile in the tarball?
A: MTX now uses GNU Autoconf to generate the Makefile. Type "./configure"
while in the extracted mtx directory.
Q: Typing 'make' gives me a bunch of errors in the Makefile. Why can't
you provide a Makefile that works?
A: Note that you need the GNU 'make'. The BSD 'make' won't work, and
Solaris 'make' probably won't work either. If you want a better
configuration and makefile system, write one, then EMAIL me the results --
mtx is Open Source software and needs your code contributions to grow.
Q: How do I compile for operating systems other than Linux?
A: MTX no longer needs you to edit the Makefile to compile for operating
systems other than Linux. Just type ./configure and go with it.
Q: How do I port it to OS's other than the supported ones?
A: Create a new scsi_ module using one of the existing modules as an
example (scsi_freebsd.c might be a good model). Decide what symbol
you want #ifdef'ed in order to include that scsi_ module. Edit
mtxl.c to #include your scsi_ module. Edit the Makefile to add the
new target, including the -D needed to #include your new scsi_ module.
*********************************************************************
Part II: Finding the correct device.
Q: Why does this command not work??
[root@Scotty mtxl-1.4.8]# ./mtx -f /dev/st0 inquiry
In /var/log/messages I see:
st0: Write not multiple of tape block size.
A: Note that mtx 1.2 and above use the SCSI GENERIC interface on Linux,
FreeBSD, and Solaris (at least). They do NOT use the tape device node.
Q: When I do 'mtx -f /dev/sga inquiry' it shows
Product Type: Tape Drive
Vendor Id: HP
Product ID: C1553A
But when I do a 'mtx -f /dev/sga status' it fails. Why?!
A: You're trying to send a robotics command to a tape drive. You need
to send robotics commands to robotics devices, not to tape drives. Look in
/proc/scsi/scsi (Linux) or camcontrol (FreeBSD) to find out what the
robotics device is. It will be reported as a 'Medium Changer', not a
'Sequential Access' or 'Tape Drive'.
Q: When I do 'cat /proc/scsi/scsi' it shows only one device, the tape device!
A: You are using a DAT autochanger that has one SCSI ID but two LUN's, LUN 0
and LUN 1. You need to compile a new kernel with SCAN SCSI LUNS enabled
or add this line to your /etc/lilo.conf (then run /sbin/lilo and reboot):
append="max_scsi_luns=2"
Q: I'm tired of typing '-f /dev/sgc' all the time. How do I set a default
device that 'mtx' looks at?
A: Set the CHANGER environment variable. For example, with 'bash':
export CHANGER=/dev/sgc
Q: I get "modprobe: can't locate module char-major-21"
syslog messages being squirreled away into a file on our syslog host,
and mtx doesn't work. What's the problem?
A: You need to compile SCSI generic support into your kernel (or as a module).
Q: When I installed mtx, a message showed
up on the console stating that a scsi changer was found at
dev sgr. However, I have no device /dev/sgr.
A: On Linux, do 'mknod /dev/sgr c 21 19' to create a device node. By default
only 16 SCSI generic nodes are created, which might not be enough if
you have multiple SCSI controllers with lots of devices.
******************************************************
III. Operational issues:
Q: I'm using Red Hat 7.0 and mtx works fine when I type ./mtx from the
command line, but when I use it from scripts I get the following:
mtx: Request Sense: Error Code=70 (Current)
mtx: Request Sense: Sense Key=Aborted Command
mtx: Request Sense: Additional Sense Code = 4E
mtx: Request Sense: Additional Sense Qualifier = 00
What's happening?
A: Do "rpm -e mtx". Red Hat 7.0 includes a busted version of mtx. Your
script is apparently picking up the busted mtx in your path. Get rid
of the busted mtx, make sure that /usr/local/bin (or wherever you
put the "good" mtx) is in the path, and all should be well.
Q: I get
# /usr/local/bin/mtx -f /dev/sgr status
mtx: Request Sense: Error Code=70 (Current)
mtx: Request Sense: Sense Key=Not Ready
mtx: Request Sense: Additional Sense Code = 04
mtx: Request Sense: Additional Sense Qualifier = 8E
mtx: READ ELEMENT STATUS Command Failed
What gives?
A: Make sure your loader is in random mode, not sequential mode.
Most "real" loaders (as vs. DAT autoloaders) will not properly report
status information unless they are in "random" mode.
Q: I issue 'mtx load 5' and it loads tape 5. But when I try to put the tape
back in the magazine, we hit problems:
mtx: MOVE MEDIUM from Element Address 82 to 5 Failed
What gives?
A: Many loaders require you to first eject the tape (using 'mt' or 'tapectl')
before you issue an 'unload' command via 'mtx'.
Q: My Breece Hill loader does not properly report its slots.
A: Either set the "auto-inventory" feature in the loader's control panel,
or run 'mtx inventory' prior to running 'mtx status'.
Q: My Breece Hill loader takes a long time to do an inventory. mtx times
out and spits all over the place. Help!
A: Many loaders that support barcodes will perform poorly if you place tapes
into them without bar codes. Place bar codes on all your tapes and you
should be able to run 'mtx inventory' without that failure.
Q: How do I eject the magazine of my autoloader?
A: Many low-end DAT autoloaders support the removable media 'EJECT' command
sent to the robotics device, even though it's not documented (or required)
in the SCSI standards. If the loader is at /dev/sgb, simply do
'mtx -f /dev/sgb eject' and see what happens. (If nothing happens,
your autoloader doesn't support 'eject'). Some high-end libraries have
their own proprietary way for ejecting magazine trays, generally
involving abuse of the 'transfer' command and 'eepos' addendums,
but this is totally non-standard and undocumented.
Q: Is there a standard for cleaning tape bar codes?
A: Many libraries, and many backup programs, expect cleaning tape bar
codes to start with "CLN".
Q: How do I report a bug?
A: First, read this FAQ. Next, check the mtx list archives at
http://mtx.sourceforge.net to make sure that it's not already addressed
by somebody else. If your problem is still not solved, send
(to the mtx list) the following information:
Result of 'mtx inquiry' on the loader,
Result of 'mtx status' on the loader (minus a bunch of tapes if
it's a 50+ tape loader!),
Results of the operation that isn't working correctly.

280
mtx-1.3.12/LICENSE Normal file
View File

@@ -0,0 +1,280 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, 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

515
mtx-1.3.12/LICENSE.html Normal file
View File

@@ -0,0 +1,515 @@
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML>
<HEAD>
<TITLE>GNU General Public License - GNU Project - Free Software Foundation (FSF)</TITLE>
<LINK REV="made" HREF="mailto:webmasters@www.gnu.org">
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#1F00FF" ALINK="#FF0000" VLINK="#9900DD">
<H1>GNU General Public License</H1>
<P>
<HR>
<P>
<H2>Table of Contents</H2>
<UL>
<LI><A NAME="TOC1" HREF="gpl.html#SEC1">GNU GENERAL PUBLIC LICENSE</A>
<UL>
<LI><A NAME="TOC2" HREF="gpl.html#SEC2">Preamble</A>
<LI><A NAME="TOC3" HREF="gpl.html#SEC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</A>
<LI><A NAME="TOC4" HREF="gpl.html#SEC4">How to Apply These Terms to Your New Programs</A>
</UL>
</UL>
<P>
<HR>
<P>
<H2><A NAME="SEC1" HREF="gpl.html#TOC1">GNU GENERAL PUBLIC LICENSE</A></H2>
<P>
Version 2, June 1991
</P>
<PRE>
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.
</PRE>
<H2><A NAME="SEC2" HREF="gpl.html#TOC2">Preamble</A></H2>
<P>
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.
</P>
<P>
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.
</P>
<P>
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.
</P>
<P>
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.
</P>
<P>
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.
</P>
<P>
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.
</P>
<P>
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.
</P>
<P>
The precise terms and conditions for copying, distribution and
modification follow.
</P>
<H2><A NAME="SEC3" HREF="gpl.html#TOC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</A></H2>
<P>
<STRONG>0.</STRONG>
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".
<P>
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.
<P>
<STRONG>1.</STRONG>
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.
<P>
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.
<P>
<STRONG>2.</STRONG>
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:
<P>
<UL>
<LI><STRONG>a)</STRONG>
You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
<P>
<LI><STRONG>b)</STRONG>
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.
<P>
<LI><STRONG>c)</STRONG>
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.)
</UL>
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.
<P>
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.
<P>
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.
<P>
<STRONG>3.</STRONG>
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:
<!-- we use this doubled UL to get the sub-sections indented, -->
<!-- while making the bullets as unobvious as possible. -->
<UL>
<LI><STRONG>a)</STRONG>
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,
<P>
<LI><STRONG>b)</STRONG>
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,
<P>
<LI><STRONG>c)</STRONG>
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.)
</UL>
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.
<P>
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.
<P>
<STRONG>4.</STRONG>
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.
<P>
<STRONG>5.</STRONG>
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.
<P>
<STRONG>6.</STRONG>
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.
<P>
<STRONG>7.</STRONG>
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.
<P>
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.
<P>
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.
<P>
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
<P>
<STRONG>8.</STRONG>
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.
<P>
<STRONG>9.</STRONG>
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.
<P>
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.
<P>
<STRONG>10.</STRONG>
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.
<P><STRONG>NO WARRANTY</STRONG></P>
<P>
<STRONG>11.</STRONG>
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.
<P>
<STRONG>12.</STRONG>
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.
<P>
<H2>END OF TERMS AND CONDITIONS</H2>
<H2><A NAME="SEC4" HREF="gpl.html#TOC4">How to Apply These Terms to Your New Programs</A></H2>
<P>
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.
</P>
<P>
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.
</P>
<PRE>
<VAR>one line to give the program's name and an idea of what it does.</VAR>
Copyright (C) <VAR>yyyy</VAR> <VAR>name of author</VAR>
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.
</PRE>
<P>
Also add information on how to contact you by electronic and paper mail.
</P>
<P>
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
</P>
<PRE>
Gnomovision version 69, Copyright (C) <VAR>year</VAR> <VAR>name of author</VAR>
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.
</PRE>
<P>
The hypothetical commands <SAMP>`show w'</SAMP> and <SAMP>`show c'</SAMP> should show
the appropriate parts of the General Public License. Of course, the
commands you use may be called something other than <SAMP>`show w'</SAMP> and
<SAMP>`show c'</SAMP>; they could even be mouse-clicks or menu items--whatever
suits your program.
</P>
<P>
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:
</P>
<PRE>
Yoyodyne, Inc., hereby disclaims all copyright
interest in the program `Gnomovision'
(which makes passes at compilers) written
by James Hacker.
<VAR>signature of Ty Coon</VAR>, 1 April 1989
Ty Coon, President of Vice
</PRE>
<P>
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.
<HR>
<P>
FSF &amp; GNU inquiries &amp; questions to
<A HREF="mailto:gnu@gnu.org"><EM>gnu@gnu.org</EM></A>.
send other questions to
<A HREF="mailto:gnu@gnu.org"><EM>gnu@gnu.org</EM></A>.
<P>
Copyright notice above.<BR>
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111, USA
<P>
<HR>
</BODY>
</HTML>

176
mtx-1.3.12/Makefile.in Normal file
View File

@@ -0,0 +1,176 @@
# WARNING -- THIS HAS BEEN RE-WRITTEN TO USE GNU MAKE. DO NOT
# TRY TO PROCESS THIS WITH A NORMAL MAKE! (FREEBSD GUYS, THIS MEANS
# USE GMAKE, NOT REGULAR BSD MAKE!)
#
# Valid targets:
# linux86 freebsd86 solarissparc sgi dec vms
#
# Makefile changes by Lars Kellogg-Stedman for better integration with
# GNU Autoconf.
# Version # for 'make dist'...
VERSION=1.3.12
BINS = mtx@EXEEXT@ tapeinfo@EXEEXT@ loaderinfo@EXEEXT@ scsitape@EXEEXT@ scsieject@EXEEXT@
EXTRA_BINS = nsmhack@EXEEXT@
DBGS := $(BINS:%@EXEEXT@=%.dbg)
MAN = mtx.1 tapeinfo.1 loaderinfo.1 scsitape.1 scsieject.1
MAN_HTML := $(MAN:%.1=%.html)
MAN_TXT := $(MAN:%.1=%.txt)
TARGET = @TARGET@
CPU = @CPU@
CC = @CC@
INSTALL = @INSTALL@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@ -DVERSION="\"$(VERSION)\"" -I$(srcdir) -I.
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
USE_OBJCOPY = @USE_OBJCOPY@
INSTALL_DOC = $(INSTALL) -m 644
INSTALL_BIN = $(INSTALL) -m 755
INSTALL_DIR = $(INSTALL) -m 755 -d
prefix = @prefix@
exec_prefix = @exec_prefix@
sbindir = @sbindir@
mandir = @mandir@
srcdir = @srcdir@
VPATH = $(srcdir)
#
# Linux on x86...
#
ifeq ($(TARGET),linux)
CFLAGS += -Wall
CPPFLAGS += -DLONG_PRINT_REQUEST_SENSE=1
endif
ifeq ($(TARGET),mingw)
CFLAGS += -Wall
CPPFLAGS += -DLONG_PRINT_REQUEST_SENSE=1
endif
#
# FreeBSD
#
ifeq ($(TARGET),freebsd86)
CPPFLAGS += -DLONG_PRINT_REQUEST_SENSE=1
LIBS += -lcam
endif
ifeq ($(TARGET),hpux)
CFLAGS += -O -D_HPUX_SOURCE -D __hpux__
endif
#
# Solaris/SPARC
#
ifeq ($(TARGET),solarissparc)
CFLAGS += -O6
endif
#
# SGI IRIX
#
ifeq ($(TARGET),sgi)
CFLAGS += -O6
LIBS += -lds
endif
#
# Digital Unix
#
ifeq ($(TARGET),dec)
CFLAGS += -O
endif
#
# OpenVMS (see vms/000readme)
#
ifeq ($(TARGET),vms)
See vms/000readme for information.
endif
%.dbg : %@EXEEXT@
ifeq ($(USE_OBJCOPY),yes)
objcopy --only-keep-debug $< $@
objcopy --strip-debug $<
objcopy --add-gnu-debuglink=$@ $<
else
strip $< -o $@
endif
all: $(BINS)
dbgs: $(DBGS)
install: $(BINS)
$(INSTALL_DIR) $(sbindir)
for file in $(BINS); do \
$(INSTALL_BIN) "$$file" $(sbindir) ; \
done
$(INSTALL_DIR) $(mandir) $(mandir)/man1
for file in mtx.1 tapeinfo.1 scsitape.1 scsieject.1 loaderinfo.1 ; do \
$(INSTALL_DOC) "$$file" $(mandir)/man1 ; \
done
clean:
rm -f *.o *~ mtx-*.zip
rm -f $(BINS)
rm -f $(DBGS)
rm -f $(MAN_HTML)
rm -f $(MAN_TXT)
rm -f mam2debug@EXEEXT@ mam2debug2@EXEEXT@
rm -rf autom4te.cache
distclean: clean
rm -f Makefile config.h config.log config.cache config.status
dist: distclean
./makedist $(VERSION)
loaderinfo@EXEEXT@: loaderinfo.o mtxl.o mtxl.h mtx.h $(EXTRA)
$(CC) $(LDFLAGS) -o loaderinfo@EXEEXT@ loaderinfo.o mtxl.o $(EXTRA) $(LIBS)
nsmhack@EXEEXT@: nsmhack.o mtxl.o $(EXTRA)
$(CC) $(LDFLAGS) -o nsmhack@EXEEXT@ nsmhack.o mtxl.o $(EXTRA) $(LIBS)
mtx@EXEEXT@: mtx.o mtxl.o mtxl.h mtx.h $(EXTRA)
$(CC) $(LDFLAGS) -o mtx@EXEEXT@ mtx.o mtxl.o $(EXTRA) $(LIBS)
mam2debug@EXEEXT@: mtxl.o mam2debug.o mtx.h $(EXTRA)
$(CC) $(LDFLAGS) -o mam2debug@EXEEXT@ mtxl.o mam2debug.o $(EXTRA) $(LIBS)
tapeinfo@EXEEXT@: tapeinfo.o mtxl.o mtx.h mtxl.h $(EXTRA)
$(CC) $(LDFLAGS) -o tapeinfo@EXEEXT@ tapeinfo.o mtxl.o $(EXTRA) $(LIBS)
mam2debug2@EXEEXT@: mtxl.o mam2debug2.o mtx.h $(EXTRA)
$(CC) $(LDFLAGS) -o mam2debug2@EXEEXT@ mtxl.o mam2debug2.o $(EXTRA) $(LIBS)
scsitape@EXEEXT@: scsitape.o mtxl.o mtxl.h mtx.h $(EXTRA)
$(CC) $(LDFLAGS) -o scsitape@EXEEXT@ scsitape.o mtxl.o $(EXTRA) $(LIBS)
scsitape.o: scsitape.c mtx.h mtxl.h
scsieject@EXEEXT@: scsieject.o mtxl.o mtxl.h mtx.h $(EXTRA)
$(CC) $(LDFLAGS) -o scsieject@EXEEXT@ scsieject.o mtxl.o $(EXTRA) $(LIBS)
scsieject.o: scsieject.c mtx.h mtxl.h
loaderinfo.o: loaderinfo.c mtx.h mtxl.h
tapeinfo.o: tapeinfo.c mtx.h mtxl.h
mam2debug.o: mam2debug.c mtx.h mtxl.h
mam2debug2.o: mam2debug2.c mtx.h mtxl.h
mtx.o: mtx.c mtx.h mtxl.h
mtxl.o: mtxl.c mtx.h mtxl.h scsi_linux.c scsi_win32.c
nsmhack.o: nsmhack.c mtxl.h mtx.h

38
mtx-1.3.12/README Normal file
View File

@@ -0,0 +1,38 @@
MTX
Programs:
mtx is the media changer control program
tapeinfo dumps some interesting stuff out of tape drives' mode pages and
sense pages.
loaderinfo dumps some interesting stuff out of loaders' mode pages and
sense pages.
scsitape sends raw SCSI commands to tape drives. Do not use this unless
you know exactly what you're doing, because you can easily get into a feud
with the system's own tape driver and end up locking up the whole system.
INSTALLATION:
WARNING: MUST HAVE GNU 'make' TO DO THIS! (e.g. use 'gmake' on freebsd, not
BSD 'make'!).
Type ./configure to create a Makefile. Type 'make', then 'make
install'. Type 'man mtx' for info about mtx, and 'man tapeinfo' for
info about tapeinfo. Enjoy.
Credits:
The original 'mtx' program is copyright 1996-1997 by Leonard Zubkoff
<lnz@dandelion.com>. This version was modified for multi-drive,
optical changer, and tape library support by Eric Lee Green
<eric@badtux.org>. Also added FreeBSD support. Please see the man page
for current info, and the file 'mtx.doc' for historical info.
My thanks to Doug Bonnell of Breece Hill for suggestions on
dynamically allocating element info, Tien That Ton of Tandberg for
being the original tester of the Import/Export Element stuff, Ken
Porter for RPM's, William D. Smith for the HP/UX port, Kai Makisara
for the barcode backoff fix, and to all the other people out there who
have used it, found problems with it, and let me know about it (you
know who you are).
-- Eric Lee Green <eric@badtux.org>

45
mtx-1.3.12/README.win32 Normal file
View File

@@ -0,0 +1,45 @@
BUILDING FOR MICROSOFT WINDOWS
==============================
You can perform the build using either MinGW or Microsoft Visual Studio 2005.
Microsoft Visual Studio 2005
----------------------------
Open the solution in msvc/mtx.sln.
Select the Build / Build Solution menu item.
MinGW with GCC
--------------
You must be using at least version 2.16.91 of binutils.
To generate the initial configure script, this only needs to be
done once.
./configure --host=mingw32
make
CHANGES FROM UNIX
=================
The only difference is in the naming of devices. On Linux the changer is
accessed using /dev/sg<N>, on Windows you use Changer<N>.
On Linux the tape drive is referenced using /dev/nst<N>, on Windows you use Tape<N>.
There is one exception, in the case where there isn't a driver loaded for the device.
This is usually only the case on Windows 2000 or if the Windows XP or Windows Server
2003 system supplied driver has been disabled.
In the case where there is no driver loaded you can access the device directly
through the SCSI driver using the following notation:
<port>:<bus>:<target>:<lun>
Port is the adapter number
Bus is the SCSI bus number relative to the adapter
Target is the SCSI device's target ID
LUN is the SCSI device's logical unit number

15
mtx-1.3.12/TODO Normal file
View File

@@ -0,0 +1,15 @@
1 Fix the big element descriptor overflow problem by adding a retry/reallocate
if things overflow.
1 Add a 'timeout' command to adjust the timeout (timeout will be in seconds!).
1 Add IES command (sets CDB[5] to something in routine Inventory() ).
1 Fix 'scsitape' READ and WRITE so that block counts work.
1 Add EXCHANGE command so that we can exchange media between slots in
NSM/DISC optical jukeboxes.
1 If moving media to/from import/export slot, try to do it even if the slot
reports that it's empty or full, this will let us stick our tongue out
on NSM/DISC optical jukeboxes for importing media.
2 Fix ports to other Unixes/VMS.
2 Add a range to 'mtx status' so that we request status only of
the elements we're interested in, rather than of all of them.
(nice for the very big loaders!).
3 Better Import/Export port support?

15
mtx-1.3.12/build.win32 Executable file
View File

@@ -0,0 +1,15 @@
manpages="mtx.1 tapeinfo.1 loaderinfo.1 scsitape.1 scsieject.1"
txtpages="mtx.txt tapeinfo.txt loaderinfo.txt scsitape.txt scsieject.txt"
htmlpages="mtx.html tapeinfo.html loaderinfo.html scsitape.html scsieject.html"
./configure --host=mingw32
make
make dbgs
for i in $manpages
do
groff -et -Thtml -mandoc $i | col -b > `basename $i .1`.html
groff -et -Tascii -mandoc $i | col -b > `basename $i .1`.txt
done
zip mtx-$1.zip README.win32 *.exe $htmlpages $txtpages

1500
mtx-1.3.12/config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

37
mtx-1.3.12/config.h.in Normal file
View File

@@ -0,0 +1,37 @@
/* Copyright 2001 Enhanced Software Technologies Inc.
* Released under GNU General Public License V2 or Above
* See http://www.gnu.org for more information about the terms of
* the GNU General Public License.
* $Date: 2007-02-13 08:45:31 -0800 (Tue, 13 Feb 2007) $
* $Revision: 144 $
*/
#ifndef CONFIG_H
#define CONFIG_H 1
/* autoconf changes these. */
#define HAVE_STRING_H 0
#define HAVE_UNISTD_H 0
#define HAVE_STDLIB_H 0
#define HAVE_STDARG_H 0
#define HAVE_SYS_PARAM_H 0
#define HAVE_SCSI_SCSI_H 0
#define HAVE_SCSI_SCSI_IOCTL_H 0
#define HAVE_SCSI_SG_H 0
#define HAVE_SYS_GSCDDS_H 0
#define HAVE_CAMLIB_H 0
#define HAVE_SYS_SCSI_IMPL_USCSI_H 0
#define HAVE_SYS_SCSI_CTL_H 0
#define HAVE_DSLIB_H 0
#define HAVE_DU_DEFS_H 0
#define HAVE_SYS_STAT_H 0
#define HAVE_SYS_TYPES_H 0
#define HAVE_FCNTL_H 0
#define HAVE_SYS_IOCTL_H 0
#define HAVE_SYS_MTIO_H 0
#define HAVE_DDK_NTDDSCSI_H 0
#define WORDS_BIGENDIAN 0
#endif

1616
mtx-1.3.12/config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

6545
mtx-1.3.12/configure vendored Executable file

File diff suppressed because it is too large Load Diff

113
mtx-1.3.12/configure.in Executable file
View File

@@ -0,0 +1,113 @@
dnl Copyright 2001 Enhanced Software Technologies Inc.
dnl Written Jan. 2001 Eric Lee Green
dnl Process this file with autoconf to produce a configure script.
AC_INIT(mtx.c)
AC_CONFIG_HEADER(config.h)
dnl Check system.
AC_CANONICAL_SYSTEM
AC_PREFIX_DEFAULT(/usr/local)
case "$host_os" in
*linux*) AC_DEFINE(LINUX)
TARGET=linux
;;
*solaris*) AC_DEFINE(SOLARIS)
TARGET=solarissparc
;;
*sunos*) TARGET=solarissparc
;;
*freebsd*) TARGET=freebsd86
;;
*aix*) TARGET=aix
;;
*irix*) TARGET=sgi
;;
*hp*) TARGET=hpux
;;
*HP*) TARGET=hpux
;;
*sequent*) AC_DEFINE(SEQUENT)
;;
*MINGW*) TARGET=mingw
;;
*MinGW*) TARGET=mingw
;;
*mingw*) TARGET=mingw
;;
*) TARGET=$host_os
;;
esac
AC_SUBST(TARGET)
case "$host_cpu" in
# force us down to '386 if we're on some other machine.
*?86*) host_cpu='i386'
CPU=386
;;
*) CPU=$host_cpu;
;;
esac
AC_SUBST(CPU)
dnl Checks for programs.
AC_PROG_CC
AC_PROG_INSTALL
AC_CHECK_PROG(USE_OBJCOPY, objcopy, yes, no)
dnl Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(\
unistd.h \
stdlib.h \
errno.h \
fcntl.h \
stdarg.h \
string.h \
scsi/scsi.h \
scsi/scsi_ioctl.h \
scsi/sg.h \
sys/gscdds.h \
camlib.h \
cam/cam_ccb.h \
cam/scsi/scsi_message.h \
sys/fsid.h \
sys/fstyp.h \
sys/stat.h \
sys/types.h \
sys/mnttab.h \
sys/param.h \
sys/time.h \
sys/scsi/impl/uscsi.h \
sys/scsi.h \
sys/scsi_ctl.h \
sys/ioctl.h \
sys/mtio.h \
sys/param.h \
dslib.h \
du/defs.h \
ddk/ntddscsi.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_SIZE_T
AC_TYPE_PID_T
AC_HEADER_TIME
AC_STRUCT_TM
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
AC_C_BIGENDIAN
dnl Checks for library functions.
dnl AC_FUNC_ALLOCA
AC_TYPE_SIGNAL
AC_FUNC_VPRINTF
dnl Check for files
AC_OUTPUT(Makefile)

209
mtx-1.3.12/contrib/MTX.html Normal file
View File

@@ -0,0 +1,209 @@
<HTML>
<HEAD>
<TITLE>TapeChanger::MTX - use 'mtx' to manipulate a tape library</TITLE>
<LINK REV="made" HREF="mailto:none">
</HEAD>
<BODY>
<A NAME="__index__"></A>
<!-- INDEX BEGIN -->
<UL>
<LI><A HREF="#name">NAME</A></LI>
<LI><A HREF="#synopsis">SYNOPSIS</A></LI>
<LI><A HREF="#description">DESCRIPTION</A></LI>
<LI><A HREF="#variables">VARIABLES</A></LI>
<LI><A HREF="#usage">USAGE</A></LI>
<LI><A HREF="#notes">NOTES</A></LI>
<LI><A HREF="#requirements">REQUIREMENTS</A></LI>
<LI><A HREF="#todo">TODO</A></LI>
<LI><A HREF="#see also">SEE ALSO</A></LI>
<LI><A HREF="#author">AUTHOR</A></LI>
<LI><A HREF="#copyright">COPYRIGHT</A></LI>
</UL>
<!-- INDEX END -->
<HR>
<P>
<H1><A NAME="name">NAME</A></H1>
<P>TapeChanger::MTX - use 'mtx' to manipulate a tape library</P>
<P>
<HR>
<H1><A NAME="synopsis">SYNOPSIS</A></H1>
<PRE>
use TapeChanger::MTX;</PRE>
<PRE>
my $loaded = TapeChanger::MTX-&gt;loadedtape;
print &quot;Currently loaded: $loaded\n&quot; if ($loaded);</PRE>
<PRE>
TapeChanger::MTX-&gt;loadtape('next');
my $nowloaded = TapeChanger::MTX-&gt;loadedtape;
print &quot;Currently loaded: $nowloaded\n&quot; if ($nowloaded);
</PRE>
<PRE>
See below for more available functions.</PRE>
<P>
<HR>
<H1><A NAME="description">DESCRIPTION</A></H1>
<P>TapeChanger::MTX is a module to manipulate a tape library using the 'mtx'
tape library program. It is meant to work with a simple shell/perl script
to load and unload tapes as appropriate, and to provide a interface for
more complicated programs to do the same. The below functions and
variables should do as good a job as explaining this as anything.</P>
<P>
<HR>
<H1><A NAME="variables">VARIABLES</A></H1>
<DL>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AMT_%3Ditem_%24TapeCha">$TapeChanger::MTX::MT
=item $TapeChanger::MTX::MTX</A></STRONG><BR>
<DD>
What is the location of the 'mt' and 'mtx' binaries? Can be set with
'$MT' and '$MTX' in ~/.mtxrc, or defaults to '/usr/sbin/mt' and
'/usr/local/sbin/mtx'.
<P></P>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADRIVE">$TapeChanger::MTX::DRIVE</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ACONTROL">$TapeChanger::MTX::CONTROL</A></STRONG><BR>
<DD>
What are the names of the tape (DRIVE) and changer (CONTROL) device
nodes? Can be set with $DRIVE or $CONTROL in ~/.mtxrc, or default to
'/dev/rmt/0' and '/dev/changer' respectively.
<P></P>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AEJECT">$TapeChanger::MTX::EJECT</A></STRONG><BR>
<DD>
Does the tape drive have to eject the tape before the changer retrieves
it? It's okay to say 'yes' if it's not necessary, in most cases. Can be
set with $EJECT in ~/.mtxrc, or defaults to '1'.
<P></P>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AREADY_TIME">$TapeChanger::MTX::READY_TIME</A></STRONG><BR>
<DD>
How long should we wait to see if the drive is ready, in seconds, after
mounting a volume? Can be set with $READY_TIME in ~/.mtxrc, or defaults
to 60.
<P></P>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADEBUG">$TapeChanger::MTX::DEBUG</A></STRONG><BR>
<DD>
Print debugging information? Set to '0' for normal verbosity, '1' for
debugging information, or '-1' for 'quiet mode' (be as quiet as possible).
<P></P></DL>
<P>
<HR>
<H1><A NAME="usage">USAGE</A></H1>
<P>This module uses the following functions:</P>
<DL>
<DT><STRONG><A NAME="item_tape_cmd">tape_cmd ( COMMAND )</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_mt_cmd">mt_cmd ( COMMAND )</A></STRONG><BR>
<DD>
Runs 'mtx' and 'mt' as appropriate. <CODE>COMMAND</CODE> is the command you're
trying to send to them. Uses 'warn()' to print the commands to the screen
if $TapeChanger::MTX::DEBUG is set.
<P></P>
<DT><STRONG><A NAME="item_numdrives">numdrives ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_numslots">numslots ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_loadedtape">loadedtape ()</A></STRONG><BR>
<DD>
Returns the number of drives, number of slots, and currently loaded tape
values, respectively, by parsing <STRONG>tape_cmd('status')</STRONG>.
<P></P>
<DT><STRONG><A NAME="item_loadtape">loadtape ( SLOT [, DRIVE] )</A></STRONG><BR>
<DD>
Loads a tape into the tape changer, and waits until the drive is again
ready to be written to. <CODE>SLOT</CODE> can be any of the following (with the
relevant function indicated):
<PRE>
current C&lt;loadedtape()&gt;
prev C&lt;loadprevtape()&gt;
next C&lt;loadnexttape()&gt;
first C&lt;loadfirsttape()&gt;
last C&lt;loadlasttape()&gt;
0 C&lt;_ejectdrive()&gt;
1..99 Loads the specified tape number, ejecting whatever is
currently in the drive.</PRE>
<P><CODE>DRIVE</CODE> is the drive to load, and defaults to 0. Returns 0 if
successful, an error string otherwise.</P>
<P></P>
<DT><STRONG><A NAME="item_loadnexttape">loadnexttape ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_loadprevtape">loadprevtape ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_loadfirsttape">loadfirsttape ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_loadlasttape">loadlasttape ()</A></STRONG><BR>
<DD>
Loads the next, previous, first, and last tapes in the changer
respectively. Use <STRONG>tape_cmd('next')</STRONG>, <STRONG>tape_cmd('previous')</STRONG>,
<STRONG>tape_cmd('first')</STRONG>, and <STRONG>tape_cmd('last')</STRONG>, respectively.
<P></P>
<DT><STRONG><A NAME="item_ejecttape">ejecttape ()</A></STRONG><BR>
<DD>
Ejects the tape, by first ejecting the tape from the drive
(<STRONG>mt_cmd(rewind)</STRONG> then <STRONG>mt_cmd(offline)</STRONG>) and then returning it to its
slot (<STRONG>tape_cmd(unload)</STRONG>). Returns 1 if successful, 0 otherwise.
<P></P>
<DT><STRONG><A NAME="item_resetchanger">resetchanger ()</A></STRONG><BR>
<DD>
Resets the changer, ejecting the tape and loading the first one from the
changer.
<P></P>
<DT><STRONG><A NAME="item_checkdrive">checkdrive ()</A></STRONG><BR>
<DD>
Checks to see if the drive is ready or not, by waiting for up to
$TapeChanger::MTX::READY_TIME seconds to see if it can get status
information using <STRONG>mt_cmd(status)</STRONG>. Returns 1 if so, 0 otherwise.
<P></P>
<DT><STRONG><A NAME="item_reportstatus">reportstatus</A></STRONG><BR>
<DD>
Returns a string containing the loaded tape and the drive that it's
mounted on.
<P></P>
<DT><STRONG><A NAME="item_cannot_run">cannot_run ()</A></STRONG><BR>
<DD>
Does some quick checks to see if you're actually capable of using this
module, based on your user permissions. Returns a list of problems if
there are any, 0 otherwise.
<P></P></DL>
<P>
<HR>
<H1><A NAME="notes">NOTES</A></H1>
<P>~/.mtxrc is automatically loaded when this module is used, if it exists,
using do(). This could cause security problems if you're trying to use
this with <CODE>setuid()</CODE> programs - so just don't do that. If you want someone
to have permission to mess with the tape drive and/or changer, let them
have that permission directly.</P>
<P>
<HR>
<H1><A NAME="requirements">REQUIREMENTS</A></H1>
<P>Perl 5.6.0 or better, an installed 'mtx' binary, and a tape changer and
reader connected to the system.</P>
<P>
<HR>
<H1><A NAME="todo">TODO</A></H1>
<P>Support for Input/Export slots is not included, though it may be later.
Possibly works for multiple drives per changer, but I haven't tested it,
so I probably missed something. 'load previous' doesn't actually work,
because mtx doesn't support it (though the help says it does).</P>
<P>
<HR>
<H1><A NAME="see also">SEE ALSO</A></H1>
<P><STRONG>mtx</STRONG>, <STRONG>mt</STRONG>, <STRONG>tapechanger</STRONG>. Inspired by <STRONG>stc-changer</STRONG>, which comes
with the AMANDA tape backup package (http://www.amanda.org), and MTX,
available at <A HREF="http://mtx.sourceforge.net.">http://mtx.sourceforge.net.</A></P>
<P>
<HR>
<H1><A NAME="author">AUTHOR</A></H1>
<P>Tim Skirvin &lt;<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>&gt;</P>
<P>
<HR>
<H1><A NAME="copyright">COPYRIGHT</A></H1>
<P>Copyright 2001-2002 by the University of Illinois Board of Trustees and
Tim Skirvin &lt;<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>&gt;.</P>
</BODY>
</HTML>

View File

@@ -0,0 +1,3 @@
Everything in here is copyrighted by its original copyright holder.
The contents of this directory are not maintained as part of the 'mtx'
project, they are maintained by their original copyright holders.

Binary file not shown.

View File

@@ -0,0 +1,151 @@
#!/bin/sh
# Copyright 2001 Enhanced Software Technologies Inc.
# All Rights Reserved
#
# This software is licensed under the terms of the Free Software Foundation's
# General Public License, version 2. See http://www.fsf.org for more
# inforation on the General Public License. It is released for public use in
# the hope that others will find it useful. Please contact eric@estinc.com
# if you have problems. Also check out our backup products at
# http://www.estinc.com (grin).
#
# usage: config_sgen_solaris.sh check|[un]install
#
# This configures sgen under Solaris (we hope! :-). Note that this
# *CAN* do a reboot of the system. Do NOT call this function unless
# you are willing to let it do a reboot of the system! Also note that
# this *must* be run as user 'root', since it does highly grokety things.
mode="$1"
cvs upd
SGEN="/kernel/drv/sgen"
SGEN_CONF="/kernel/drv/sgen.conf"
do_check() {
if test ! -f $SGEN_CONF; then
# sgen.conf not installed...
return 1
fi
changer_type_count=`grep "changer" $SGEN_CONF | grep -v "^#" | wc -l`
target_count=`grep "target=" $SGEN_CONF | grep -v "^#" | wc -l`
if test $changer_type_count = 0 -o $target_count = 0; then
# sgen.conf not configured
return 1
fi
# sgen.conf installed, and configured
return 0
}
do_install() {
# see if already installed
do_check
if test $? = 0; then
echo "sgen already configured, skipping"
return 0 # successfully installed (?)
fi
if test ! -f $SGEN; then
echo "sgen driver not installed, aborting"
return 1
fi
echo "configuring sgen driver..."
echo 'device-type-config-list="changer"; # BRU-PRO' >>$SGEN_CONF
target=0
while test $target -le 15; do
echo "name=\"sgen\" class=\"scsi\" target=$target lun=0; # BRU-PRO" >>$SGEN_CONF
target=`expr $target + 1`
done
echo "Attempting to reload driver..."
rem_drv sgen >/dev/null 2>&1
add_drv sgen
if test "$?" != "0"; then
# failed
touch /reconfigure
echo "Driver was successfully configured, but could not be re-loaded."
echo "The system must be rebooted for the driver changes to take effect."
ans=""
while test "$ans" = ""; do
printf "Do you want to reboot now (shutdown -g 1 -y -i 6)? [Y/n] "
read ans
if test "$ans" = "Y"; then
ans="y"
fi
if test "$ans" = "N"; then
ans="n"
fi
if test "$ans" != "y" -a "$ans" != "n"; then
echo "Please enter 'y' or 'n'"
ans=""
fi
done
if test "$ans" = "y"; then
shutdown -g 1 -y -i 6
# will be killed by reboot...
while true; do
echo "Waiting for reboot..."
sleep 300
done
fi
# not rebooted, exit with error
return 2
fi
# successful
return 0
}
do_uninstall() {
do_check
if test $? = 1; then
echo "sgen not configured, skipping"
return 0 # successfully uninstalled (?)
fi
printf "removing BRU-PRO configuration from $SGEN_CONF..."
grep -v "# BRU-PRO" $SGEN_CONF > ${SGEN_CONF}.$$ || return 1
cat ${SGEN_CONF}.$$ >${SGEN_CONF} || return 1
rm -f ${SGEN_CONF}.$$ >/dev/null || return 1
printf "done\n"
touch /reconfigure
printf "Devices will be reconfigured at next reboot.\n"
return 0
}
uname | grep SunOS >/dev/null 2>&1
if test $? != 0; then
echo "$0: not on Solaris, ABORT!"
exit 99
fi
case "$mode" in
check)
do_check
;;
install)
do_install
;;
uninstall)
do_uninstall
;;
*)
echo "usage: $0 check|[un]install"
exit 1
;;
esac
exit $?

431
mtx-1.3.12/contrib/mtx-changer Executable file
View File

@@ -0,0 +1,431 @@
#! /bin/sh
###############################################################################
# AMANDA Tape Changer script for use with the MTX tape changer program
# Version 1.0 - Tue Feb 20 13:59:39 CST 2001
#
# Based on 'stc-changer' by Eric Berggren (eric@ee.pdx.edu)
# Updated by Tim Skirvin (tskirvin@ks.uiuc.edu)
#
# Given that there's no license...let's make this the Perl Artistic License.
# Just make sure you give me and Eric credit if you modify this.
###############################################################################
### USER CONFIGURATION
# Name of the tape drive (takes place of "tapedev" option in amanda.conf)
# and default driver number in library (usu 0) that DRIVE_NAME points to
DRIVE_NAME="/dev/rmt/0n"
DRIVE_NUM=0
# Location of "STC" command and control device
MTX_CMD="/usr/local/sbin/mtx";
MTX_CONTROL="/dev/scsi/changer/c4t1d0";
# Whether tape drive must eject tape before changer retrieves
# (ie, EXB-2x0). Usually okay if set while not necessary, bad if
# required but not set.
DRIVE_MUST_EJECT=1
# How long to check drive readiness (in seconds) after mounting (or
# ejecting) a volume (on some libraries, the motion or eject command may
# complete before the drive has the volume fully mounted and online,
# or ready for retrieval, resulting in "Drive not ready"/"Media not
# ready" errors). Do an "mt status" command every 5 seconds upto this
# time.
DRIVE_READY_TIME_MAX=120
# tape "mt" command location...
MT_CMD="/usr/bin/mt" # called via "MT_CMD -f DRIVE_NAME rewind" &
# "MT_CMD -f DRIVE_NAME offline" to eject
# and "MT_CMD -f DRIVE_NAME status" to get ready info
##############################################################################
#
NumDrives=-1
NumSlots=-1
LastSlot=-1
LoadedTape=-1
#
# Usage information
#
usage()
{
echo
echo "Usage: $Progname <command> [arg...]"
echo " -info reports capability and loaded tape"
echo " -slot <slot> loads specified tape into drive"
echo " current reports current mounted tape"
echo " next loads logically next tape (loops to top)"
echo " prev loads logically previous tape (loops to bot)"
echo " first loads first tape"
echo " last loads last tape"
echo " 0..99 loads tape from specified slot#"
echo " -eject uloads current mounted tape"
echo " -reset resets changer (and drive); loads first tape"
echo
exit 5
}
#
# Perform "stc" changer command (& handle the "fatal" errors)
# else, set 'CommandResStr' and 'CommandRawResStr' to the result string
# and 'CommandResCode' to the exit code
#
dotapecmd()
{
cmd=$1
arg=$2
CommandResStr=`$MTX_CMD $MTX_CONTROL $cmd $arg 2>&1`
CommandRawResStr=$CommandResStr
CommandResCode=$?
CommandResStr=`echo $CommandResStr | head -1 | sed 's/^[^:]*: //'`
if [ $CommandResCode -gt 1 ]; then
echo "0 $Progname: returned $CommandResStr"
exit 2
fi
}
#
# Unload tape from drive (a drive command; "ejecttape" is a changer command
# to actually retrieve the tape). Needed by some changers (controlled by
# setting "DRIVE_MUST_EJECT")
#
ejectdrive()
{
# Tell drive to eject tape before changer retrieves; req'd by some
# drives (ie, EXB-2x0). Not needed by QDLT-4x00. Do a "rewind"
# command first, then "offline" to eject (instead of "rewoffl")
#
if [ "$DRIVE_MUST_EJECT" -ne 0 ]; then
mtresstr=`$MT_CMD -f $DRIVE_NAME rewind 2>&1`
mtrescode=$?
if [ $mtrescode -ne 0 ]; then
if echo "$mtresstr" | egrep -s 'no tape'; then
:; # no tape mounted; assume okay...
else
# can't eject tape, bad; output: <tape#> reason
echo "0 $mtresstr"
exit 1
fi
else
mtresstr=`$MT_CMD -f $DRIVE_NAME offline 2>&1`
mtrescode=$?
checkdrive 1
fi
fi
}
#
# Check drive readiness after (un)mounting a volume (which may take a while
# after the volume change command completes)
#
checkdrive()
{
unmounting=$1
if [ "$DRIVE_READY_TIME_MAX" -gt 0 ]; then
# sleep time between checks
pausetime=5
# number of interations to check
numchecks=`expr $DRIVE_READY_TIME_MAX / $pausetime`
if [ "$numchecks" -eq 0 ]; then
numchecks=1
fi
# check until success, or out of attempts...
while [ "$numchecks" -gt 0 ]; do
mtresstr=`$MT_CMD -f $DRIVE_NAME status 2>&1`
mtrescode=$?
if [ $mtrescode -eq 0 ]; then
# Success ?
return 0
else
# pause, before trying again....
if [ "$numchecks" -gt 1 ]; then
sleep $pausetime
# if unmounting a volume, check for 'mt' command
# failure; (sleep first for additional comfort)
if [ "$unmounting" -ne 0 ]; then
return 0
fi
fi
fi
numchecks=`expr $numchecks - 1`
done
# failed; output: -1 reason
echo "-1 drive won't report ready"
exit 1
fi
}
#
# Get changer parameters
#
getchangerparms()
{
dotapecmd status
if [ $CommandResCode -eq 0 ] && \
echo "$CommandResStr" | egrep -s '^Storage Changer'; then
NumDrives=`echo $dspec | wc -l`
NumDrives=`echo "$CommandRawResStr" | \
grep 'Data Transfer Element' | wc -l`
if [ "$NumDrives" -le "$DRIVE_NUM" ]; then
echo "$Program: Invalid drive # specified ($DRIVE_NUM > $NumDrives)"
exit 3
fi
# grep 'Data Transfer Element $DRIVE_NUM' | \
LoadedTape=`echo "$CommandRawResStr" | \
grep 'Data Transfer Element' | \
grep 'Storage Element [0-9]' | \
awk '{ print $7 }' `
if [ -z "$LoadedTape" -o "$LoadedTape" = "e" ]; then
LoadedTape=-1
fi
NumSlots=`echo "$CommandRawResStr" | \
grep 'Storage Element [0-9]\{1,\}:' | \
grep -v 'Data Element' | \
wc -l | sed -e 's/ //g' `
LastSlot=`expr $NumSlots - 1`
else
echo \
"$Progname: Can't get changer parameters; Result was $CommandResStr"
exit 3
fi
}
#
# Display changer info
#
changerinfo()
{
getchangerparms
# output status string: currenttape numslots randomaccess?
echo "$LoadedTape $NumSlots 1"
exit 0
}
#
# Eject current mounted tape
#
ejecttape()
{
getchangerparms
ct=$LoadedTape
# If no tape reported mounted, assume success (could be bad if changer
# lost track of tape)
#
if [ $ct -lt 0 ]; then
CommandResCode=0
else
ejectdrive
dotapecmd unload
fi
if [ $CommandResCode -ne 0 ]; then
# failed; output: <tape#> reason
echo "$ct $CommandResStr"
exit 1
else
# success; output: <tape#> drive
echo "$ct $DRIVE_NAME"
exit 0
fi
}
#
# Move specified tape into drive (operation level)
#
doloadtape()
{
slot=$1
if [ "$slot" -eq "$LoadedTape" ]; then
return 0
fi
ejectdrive
dotapecmd load $slot
return $CommandResCode
}
#
# Load next available tape into drive
#
loadnexttape()
{
curslot=$1
direction=$2
startslot=$curslot
while true; do
if doloadtape $curslot; then
return 0
else
if echo $CommandResStr | egrep -s 'Slot.*reported empty'; then
if [ "$direction" -lt 0 ]; then
curslot=`expr $curslot - 1`
if [ "$curslot" -lt 0 ]; then
curslot=$LastSlot
fi
else
curslot=`expr $curslot + 1`
if [ "$curslot" -gt "$LastSlot" ]; then
curslot=0
fi
fi
# Check if we're back to where we started...
if [ "$curslot" = "$startslot" ]; then
if [ "$direction" -lt 0 ]; then
CommandResStr="No previous volume available"
else
CommandResStr="No subsequent volume available"
fi
return 1
fi
else
return 1
fi
fi
done
}
#
# Report loadtape() status
#
reportstatus()
{
if [ $CommandResCode -eq 0 ]; then
# success; output currenttape drivename
echo "$LoadedTape $DRIVE_NAME"
exit 0
else
# failed (empty slot?); output currenttape reason
echo "$LoadedTape $CommandResStr"
exit 1
fi
}
#
# Move specified tape into drive (command level)
#
loadtape()
{
slot=$1
getchangerparms
case "$slot" in
current)
if [ $LoadedTape -lt 0 ]; then
CommandResStr="Can't determine current tape; drive empty ?"
CommandResCode=1
fi
;;
prev)
if [ $LoadedTape -le 0 ]; then
loadnexttape $LastSlot -1
else
loadnexttape `expr $LoadedTape - 1` -1
fi
;;
next)
if [ $LoadedTape -ge $LastSlot -o $LoadedTape -lt 0 ]; then
loadnexttape 0 1
else
loadnexttape `expr $LoadedTape + 1` 1
fi
;;
first)
loadnexttape 0 1
;;
last)
loadnexttape $LastSlot -1
;;
[0-9]*)
doloadtape $slot
;;
*)
# error; no valid slot specified
echo "$Progname: No valid slot specified"
exit 1
;;
esac
if [ $CommandResCode -eq 0 ]; then
getchangerparms
checkdrive
fi
reportstatus
}
#
# Reset changer to known state
#
resetchanger()
{
ejectdrive
dotapecmd reset
if [ $CommandResCode -ne 0 ]; then
# failed; output: failed? reason
echo "-1 $CommandResStr"
exit 2;
else
loadtape first
fi
}
#############################################################################
#
# MAIN
#
Progname=`basename $0`
if [ ! -x "$MTX_CMD" ]; then
echo "-1 $Progname: cannot run STC command ($MTX_CMD)"
exit 2
fi
if [ -n "$MTX_CONTROL" ]; then
if echo "$MTX_CONTROL" | egrep -s '^-f'; then
:;
else
MTX_CONTROL="-f $MTX_CONTROL"
fi
fi
if [ -n "$DRIVE_NUM" ]; then
DRIVE_NUM=0
fi
if [ $# -ge 1 ]; then command=$1; else command="-usage"; fi
case "$command" in
-info)
changerinfo
;;
-slot)
loadtape $2
;;
-eject)
ejecttape
;;
-reset)
resetchanger
;;
*)
usage
;;
esac
exit 0

306
mtx-1.3.12/contrib/mtx.py Normal file
View File

@@ -0,0 +1,306 @@
# Copyright 2000 Enhanced Software Technologies Inc.
# Released under Free Software Foundation's General Public License,
# Version 2 or above
#
# This is an example of how to parse the 'mtx' output from a scripting
# language.
#
# Routine to call 'mtx' and read status, or
# whatever.
#
# We do this here rather than creating a Python "C" module because:
# 1) Reduces complexity of compile environment
# 2) Easier debugging.
# 3) More in keeping with the Unix philosophy of things.
# 4) Easier to add new functionality.
#
#
import string
import os
import time # we can do some waiting here...
def readpipe(command):
result=""
infile=os.popen(command,"r")
try:
s=infile.read()
except:
s=""
pass
if not s:
return None # didn't get anything :-(.
result=result+s
return result
# these are returned by the mtx.status routine:
class slotstatus:
def __init__(self,slotnum,middle,barcode=None):
middle=string.strip(middle)
try:
left,right=string.split(middle," ")
except:
left=middle
right=None
pass
# Note: We will not be able to test this at the moment since the
# 220 has no import/export port!
if right=="IMPORT/EXPORT":
self.importexport=1
else:
self.importexport=0
pass
self.slotnum=int(slotnum) # make sure it's an integer...
if left=="Full":
self.full=1
else:
self.full=None
pass
if not barcode:
self.voltag=None
else:
l=string.split(barcode,"=",1)
self.voltag=l[1]
pass
return
pass
# Drive status lines have this format:
#Data Transfer Element 0:Full (Storage Element 10 Loaded):VolumeTag = B0000009H
#Data Transfer Element 1:Empty
class drivestatus:
def __init__(self,slotnum,middle,barcode=None):
self.slotnum=slotnum
if middle=="Empty":
self.full=None
self.origin=None
self.voltag=None
return
else:
self.full=1
pass
# okay, we know we have a tape in the drive.
# split and find out our origin: we will always have
# an origin, even if the #$@% mtx program had to dig one
# out of the air:
l=string.split(middle," ")
self.origin=int(l[3])
if not barcode:
self.voltag=None # barcode of this element.
else:
l=string.split(barcode," ")
self.voltag=string.strip(l[2])
pass
return
pass
# This is the return value for mtx.status.
class mtxstatus:
def __init__(self):
self.numdrives=None
self.numslots=None
self.numexport=None
self.drives=[] # a list of drivestatus instances.
self.slots=[] # a list of slotstatus instances
self.export=[] # another list of slotstatus instances
return
pass
# call 'mtx' and get barcode info, if possible:
# okay, we now have a string that consists of a number of lines.
# we want to explode this string into its component parts.
# Example format:
# Storage Changer /dev/sgd:2 Drives, 21 Slots
# Data Transfer Element 0:Full (Storage Element '5' Loaded)
# Data Transfer Element 1:Empty
# Storage Element 1:Full :VolumeTag=CLNA0001
# Storage Element 2:Full :VolumeTag=B0000009
# Storage Element 3:Full :VolumeTag=B0000010
# ....
# What we want to do, then, is:
# 1) Turn it into lines by doing a string.split on newline.
# 2) Split the 1st line on ":" to get left and right.
# 3) Split the right half on space to get #drives "Drives," #slots
# 4) process the drives (Full,Empty, etc.)
# 5) For each of the remaining lines: Split on ':'
# 6) Full/Empty status is in 2)
#
configdir="/opt/brupro/bin" # sigh.
def status(device):
retval=mtxstatus()
command="%s/mtx -f %s status" % (configdir,device)
result=readpipe(command)
if not result:
return None # sorry!
# now to parse:
try:
lines=string.split(result,"\n")
except:
return None # sorry, no status!
# print "DEBUG:lines=",lines
try:
l=string.split(lines[0],":")
except:
return None # sorry, no status!
# print "DEBUG:l=",l
leftside=l[0]
rightside=l[1]
if len(l) > 2:
barcode=l[3]
else:
barcode=None
pass
t=string.split(rightside)
retval.numdrives=int(t[0])
retval.numslots=int(t[2])
for s in lines[1:]:
if not s:
continue # skip blank lines!
#print "DEBUG:s=",s
parts=string.split(s,":")
leftpart=string.split(parts[0])
rightpart=parts[1]
try:
barcode=parts[2]
except:
barcode=None
pass
#print "DEBUG:leftpart=",leftpart
if leftpart[0]=="Data" and leftpart[1]=="Transfer":
retval.drives.append(drivestatus(leftpart[3],rightpart,barcode))
pass
if leftpart[0]=="Storage" and leftpart[1]=="Element":
element=slotstatus(leftpart[2],rightpart,barcode)
if element.importexport:
retval.export.append(element)
else:
retval.slots.append(element)
pass
pass
continue
return retval
# Output of a mtx inquiry looks like:
#
#Product Type: Medium Changer
#Vendor ID: 'EXABYTE '
#Product ID: 'Exabyte EZ17 '
#Revision: '1.07'
#Attached Changer: No
#
# We simply return a hash table with these values { left:right } format.
def mtxinquiry(device):
command="%s/mtx -f %s inquiry" % (configdir,device)
str=readpipe(command) # calls the command, returns all its data.
str=string.strip(str)
lines=string.split(str,"\n")
retval={}
for l in lines:
# DEBUG #
l=string.strip(l)
print "splitting line: '",l,"'"
idx,val=string.split(l,':',1)
val=string.strip(val)
if val[0]=="'":
val=val[1:-1] # strip off single quotes, sigh.
pass
retval[idx]=val
continue
return retval
# Now for the easy part:
def load(device,slot,drive=0):
command="%s/mtx -f %s load %s %s >/dev/null " % (configdir,device,slot,drive)
status=os.system(command)
return status
def unload(device,slot,drive=0):
command="%s/mtx -f %s unload %s %s >/dev/null " % (configdir,device,slot,drive)
return os.system(command)
def inventory(device):
command="%s/mtx -f %s inventory >/dev/null " % (configdir,device)
return os.system(command)
def wait_for_inventory(device):
# loop while we have an error return...
errcount=0
while inventory(device):
if errcount==0:
print "Waiting for loader '%s'" % device
pass
time.sleep(1)
try:
s=status(device)
except:
s=None
pass
if s:
return 0 # well, whatever we're doing, we're inventoried :-(
errcount=errcount+1
if errcount==600: # we've been waiting for 10 minutes :-(
return 1 # sorry!
continue
return 0 # we succeeded!
# RCS REVISION LOG:
# $Log$
# Revision 1.1 2001/06/05 17:10:51 elgreen
# Initial revision
#
# Revision 1.2 2000/12/22 14:17:19 eric
# mtx 1.2.11pre1
#
# Revision 1.14 2000/11/12 20:35:29 eric
# do string.strip on the voltag
#
# Revision 1.13 2000/11/04 00:33:38 eric
# if we can get an inventory on the loader after we send it 'mtx inventory',
# then obviously we managed to do SOMETHING.
#
# Revision 1.12 2000/10/28 00:04:34 eric
# added wait_for_inventory command
#
# Revision 1.11 2000/10/27 23:27:58 eric
# Added inventory command...
#
# Revision 1.10 2000/10/01 01:06:29 eric
# evening checkin
#
# Revision 1.9 2000/09/29 02:49:29 eric
# evening checkin
#
# Revision 1.8 2000/09/02 01:05:33 eric
# Evening Checkin
#
# Revision 1.7 2000/09/01 00:08:11 eric
# strip lines in mtxinquiry
#
# Revision 1.6 2000/09/01 00:05:33 eric
# debugging
#
# Revision 1.5 2000/08/31 23:46:01 eric
# fix def:
#
# Revision 1.4 2000/08/31 23:44:06 eric
# =->==
#

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,209 @@
<HTML>
<HEAD>
<TITLE>TapeChanger::MTX - use 'mtx' to manipulate a tape library</TITLE>
<LINK REV="made" HREF="mailto:none">
</HEAD>
<BODY>
<A NAME="__index__"></A>
<!-- INDEX BEGIN -->
<UL>
<LI><A HREF="#name">NAME</A></LI>
<LI><A HREF="#synopsis">SYNOPSIS</A></LI>
<LI><A HREF="#description">DESCRIPTION</A></LI>
<LI><A HREF="#variables">VARIABLES</A></LI>
<LI><A HREF="#usage">USAGE</A></LI>
<LI><A HREF="#notes">NOTES</A></LI>
<LI><A HREF="#requirements">REQUIREMENTS</A></LI>
<LI><A HREF="#todo">TODO</A></LI>
<LI><A HREF="#see also">SEE ALSO</A></LI>
<LI><A HREF="#author">AUTHOR</A></LI>
<LI><A HREF="#copyright">COPYRIGHT</A></LI>
</UL>
<!-- INDEX END -->
<HR>
<P>
<H1><A NAME="name">NAME</A></H1>
<P>TapeChanger::MTX - use 'mtx' to manipulate a tape library</P>
<P>
<HR>
<H1><A NAME="synopsis">SYNOPSIS</A></H1>
<PRE>
use TapeChanger::MTX;</PRE>
<PRE>
my $loaded = TapeChanger::MTX-&gt;loadedtape;
print &quot;Currently loaded: $loaded\n&quot; if ($loaded);</PRE>
<PRE>
TapeChanger::MTX-&gt;loadtape('next');
my $nowloaded = TapeChanger::MTX-&gt;loadedtape;
print &quot;Currently loaded: $nowloaded\n&quot; if ($nowloaded);
</PRE>
<PRE>
See below for more available functions.</PRE>
<P>
<HR>
<H1><A NAME="description">DESCRIPTION</A></H1>
<P>TapeChanger::MTX is a module to manipulate a tape library using the 'mtx'
tape library program. It is meant to work with a simple shell/perl script
to load and unload tapes as appropriate, and to provide a interface for
more complicated programs to do the same. The below functions and
variables should do as good a job as explaining this as anything.</P>
<P>
<HR>
<H1><A NAME="variables">VARIABLES</A></H1>
<DL>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AMT_%3Ditem_%24TapeCha">$TapeChanger::MTX::MT
=item $TapeChanger::MTX::MTX</A></STRONG><BR>
<DD>
What is the location of the 'mt' and 'mtx' binaries? Can be set with
'$MT' and '$MTX' in ~/.mtxrc, or defaults to '/usr/sbin/mt' and
'/usr/local/sbin/mtx'.
<P></P>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADRIVE">$TapeChanger::MTX::DRIVE</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ACONTROL">$TapeChanger::MTX::CONTROL</A></STRONG><BR>
<DD>
What are the names of the tape (DRIVE) and changer (CONTROL) device
nodes? Can be set with $DRIVE or $CONTROL in ~/.mtxrc, or default to
'/dev/rmt/0' and '/dev/changer' respectively.
<P></P>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AEJECT">$TapeChanger::MTX::EJECT</A></STRONG><BR>
<DD>
Does the tape drive have to eject the tape before the changer retrieves
it? It's okay to say 'yes' if it's not necessary, in most cases. Can be
set with $EJECT in ~/.mtxrc, or defaults to '1'.
<P></P>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3AREADY_TIME">$TapeChanger::MTX::READY_TIME</A></STRONG><BR>
<DD>
How long should we wait to see if the drive is ready, in seconds, after
mounting a volume? Can be set with $READY_TIME in ~/.mtxrc, or defaults
to 60.
<P></P>
<DT><STRONG><A NAME="item_%24TapeChanger%3A%3AMTX%3A%3ADEBUG">$TapeChanger::MTX::DEBUG</A></STRONG><BR>
<DD>
Print debugging information? Set to '0' for normal verbosity, '1' for
debugging information, or '-1' for 'quiet mode' (be as quiet as possible).
<P></P></DL>
<P>
<HR>
<H1><A NAME="usage">USAGE</A></H1>
<P>This module uses the following functions:</P>
<DL>
<DT><STRONG><A NAME="item_tape_cmd">tape_cmd ( COMMAND )</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_mt_cmd">mt_cmd ( COMMAND )</A></STRONG><BR>
<DD>
Runs 'mtx' and 'mt' as appropriate. <CODE>COMMAND</CODE> is the command you're
trying to send to them. Uses 'warn()' to print the commands to the screen
if $TapeChanger::MTX::DEBUG is set.
<P></P>
<DT><STRONG><A NAME="item_numdrives">numdrives ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_numslots">numslots ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_loadedtape">loadedtape ()</A></STRONG><BR>
<DD>
Returns the number of drives, number of slots, and currently loaded tape
values, respectively, by parsing <STRONG>tape_cmd('status')</STRONG>.
<P></P>
<DT><STRONG><A NAME="item_loadtape">loadtape ( SLOT [, DRIVE] )</A></STRONG><BR>
<DD>
Loads a tape into the tape changer, and waits until the drive is again
ready to be written to. <CODE>SLOT</CODE> can be any of the following (with the
relevant function indicated):
<PRE>
current C&lt;loadedtape()&gt;
prev C&lt;loadprevtape()&gt;
next C&lt;loadnexttape()&gt;
first C&lt;loadfirsttape()&gt;
last C&lt;loadlasttape()&gt;
0 C&lt;_ejectdrive()&gt;
1..99 Loads the specified tape number, ejecting whatever is
currently in the drive.</PRE>
<P><CODE>DRIVE</CODE> is the drive to load, and defaults to 0. Returns 0 if
successful, an error string otherwise.</P>
<P></P>
<DT><STRONG><A NAME="item_loadnexttape">loadnexttape ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_loadprevtape">loadprevtape ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_loadfirsttape">loadfirsttape ()</A></STRONG><BR>
<DD>
<DT><STRONG><A NAME="item_loadlasttape">loadlasttape ()</A></STRONG><BR>
<DD>
Loads the next, previous, first, and last tapes in the changer
respectively. Use <STRONG>tape_cmd('next')</STRONG>, <STRONG>tape_cmd('previous')</STRONG>,
<STRONG>tape_cmd('first')</STRONG>, and <STRONG>tape_cmd('last')</STRONG>, respectively.
<P></P>
<DT><STRONG><A NAME="item_ejecttape">ejecttape ()</A></STRONG><BR>
<DD>
Ejects the tape, by first ejecting the tape from the drive
(<STRONG>mt_cmd(rewind)</STRONG> then <STRONG>mt_cmd(offline)</STRONG>) and then returning it to its
slot (<STRONG>tape_cmd(unload)</STRONG>). Returns 1 if successful, 0 otherwise.
<P></P>
<DT><STRONG><A NAME="item_resetchanger">resetchanger ()</A></STRONG><BR>
<DD>
Resets the changer, ejecting the tape and loading the first one from the
changer.
<P></P>
<DT><STRONG><A NAME="item_checkdrive">checkdrive ()</A></STRONG><BR>
<DD>
Checks to see if the drive is ready or not, by waiting for up to
$TapeChanger::MTX::READY_TIME seconds to see if it can get status
information using <STRONG>mt_cmd(status)</STRONG>. Returns 1 if so, 0 otherwise.
<P></P>
<DT><STRONG><A NAME="item_reportstatus">reportstatus</A></STRONG><BR>
<DD>
Returns a string containing the loaded tape and the drive that it's
mounted on.
<P></P>
<DT><STRONG><A NAME="item_cannot_run">cannot_run ()</A></STRONG><BR>
<DD>
Does some quick checks to see if you're actually capable of using this
module, based on your user permissions. Returns a list of problems if
there are any, 0 otherwise.
<P></P></DL>
<P>
<HR>
<H1><A NAME="notes">NOTES</A></H1>
<P>~/.mtxrc is automatically loaded when this module is used, if it exists,
using do(). This could cause security problems if you're trying to use
this with <CODE>setuid()</CODE> programs - so just don't do that. If you want someone
to have permission to mess with the tape drive and/or changer, let them
have that permission directly.</P>
<P>
<HR>
<H1><A NAME="requirements">REQUIREMENTS</A></H1>
<P>Perl 5.6.0 or better, an installed 'mtx' binary, and a tape changer and
reader connected to the system.</P>
<P>
<HR>
<H1><A NAME="todo">TODO</A></H1>
<P>Support for Input/Export slots is not included, though it may be later.
Possibly works for multiple drives per changer, but I haven't tested it,
so I probably missed something. 'load previous' doesn't actually work,
because mtx doesn't support it (though the help says it does).</P>
<P>
<HR>
<H1><A NAME="see also">SEE ALSO</A></H1>
<P><STRONG>mtx</STRONG>, <STRONG>mt</STRONG>, <STRONG>tapechanger</STRONG>. Inspired by <STRONG>stc-changer</STRONG>, which comes
with the AMANDA tape backup package (http://www.amanda.org), and MTX,
available at <A HREF="http://mtx.sourceforge.net.">http://mtx.sourceforge.net.</A></P>
<P>
<HR>
<H1><A NAME="author">AUTHOR</A></H1>
<P>Tim Skirvin &lt;<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>&gt;</P>
<P>
<HR>
<H1><A NAME="copyright">COPYRIGHT</A></H1>
<P>Copyright 2001-2002 by the University of Illinois Board of Trustees and
Tim Skirvin &lt;<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>&gt;.</P>
</BODY>
</HTML>

View File

@@ -0,0 +1,55 @@
# Copyright 2000 Enhanced Software Technologies Inc.
# All Rights Reserved
# Released under Free Software Foundation's General Public License,
# Version 2 or above
# Routine to call 'tapeinfo' and read status for a node. This is an
# example of how to parse the 'tapeinfo' output from a scripting language.
#
import os
import string
import sys
configdir="/opt/brupro/bin" # sigh.
def inquiry(device):
retval={}
# okay, now do the thing:
command="%s/tapeinfo -f %s" % (configdir,device)
# Now to read:
infile=os.popen(command,"r")
try:
s=infile.readline()
except:
s=""
pass
if not s:
return None # did not get anything.
while s:
s=string.strip(s)
idx,val=string.split(s,':',1)
val=string.strip(val)
if val[0]=="'":
val=val[1:-1] # strip off single quotes, sigh.
val=string.strip(val)
pass
while "\0" in val:
# zapo!
val=string.replace(val,"\0","")
pass
retval[idx]=val
try:
s=infile.readline()
except:
s=""
pass
continue # to top of loop!
return retval

View File

@@ -0,0 +1,118 @@
!/usr/bin/perl
########################## tapeload ###########################
# This script uses mtx 1.2.9pre2 to load a tape based
# on its volume tag. You can
# specify a tape drive by number, but if you don`t, it puts the
# tape in the first available drive and returns the number of that drive,
# both from the standard output and as the exit value.
# A negative exit value indicates an error.
# If volume tags are missing from any full slot, bar codes are rescanned
# automatically.
#
# usage:
# tapeload TAPE_LABEL_1 # Loads tape with label TAPE_LABEL_1 into a drive
# or
# tapeload TAPE_LABEL_1 1 # Loads tape with label TAPE_LABEL_1 into drive #1
#
# Set this variable to your mtx binary and proper scsi library device.
$MTXBIN="/usr/local/bin/mtx -f /dev/sga" ;
# Additions and corrections are welcome.
# This software comes with absolutely no warranty and every other imaginable
# disclaimer.
# -- Frank Samuelson sam@milagro.lanl.gov
##################################################################
@wt= &mdo("status"); #slurp in the list of slots
# Check to be certain that every full slot has a volume tag
for ($i=0; $i< $#wt; $i++) { # look through every line
if ( $wt[$i] =~ /Full/ && $wt[$i] !~ /VolumeTag/ ) {
# if the element is full, but there is no volume tag, do inventory
@wt= &mdo("inventory status");
break;
}
}
#try to find our tape
$slot=-1;
for ($i=0; $i< $#wt; $i++) { # look through every line
if ($wt[$i] =~ / *Storage Element (d*):Full :VolumeTag=(.*)/ ) {
if ($ARGV[0] eq $2) { # We found the tape
$slot=$1; # set the slot number
break; # stop reading the rest of the file.
}
}
}
if ( $slot>0) { # we found the tape you wanted.
$drivefound=-1; # set flag to bad value
for ($i=0; $i< $#wt; $i++) { # look through every line
# if this is a tape drive
if ($wt[$i] =~ / *Data Transfer Element (d*):(.*)/ ) { #parse the line
$drive=$1;
$state=$2;
# print STDERR "$wt[$i] $drive $state";
if ($state =~ /Full/) { # This drive is full.
# if we are looking for a particular drive and this is it
if ( $#ARGV==1 && $drive == $ARGV[1]) {
print STDERR " ERROR: Specified drive $ARGV[1] is full.";
print STDERR @wt;
exit(-6);
}
} elsif ($state =~ /Empty/) { #This is a tape drive and it`s empty.
if ( $#ARGV==1 ) { # If we want a particular drive
if ($drive == $ARGV[1]) { # and this is it,
$drivefound=$drive; # mark it so.
break;
}
} else { # If any old drive will do
$drivefound=$drive; # Mark it.
break;
}
} else { # This is a tape drive, but what the heck is it?
print STDERR " Cannot assess drive status in line";
print STDERR $wt[$i];
exit(-7);
}
}
}
if ( $drivefound < 0 ) { # specified drive was not found
print STDERR "Error: Specified drive $ARGV[1] was not found";
print STDERR @wt;
exit(-8);
}
# Now we actually load the tape.
@dump=&mdo(" load $slot $drivefound ");
print "$drivefound";
exit($drivefound);
# The end.
} else {
print STDERR " Ug. Tape $ARGV[0] is not in the library.";
print STDERR @wt;
exit(-4);
}
sub mdo # a subroutine to call mtx ;
{
# print STDERR "$_[0]";
if (!open(FD,"$MTXBIN $_[0] |")) { #call mtx function
print STDERR " ERRKK. Could not start mtx ";
exit (-9);
}
@twt= <FD>; # slurp in the output
if (! close(FD)) { # if mtx exited with a nonzero value...
print STDERR " Mtx gave an error. Tapeload is exiting... ";
exit (-10);
}
@twt;
}

View File

@@ -0,0 +1,98 @@
#!/usr/bin/perl
########################## tapeunload ###########################
# This script uses mtx 1.2.9pre2 to unload a tape
# based on its volume tag. You can
# specify a slot into which the tape should go, but if you don`t, it puts the
# tape into the slot from which it was taken. This slot number
# is returned
# both from the standard output and as the exit value.
# A negative exit value indicates an error.
# If volume tags are missing from any full slot or drive,
# bar codes are rescanned automatically.
# Note: This script assumes that the tape is ready to be removed from
# the drive. That means you may have to unload the tape from the drive
# with "mt offline" before the tape can be moved to a storage slot.
#
# usage:
# tapeunload TAPE_LABEL_1
# Removes tape with label TAPE_LABEL_1 from a drive and puts it
# back into its storage slot. Or,
# tapeunload TAPE_LABEL_1 40
# Removes tape with label TAPE_LABEL_1 from a drive and puts it
# into its storage slot 40.
#
# Set this variable to your mtx binary and proper scsi library device.
$MTXBIN="/usr/local/bin/mtx -f /dev/sga" ;
# Additions and corrections are welcome.
# This software comes with absolutely no warranty and every other imaginable
# disclaimer.
# -- Frank Samuelson sam@milagro.lanl.gov
##################################################################
@wt= &mdo("status"); #slurp in the list of slots
# Check to be certain that every full slot has a volume tag
# Rescanning probably will not help. I haven`t seen any bar code
# readers that read tapes that are in the drives. But you never know...
for ($i=0; $i< $#wt; $i++) { # look through every line
if ( $wt[$i] =~ /Full/ && $wt[$i] !~ /VolumeTag/ ) {
# if the element is full, but there is no volume tag, do inventory
@wt= &mdo("inventory status");
break;
}
}
#try to find our tape
$drivein=-1;
for ($i=0; $i< $#wt; $i++) { # look through every line
# for a full tape drive
if ($wt[$i] =~ / *Data Transfer Element (d*):Full (Storage Element
(d*) Loaded):VolumeTag = (.*)/ ){
if ($ARGV[0] eq $3) { # We found our tape
$drivein=$1; # set the drive number
$slottogo=$2; # set the slot number
break; # stop reading the rest of the file.
}
}
}
if ( $drivein>=0) { # we found the tape you wanted.
if ($#ARGV==1) { #If an alternative slot was requested, set it.
$slottogo=$ARGV[1]; # and let mtx handle the errors.
}
# Now we unload the tape.
@dump=&mdo(" unload $slottogo $drivein ");
print "$slottogo";
exit($slottogo);
# The end.
} else {
print STDERR " Ug. Tape $ARGV[0] is not in a tape drive.";
print STDERR @wt;
exit(-4);
}
sub mdo # a subroutine to call mtx ;
{
# print STDERR "$_[0]";
if (!open(FD,"$MTXBIN $_[0] |")) { #call mtx function
print STDERR " ERRKK. Could not start mtx ";
exit (-9);
}
@twt= <FD>; # slurp in the output
if (! close(FD)) { # if mtx exited with a nonzero value...
print STDERR " Mtx gave an error. Tapeload is exiting... ";
exit (-10);
}
@twt;
}

View File

@@ -0,0 +1,45 @@
# mtx completion by Jon Middleton <jjm@ixtab.org.uk>
#
# $Id: bash_completion,v 1.1 2004-02-15 05:43:25 bdale Exp $
_mtx()
{
local cur prev options tapes drives
COMPREPLY=()
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
options="-f nobarcode invert noattach --version inquiry noattach \
inventory status load unload eepos first last next"
tapes=$(mtx status | \
awk '/Storage Element [0-9]+:Full/ { printf "%s ", $3 }')
tapes=${tapes//:Full}
drives=$(mtx status | \
awk '/Data Transfer Element [0-9]+:(Full|Empty)/ { printf "%s ", $4 }')
drives=${drives//:Full}
drives=${drives//:Empty}
if [ $COMP_CWORD -gt 1 ]; then
case $prev in
load)
COMPREPLY=( $( compgen -W "$tapes" -- $cur ) )
;;
unload|first|last|next)
COMPREPLY=( $( compgen -W "$drives" -- $cur ) )
;;
-f)
true
;;
*)
true
;;
esac
else
COMPREPLY=( $( compgen -W "$options" -- $cur ) )
fi
return 0
}
complete -F _mtx mtx

159
mtx-1.3.12/debian/changelog Normal file
View File

@@ -0,0 +1,159 @@
mtx (1.3.12-1) unstable; urgency=low
* Incorporate debian packaging into upsteam version
* Remove strip of binaries
* Remove unsupported nsmhack from list of binaries built by default
* Add support for building outside of source tree
* Update copyrights
* Fix typo in mtx.1 man page
* Clear outstanding UNIT ATTENTION state at start
-- Robert Nelson <robertn@the-nelsons.org> Tue, 19 Aug 2008 09:04:00 +0000
mtx (1.3.11-1) unstable; urgency=low
* new upstream version, closes: #425687, #425688
* don't let Makefile.in strip binaries, let dh_strip do it, closes: #437589
-- Bdale Garbee <bdale@gag.com> Tue, 15 Apr 2008 14:34:32 -0600
mtx (1.2.17rel-2) unstable; urgency=low
* update config.sub and config.guess in rules clean target using the
autotools-dev package, closes: #367467
-- Bdale Garbee <bdale@gag.com> Sat, 19 Aug 2006 18:44:54 -0600
mtx (1.2.17rel-1) unstable; urgency=low
* new upstream version
-- Bdale Garbee <bdale@gag.com> Wed, 28 Jun 2006 23:57:44 -0400
mtx (1.2.16rel-5) unstable; urgency=low
* add build-deps needed for GNU/kFreeBSD, closes: #367467
* update debhelper build dependency
-- Bdale Garbee <bdale@gag.com> Wed, 28 Jun 2006 23:42:00 -0400
mtx (1.2.16rel-4) unstable; urgency=medium
* revert SG_SCSI_DEFAULT_TIMEOUT to 5 minutes since at least the Sony
TLS-9000 takes more than a minute to load sometimes, closes: #229169
* remove 'previous' from command summary, since it's not actually
implemented in the program, closes: #230041
* include bash_completion file from Jon Middleton, closes: #227456
-- Bdale Garbee <bdale@gag.com> Sat, 14 Feb 2004 22:36:23 -0700
mtx (1.2.16rel-3) unstable; urgency=low
* apply patch from Torsten Werner <twerner@debian.org> that elminates
hard-coding of the value of HZ, closes: #224147
-- Bdale Garbee <bdale@gag.com> Tue, 16 Dec 2003 10:04:26 -0700
mtx (1.2.16rel-2) unstable; urgency=low
* apply patch from R.A.Owen <rao3@leicester.ac.uk> that fixes the "staggered"
output from the status command on some changers, closes: #129910
-- Bdale Garbee <bdale@gag.com> Tue, 9 Apr 2002 19:30:06 -0600
mtx (1.2.16rel-1) unstable; urgency=low
* new upstream version, bug-fixing release, reportedly fixes timeout
problem with some drives, closes: #113947
-- Bdale Garbee <bdale@gag.com> Mon, 4 Mar 2002 01:27:48 -0700
mtx (1.2.15-1) unstable; urgency=low
* new upstream source
* update standards version, rebuild rules file
* man pages all included now, mtx-changer and other pieces from contrib
provided as examples, though chg-mtx in the amanda package is probably
a better choice for use with amanda, closes: #113728
* apply diffs to correct "spelling errors" (actually hyphenation) in the
descriptions in the control file, closes: #125160
-- Bdale Garbee <bdale@gag.com> Sun, 30 Dec 2001 21:28:46 -0700
mtx (1.2.10-1) unstable; urgency=low
* new upstream source
-- Bdale Garbee <bdale@gag.com> Mon, 11 Dec 2000 17:34:13 -0700
mtx (1.0-10) frozen unstable; urgency=low
* deliver mtx.doc, lost when the package was made FHS compliant,
closes: #56276 Target frozen since including the documentation
is worthwhile for potato, and there is no added risk.
-- Bdale Garbee <bdale@gag.com> Wed, 9 Feb 2000 12:27:58 -0700
mtx (1.0-9) unstable; urgency=low
* fix postinst/postrm scripts to be executable again
-- Bdale Garbee <bdale@gag.com> Tue, 11 Jan 2000 23:00:17 -0700
mtx (1.0-8) unstable; urgency=low
* update to latest standards revision, add Build-Depends
* fix all the lintian errors that aren't intentional, override the two
permissions warnings since they're precisely what is needed
-- Bdale Garbee <bdale@gag.com> Fri, 7 Jan 2000 02:47:08 -0700
mtx (1.0-7) unstable; urgency=low
* grab a local copy of scsi_ioctl.h from the 2.2.10 kernel source tree. This
doesn't change often in any way we care about, and this is much simpler
than having to reference a live kernel tree somewhere...
* move to debhelper and CVS
-- Bdale Garbee <bdale@gag.com> Sun, 20 Jun 1999 10:42:39 -0600
mtx (1.0-6) unstable; urgency=low
* put mtx in group backup, setuid root, perms set so that only members of
group backup can run mtx. This makes mtx compatible with amanda.
-- Bdale Garbee <bdale@gag.com> Tue, 27 Jan 1998 15:06:13 -0700
mtx (1.0-5) unstable; urgency=low
* explicit include path to find <scsi/scsi_ioctl.h> in the
/usr/src/linux/include tree. closes bug 16877
-- Bdale Garbee <bdale@gag.com> Sun, 25 Jan 1998 23:02:46 -0700
mtx (1.0-4) unstable; urgency=low
* actually install the mtx.doc file that's referenced in the man page /o\
-- Bdale Garbee <bdale@gag.com> Sun, 21 Sep 1997 02:38:50 -0600
mtx (1.0-3) unstable; urgency=low
* libc6
-- Bdale Garbee <bdale@gag.com> Thu, 4 Sep 1997 02:56:39 -0600
mtx (1.0-2) unstable; urgency=low
* Add an 'mtx-changer' wrapper script from the amanda-users mailing list
to make this more useful with Amanda.
* Hack mtx-changer to report 6 slots instead of 4, since I have an HP
SureStore 12000e. Should make it configurable, someday.
-- Bdale Garbee <bdale@gag.com> Sun, 10 Aug 1997 03:50:42 -0600
mtx (1.0-1) unstable; urgency=low
* Initial Release.
-- Bdale Garbee <bdale@gag.com> Sun, 10 Aug 1997 03:05:18 -0600

1
mtx-1.3.12/debian/compat Normal file
View File

@@ -0,0 +1 @@
4

16
mtx-1.3.12/debian/control Normal file
View File

@@ -0,0 +1,16 @@
Source: mtx
Section: admin
Priority: extra
Maintainer: Robert Nelson <robertn@the-nelsons.org>
Build-Depends: debhelper (>= 4), libcam-dev [kfreebsd-i386 kfreebsd-amd64], autotools-dev
Standards-Version: 3.7.3
Package: mtx
Architecture: any
Depends: ${shlibs:Depends}
Description: controls tape autochangers
MTX can be used to manipulate tape auto-changers, also known as "jukeboxes",
such that backup software can make use of the multiple tape capabilities of
the auto-changer. In particular, this is necessary glue for using a backup
system like Amanda with a DDS auto-changer like the HP Surestore 12000e.

View File

@@ -0,0 +1,15 @@
This package was debianized by Bdale Garbee bdale@gag.com on
Sun, 10 Aug 1997 03:05:18 -0600.
mtx was downloaded from http://mtx.sourceforge.net/
Copyright:
Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
Changes copyright 2000, 2001 Eric Green <eric@badtux.org>
Changes copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
GPL, version 2
On Debian GNU/Linux systems, the complete text of the GNU General
Public License can be found in `/usr/share/common-licenses/GPL'.

2
mtx-1.3.12/debian/dirs Normal file
View File

@@ -0,0 +1,2 @@
etc/bash_completion.d
usr/sbin

69
mtx-1.3.12/debian/rules Executable file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/make -f
# Sample debian/rules that uses debhelper.
# GNU copyright 1997 to 1999 by Joey Hess.
export DH_VERBOSE=1
configure: configure-stamp
configure-stamp:
dh_testdir
./configure --prefix=/usr
touch configure-stamp
build: build-stamp
build-stamp: configure-stamp
dh_testdir
$(MAKE)
touch build-stamp
clean:
dh_testdir
dh_testroot
rm -f build-stamp configure-stamp
[ ! -f Makefile ] || $(MAKE) distclean
-test -r /usr/share/misc/config.sub && \
cp -f /usr/share/misc/config.sub config.sub
-test -r /usr/share/misc/config.guess && \
cp -f /usr/share/misc/config.guess config.guess
dh_clean
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
make install bindir=`pwd`/debian/mtx/bin prefix=`pwd`/debian/mtx/usr \
mandir=`pwd`/debian/mtx/usr/share/man
install -o root -g root -m 0644 debian/bash_completion \
debian/mtx/etc/bash_completion.d/mtx
# Build architecture-independent files here.
binary-indep: build install
# Build architecture-dependent files here.
binary-arch: build install
dh_testdir
dh_testroot
dh_installdocs
dh_installexamples contrib/*
dh_installmenu
dh_installcron
# dh_installman
dh_installinfo
dh_installchangelogs CHANGES
dh_link
dh_strip
dh_compress
dh_fixperms
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install configure

25
mtx-1.3.12/du/defs.h Normal file
View File

@@ -0,0 +1,25 @@
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
typedef int DEVICE_TYPE;
#ifdef __osf__
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <io/common/iotypes.h>
#else /* must be ultrix */
#include <sys/devio.h>
#endif
#include <io/cam/cam.h>
#include <io/cam/uagt.h>
#include <io/cam/dec_cam.h>
#include <io/cam/scsi_all.h>
#define DEV_CAM "/dev/cam" /* CAM device path */
#define DIGITAL_UNIX

214
mtx-1.3.12/du/scsi.c Normal file
View File

@@ -0,0 +1,214 @@
/* SCSI.C - Digital Unix-specific SCSI routines.
**
** TECSys Development, Inc., April 1998
**
** This module began life as a part of XMCD, an X-windows CD player
** program for many platforms. No real functionality from the original XMCD
** is present in this module, but in the interest of making certain that
** proper credit is given where it may be due, the copyrights and inclusions
** from the XMCD module OS_DEC.C are included below.
**
** The portions of coding in this module ascribable to TECSys Development
** are hereby also released under the terms and conditions of version 2
** of the GNU General Public License as described below....
*/
/*
* libdi - scsipt SCSI Device Interface Library
*
* Copyright (C) 1993-1997 Ti Kan
* E-mail: ti@amb.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Digital UNIX (OSF/1) and Ultrix support
*
* Contributing author: Matt Thomas
* E-Mail: thomas@lkg.dec.com
*
* This software fragment contains code that interfaces the
* application to the Digital UNIX and Ultrix operating systems.
* The term Digital, Ultrix and OSF/1 are used here for identification
* purposes only.
*/
static int bus = -1,
target = -1,
lun = -1;
static int SCSI_OpenDevice(char *DeviceName)
{
int fd;
struct stat stbuf;
int saverr;
/* Check for validity of device node */
if (stat(DeviceName, &stbuf) < 0)
{
FatalError("cannot stat SCSI device '%s' - %m\n", DeviceName);
}
if (!S_ISCHR(stbuf.st_mode))
{
FatalError("device '%s': not appropriate device type - %m\n", DeviceName);
}
if ((fd = open(DeviceName, O_RDONLY | O_NDELAY, 0)) >= 0)
{
struct devget devget;
if (ioctl(fd, DEVIOCGET, &devget) >= 0)
{
#ifdef __osf__
lun = devget.slave_num % 8;
devget.slave_num /= 8;
#else
lun = 0;
#endif
target = devget.slave_num % 8;
devget.slave_num /= 8;
bus = devget.slave_num % 8;
(void) close(fd);
if ((fd = open(DEV_CAM, O_RDWR, 0)) >= 0 ||
(fd = open(DEV_CAM, O_RDONLY, 0)) >= 0)
{
return (fd);
}
fd = bus = target = lun = -1;
FatalError("error %d opening SCSI device '%s' - %m\n",
errno, DEV_CAM);
}
else
{
(void) close(fd);
fd = bus = target = lun = -1;
FatalError("error %d on DEVIOCGET ioctl for '%s' - %m\n",
errno, DeviceName);
}
}
else
{
saverr = errno;
fd = bus = target = lun = -1;
FatalError("cannot open SCSI device '%s', error %d - %m\n",
DeviceName, saverr);
}
fd = bus = target = lun = -1;
return -1;
}
static void SCSI_CloseDevice(char *DeviceName, int DeviceFD)
{
(void) close(DeviceFD);
bus = target = lun = -1;
}
static int SCSI_ExecuteCommand(int DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
UAGT_CAM_CCB uagt;
CCB_SCSIIO ccb;
if (DeviceFD < 0)
return -1;
(void) memset(&uagt, 0, sizeof(uagt));
(void) memset(&ccb, 0, sizeof(ccb));
/* Setup the user agent ccb */
uagt.uagt_ccb = (CCB_HEADER *) &ccb;
uagt.uagt_ccblen = sizeof(CCB_SCSIIO);
/* Setup the scsi ccb */
(void) memcpy((unsigned char *) ccb.cam_cdb_io.cam_cdb_bytes,
CDB, CDB_Length);
ccb.cam_cdb_len = CDB_Length;
ccb.cam_ch.my_addr = (CCB_HEADER *) &ccb;
ccb.cam_ch.cam_ccb_len = sizeof(CCB_SCSIIO);
ccb.cam_ch.cam_func_code = XPT_SCSI_IO;
if (DataBuffer != NULL && DataBufferLength > 0)
{
ccb.cam_ch.cam_flags |= (Direction == Input) ?
CAM_DIR_IN : CAM_DIR_OUT;
uagt.uagt_buffer = (u_char *) DataBuffer;
uagt.uagt_buflen = DataBufferLength;
}
else
ccb.cam_ch.cam_flags |= CAM_DIR_NONE;
ccb.cam_ch.cam_flags |= CAM_DIS_AUTOSENSE;
ccb.cam_data_ptr = uagt.uagt_buffer;
ccb.cam_dxfer_len = uagt.uagt_buflen;
ccb.cam_timeout = 300; /* Timeout set to 5 minutes */
ccb.cam_sense_ptr = (u_char *) RequestSense;
ccb.cam_sense_len = sizeof(RequestSense_T);
ccb.cam_ch.cam_path_id = bus;
ccb.cam_ch.cam_target_id = target;
ccb.cam_ch.cam_target_lun = lun;
if (ioctl(DeviceFD, UAGT_CAM_IO, (caddr_t) &uagt) < 0)
{
return -1;
}
/* Check return status */
if ((ccb.cam_ch.cam_status & CAM_STATUS_MASK) != CAM_REQ_CMP)
{
if (ccb.cam_ch.cam_status & CAM_SIM_QFRZN)
{
(void) memset(&ccb, 0, sizeof(ccb));
(void) memset(&uagt, 0, sizeof(uagt));
/* Setup the user agent ccb */
uagt.uagt_ccb = (CCB_HEADER *) &ccb;
uagt.uagt_ccblen = sizeof(CCB_RELSIM);
/* Setup the scsi ccb */
ccb.cam_ch.my_addr = (struct ccb_header *) &ccb;
ccb.cam_ch.cam_ccb_len = sizeof(CCB_RELSIM);
ccb.cam_ch.cam_func_code = XPT_REL_SIMQ;
ccb.cam_ch.cam_path_id = bus;
ccb.cam_ch.cam_target_id = target;
ccb.cam_ch.cam_target_lun = lun;
if (ioctl(DeviceFD, UAGT_CAM_IO, (caddr_t) &uagt) < 0)
return -1;
}
printf( "mtx: %s:\n%s=0x%x %s=0x%x\n",
"SCSI command fault",
"Opcode",
CDB[0],
"Status",
ccb.cam_scsi_status);
return -1;
}
return 0;
}

253
mtx-1.3.12/install-sh Executable file
View File

@@ -0,0 +1,253 @@
#!/bin/sh
# $Date: 2001-06-05 10:10:15 -0700 (Tue, 05 Jun 2001) $
# $Revision: 2 $
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

90
mtx-1.3.12/loaderinfo.1 Normal file
View File

@@ -0,0 +1,90 @@
.\" tapeinfo.1 Document copyright 2000 Eric Lee Green
.\" Program Copyright 2000 Eric Lee Green <eric@badtux.org>
.\" Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
.\"
.\" This is free documentation; 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.
.\"
.\" The GNU General Public License's references to "object code"
.\" and "executables" are to be interpreted as the output of any
.\" document formatting or typesetting system, including
.\" intermediate and printed output.
.\"
.\" This manual 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 manual; if not, write to the Free
.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
.\" USA.
.\"
.TH LOADERINFO 1 LOADERINFO1.0
.SH NAME
loaderinfo \- report SCSI tape device info
.SH SYNOPSIS
loaderinfo -f <scsi-generic-device>
.SH DESCRIPTION
The
.B loaderinfo
command reads various information from SCSI tape loaders. Its intended
use is for high-level programs that are trying to decide what the
capabilities of a device are.
.P
The following are printed:
.TP 10
.B Element Address Assignment Page:
This tells how many elements are in the loader, and what their raw
hardware addresses are.
.TP 10
.B Transport Geometry Descriptor Page:
Will display whether media is invertible or not (usable with some
optical jukeboxes for detirmining whether to "flip" media after writing
to the first side).
.TP 10
.B Device Capabilities Page
Currently will only display whether we can transfer between slots (i.e.
whether 'mtx transfer' works).
.TP 10
.B Inquiry Page
Aside from the normal inquiry info, will also print out whether we have
a bar code reader (for loaders that support the Exabyte extension for
reporting presense of said reader).
.SH OPTIONS
The first argument, given following
.B -f
, is the SCSI generic device corresponding to your tape loader.
Consult your operating system's documentation for more information (for
example, under Linux these are generally start at /dev/sg0
under FreeBSD these start at /dev/pass0).
.P
Under FreeBSD, 'camcontrol devlist' will tell you what SCSI devices you
have, along with which 'pass' device controls them. Under Linux,
"cat /proc/scsi/scsi" will tell you what SCSI devices you have. Under
Solaris 8,
.B find /devices -name '*changer*'
will display the device names for your attached changers. Make sure
to configure your 'sgen' driver first.
.SH BUGS AND LIMITATIONS
.P
This program has only been tested on Linux with a limited number of
loaders (Ecrix Autopack, Exabyte 220).
.P
.SH AVAILABILITY
.B loaderinfo
is currently being maintained by Robert Nelson <robertnelson@users.sourceforge.net>
as part of the 'mtx' suite of programs. The 'mtx' home page is
http://mtx.sourceforge.net and the actual code is currently available there and via
SVN from http://sourceforge.net/projects/mtx.
.SH SEE ALSO
.BR mt (1), tapeinfo (1), mtx (1)

510
mtx-1.3.12/loaderinfo.c Normal file
View File

@@ -0,0 +1,510 @@
/* Copyright 2000 Enhanced Software Technologies Inc.
* Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
* Released under terms of the GNU General Public License as
* required by the license on 'mtxl.c'.
*/
/*
* $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
* $Revision: 193 $
*/
/* What this does: Basically dumps out contents of:
* Mode Sense: Element Address Assignment Page (0x1d)
* 1Eh (Transport Geometry Parameters) has a bit which indicates is
* a robot is capable of rotating the media. It`s the
* `Rotate` bit, byte 2, bit 1.
* Device Capabilities page (0x1f)
* Inquiry -- prints full inquiry info.
* DeviceType:
* Manufacturer:
* ProdID:
* ProdRevision:
* If there is a byte 55, we use the Exabyte extension to
* print out whether we have a bar code reader or not. This is
* bit 0 of byte 55.
*
* Next, we request element status on the drives. We do not
* request volume tags though. If Exabyte
* extensions are supported, we report the following information for
* each drive:
*
* Drive number
* EXCEPT (with ASC and ASCQ), if there is a problem.
* SCSI address and LUN
* Tape drive Serial number
*
*/
#include <stdio.h>
#include "mtx.h"
#include "mtxl.h"
DEVICE_TYPE MediumChangerFD; /* historic purposes... */
char *argv0;
/* A table for printing out the peripheral device type as ASCII. */
static char *PeripheralDeviceType[32] =
{
"Disk Drive",
"Tape Drive",
"Printer",
"Processor",
"Write-once",
"CD-ROM",
"Scanner",
"Optical",
"Medium Changer",
"Communications",
"ASC IT8",
"ASC IT8",
"RAID Array",
"Enclosure Services",
"OCR/W",
"Bridging Expander", /* 0x10 */
"Reserved", /* 0x11 */
"Reserved", /* 0x12 */
"Reserved", /* 0x13 */
"Reserved", /* 0x14 */
"Reserved", /* 0x15 */
"Reserved", /* 0x16 */
"Reserved", /* 0x17 */
"Reserved", /* 0x18 */
"Reserved", /* 0x19 */
"Reserved", /* 0x1a */
"Reserved", /* 0x1b */
"Reserved", /* 0x1c */
"Reserved", /* 0x1d */
"Reserved", /* 0x1e */
"Unknown" /* 0x1f */
};
/* okay, now for the structure of an Element Address Assignment Page: */
typedef struct EAAP
{
unsigned char Page_Code;
unsigned char Parameter_Length;
unsigned char MediumTransportElementAddress[2];
unsigned char NumMediumTransportElements[2];
unsigned char FirstStorageElementAdddress[2];
unsigned char NumStorageElements[2];
unsigned char FirstImportExportElementAddress[2];
unsigned char NumImportExportElements[2];
unsigned char FirstDataTransferElementAddress[2];
unsigned char NumDataTransferElements[2];
unsigned char Reserved[2];
} EAAP_Type;
/* okay, now for the structure of a transport geometry
* descriptor page:
*/
typedef struct TGDP
{
unsigned char Page_Code;
unsigned char ParameterLength;
unsigned char Rotate;
unsigned char ElementNumber; /* we don't care about this... */
} TGDP_Type;
/* Structure of the Device Capabilities Page: */
typedef struct DCP
{
unsigned char Page_Code;
unsigned char ParameterLength;
unsigned char CanStore; /* bits about whether elements can store carts */
unsigned char SMC2_Caps;
unsigned char MT_Transfer; /* bits about whether mt->xx transfers work. */
unsigned char ST_Transfer; /* bits about whether st->xx transfers work. */
unsigned char IE_Transfer; /* bits about whether id->xx transfers work. */
unsigned char DT_Transfer; /* bits about whether DT->xx transfers work. */
unsigned char Reserved[4]; /* more reserved data */
unsigned char MT_Exchange; /* bits about whether mt->xx exchanges work. */
unsigned char ST_Exchange; /* bits about whether st->xx exchanges work. */
unsigned char IE_Exchange; /* bits about whether id->xx exchanges work. */
unsigned char DT_Exchange; /* bits about whether DT->xx exchanges work. */
unsigned char Reserved2[4]; /* more reserved data */
} DCP_Type;
#define MT_BIT 0x01
#define ST_BIT 0x02
#define IE_BIT 0x04
#define DT_BIT 0x08
/* Okay, now for the inquiry information: */
static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
{
RequestSense_T RequestSense;
Inquiry_T *Inquiry;
int i;
Inquiry = RequestInquiry(MediumChangerFD,&RequestSense);
if (Inquiry == NULL)
{
PrintRequestSense(&RequestSense);
FatalError("INQUIRY Command Failed\n");
}
printf("Product Type: %s\n",PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
printf("Vendor ID: '");
for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
printf("%c", Inquiry->VendorIdentification[i]);
printf("'\nProduct ID: '");
for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
printf("%c", Inquiry->ProductIdentification[i]);
printf("'\nRevision: '");
for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
printf("%c", Inquiry->ProductRevisionLevel[i]);
printf("'\n");
if (Inquiry->MChngr)
{
/* check the attached-media-changer bit... */
printf("Attached Changer: Yes\n");
}
else
{
printf("Attached Changer: No\n");
}
/* Now see if we have a bar code flag: */
if (Inquiry->AdditionalLength > 50)
{
/* see if we have 56 bytes: */
if (Inquiry->VendorFlags & 1)
{
printf("Bar Code Reader: Yes\n");
}
else
{
printf("Bar Code Reader: No\n");
}
}
free(Inquiry); /* well, we're about to exit, but ... */
}
/*********** MODE SENSE *******************/
/* We need 3 different mode sense pages. This is a generic
* routine for obtaining mode sense pages.
*/
static unsigned char
*mode_sense(DEVICE_TYPE fd, char pagenum, int alloc_len, RequestSense_T *RequestSense)
{
CDB_T CDB;
unsigned char *input_buffer; /*the input buffer -- has junk prepended to
* actual sense page.
*/
unsigned char *tmp;
unsigned char *retval; /* the return value. */
int i,pagelen;
if (alloc_len > 255)
{
FatalError("mode_sense(6) can only read up to 255 characters!\n");
}
input_buffer = (unsigned char *)xzmalloc(256); /* overdo it, eh? */
/* clear the sense buffer: */
slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
/* returns an array of bytes in the page, or, if not possible, NULL. */
CDB[0] = 0x1a; /* Mode Sense(6) */
CDB[1] = 0x08;
CDB[2] = pagenum; /* the page to read. */
CDB[3] = 0;
CDB[4] = 255; /* allocation length. This does max of 256 bytes! */
CDB[5] = 0;
if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
input_buffer, 255, RequestSense) != 0)
{
#ifdef DEBUG_MODE_SENSE
fprintf(stderr,"Could not execute mode sense...\n");
fflush(stderr);
#endif
return NULL; /* sorry, couldn't do it. */
}
/* First skip past any header.... */
tmp = input_buffer + 4 + input_buffer[3];
/* now find out real length of page... */
pagelen=tmp[1] + 2;
retval = xmalloc(pagelen);
/* and copy our data to the new page. */
for (i = 0; i < pagelen; i++)
{
retval[i] = tmp[i];
}
/* okay, free our input buffer: */
free(input_buffer);
return retval;
}
/* Report the Element Address Assignment Page */
static void ReportEAAP(DEVICE_TYPE MediumChangerFD)
{
EAAP_Type *EAAP;
RequestSense_T RequestSense;
EAAP = (EAAP_Type *)mode_sense(MediumChangerFD, 0x1d, sizeof(EAAP_Type), &RequestSense);
if (EAAP == NULL)
{
PrintRequestSense(&RequestSense);
printf("EAAP: No\n");
return;
}
/* we did get an EAAP, so do our thing: */
printf("EAAP: Yes\n");
printf("Number of Medium Transport Elements: %d\n", ( ((unsigned int)EAAP->NumMediumTransportElements[0]<<8) + (unsigned int)EAAP->NumMediumTransportElements[1]));
printf("Number of Storage Elements: %d\n", ( ((unsigned int)EAAP->NumStorageElements[0]<<8) + (unsigned int)EAAP->NumStorageElements[1]));
printf("Number of Import/Export Elements: %d\n", ( ((unsigned int)EAAP->NumImportExportElements[0]<<8) + (unsigned int)EAAP->NumImportExportElements[1]));
printf("Number of Data Transfer Elements: %d\n", ( ((unsigned int)EAAP->NumDataTransferElements[0]<<8) + (unsigned int)EAAP->NumDataTransferElements[1]));
free(EAAP);
}
/* See if we can get some invert information: */
static void Report_TGDP(DEVICE_TYPE MediumChangerFD)
{
TGDP_Type *result;
RequestSense_T RequestSense;
result=(TGDP_Type *)mode_sense(MediumChangerFD,0x1e,255,&RequestSense);
if (!result)
{
printf("Transport Geometry Descriptor Page: No\n");
return;
}
printf("Transport Geometry Descriptor Page: Yes\n");
/* Now print out the invert bit: */
if ( result->Rotate & 1 )
{
printf("Invertable: Yes\n");
}
else
{
printf("Invertable: No\n");
}
free(result);
}
/* Okay, let's get the Device Capabilities Page. We don't care
* about much here, just whether 'mtx transfer' will work (i.e.,
* ST->ST).
*/
void TransferExchangeTargets(unsigned char ucValue, char *szPrefix)
{
if (ucValue & DT_BIT)
{
printf("%sData Transfer", szPrefix);
}
if (ucValue & IE_BIT)
{
printf("%s%sImport/Export", ucValue > (IE_BIT | (IE_BIT - 1)) ? ", " : "", szPrefix);
}
if (ucValue & ST_BIT)
{
printf("%s%sStorage", ucValue > (ST_BIT | (ST_BIT - 1)) ? ", " : "", szPrefix);
}
if (ucValue & MT_BIT)
{
printf("%s%sMedium Transfer", ucValue > (MT_BIT | (MT_BIT - 1)) ? ", " : "", szPrefix);
}
}
static void Report_DCP(DEVICE_TYPE MediumChangerFD)
{
DCP_Type *result;
RequestSense_T RequestSense;
/* Get the page. */
result=(DCP_Type *)mode_sense(MediumChangerFD,0x1f,sizeof(DCP_Type),&RequestSense);
if (!result)
{
printf("Device Configuration Page: No\n");
return;
}
printf("Device Configuration Page: Yes\n");
printf("Storage: ");
if (result->CanStore & DT_BIT)
{
printf("Data Transfer");
}
if (result->CanStore & IE_BIT)
{
printf("%sImport/Export", result->CanStore > (IE_BIT | (IE_BIT - 1)) ? ", " : "");
}
if (result->CanStore & ST_BIT)
{
printf("%sStorage", result->CanStore > (ST_BIT | (ST_BIT - 1)) ? ", " : "");
}
if (result->CanStore & MT_BIT)
{
printf("%sMedium Transfer", result->CanStore > (MT_BIT | (MT_BIT - 1)) ? ", " : "");
}
printf("\n");
printf("SCSI Media Changer (rev 2): ");
if (result->SMC2_Caps & 0x01)
{
printf("Yes\n");
printf("Volume Tag Reader Present: %s\n", result->SMC2_Caps & 0x02 ? "Yes" : "No");
printf("Auto-Clean Enabled: %s\n", result->SMC2_Caps & 0x04 ? "Yes" : "No");
}
else
{
printf("No\n");
}
printf("Transfer Medium Transport: ");
if ((result->MT_Transfer & 0x0F) != 0)
{
TransferExchangeTargets(result->MT_Transfer, "->");
}
else
{
printf("None");
}
printf("\nTransfer Storage: ");
if ((result->ST_Transfer & 0x0F) != 0)
{
TransferExchangeTargets(result->ST_Transfer, "->");
}
else
{
printf("None");
}
printf("\nTransfer Import/Export: ");
if ((result->IE_Transfer & 0x0F) != 0)
{
TransferExchangeTargets(result->IE_Transfer, "->");
}
else
{
printf("None");
}
printf("\nTransfer Data Transfer: ");
if ((result->DT_Transfer & 0x0F) != 0)
{
TransferExchangeTargets(result->DT_Transfer, "->");
}
else
{
printf("None");
}
printf("\nExchange Medium Transport: ");
if ((result->MT_Exchange & 0x0F) != 0)
{
TransferExchangeTargets(result->MT_Exchange, "<>");
}
else
{
printf("None");
}
printf("\nExchange Storage: ");
if ((result->ST_Exchange & 0x0F) != 0)
{
TransferExchangeTargets(result->ST_Exchange, "<>");
}
else
{
printf("None");
}
printf("\nExchange Import/Export: ");
if ((result->IE_Exchange & 0x0F) != 0)
{
TransferExchangeTargets(result->IE_Exchange, "<>");
}
else
{
printf("None");
}
printf("\nExchange Data Transfer: ");
if ((result->DT_Exchange & 0x0F) != 0)
{
TransferExchangeTargets(result->DT_Exchange, "<>");
}
else
{
printf("None");
}
printf("\n");
free(result);
}
void usage(void)
{
FatalError("Usage: loaderinfo -f <generic-device>\n");
}
/* we only have one argument: "-f <device>". */
int main(int argc, char **argv)
{
DEVICE_TYPE fd;
char *filename;
argv0=argv[0];
if (argc != 3)
{
fprintf(stderr,"argc=%d",argc);
usage();
}
if (strcmp(argv[1],"-f")!=0)
{
usage();
}
filename=argv[2];
fd=SCSI_OpenDevice(filename);
/* Now to call the various routines: */
ReportInquiry(fd);
ReportEAAP(fd);
Report_TGDP(fd);
Report_DCP(fd);
exit(0);
}

19
mtx-1.3.12/makedist Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/sh
# note -- this assumes 'bash' shell, GNU tar, 'gzip'.
# pass a version number e.g. 1.4.3 as 1st parameter...
ME=`pwd`
# okay, now to create a spec file w/the proper version number:
sed -e "1,\$s/@@VERSION@@/${1}/g" <mtx.spec.in >mtx.spec
cd ..
if [ ! -s mtx-${1} ]
then
ln -s "${ME}" "mtx-${1}"
fi
tar --exclude CVS --exclude .svn -czvhf mtx-${1}.tar.gz mtx-${1}

124
mtx-1.3.12/mam2debug.c Normal file
View File

@@ -0,0 +1,124 @@
/* Mammoth 2 Debug Buffer Dumper
Copyright 2000 Enhanced Software Technologies Inc.
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
Written by Eric Lee Green <eric@badtux.org>
Released under the terms of the GNU General Public License v2 or
above.
This is an example of how to use the mtx library file 'mtxl.c' to
do a special-purpose task -- dump the Mammoth2 debug buffer, in this case.
Note that this debug buffer is 1M-4M in size, thus may overwhelm the
SCSI generic subsystem on some supported platforms...
syntax:
mam2debug generic-filename output-filename.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "mtx.h"
#include "mtxl.h"
/* This is a TOTALLY UNDOCUMENTED feature to read the debug data buffer
* in an Exabyte Mammoth II and dump it to a file:
*/
static RequestSense_T *DumpM2DebugBuff(DEVICE_TYPE MediumChangerFD, int outfile)
{
RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
CDB_T CDB;
unsigned char *databuffer;
unsigned char buff_descriptor[4];
int numbytes;
int testbytes;
CDB[0]=0x3c; /* command. */
CDB[1]=0x03; /* mode - read buff_descriptor! */
CDB[2]=0x01; /* page. */
CDB[3]=0; /* offset. */
CDB[4]=0;
CDB[5]=0;
CDB[6]=0; /* length. */
CDB[7]=0;
CDB[8]=4; /* the descriptor is 4 long. */
CDB[9]=0;
if ((testbytes=SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10,
buff_descriptor, 4, RequestSense)) != 0){
fprintf(stderr,"mam2debug: could not read buff_descriptor. [%d]\n",testbytes);
return RequestSense; /* couldn't do it. */
}
/* okay, read numbytes: */
numbytes=(buff_descriptor[1]<<16) + (buff_descriptor[2]<<8) + buff_descriptor[3];
databuffer=(unsigned char *) xmalloc(numbytes+1000000); /* see if this helps :-(. */
CDB[6]=buff_descriptor[1];
CDB[7]=buff_descriptor[2];
CDB[8]=buff_descriptor[3];
CDB[1]=0x02; /* mode -- read buffer! */
if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10,
databuffer, numbytes, RequestSense) != 0){
fprintf(stderr,"mam2debug: could not read buffer.\n");
free(databuffer);
return RequestSense; /* couldn't do it. */
}
write(outfile,databuffer,numbytes);
close(outfile);
free(databuffer);
free(RequestSense);
return NULL; /* okay! */
}
static void usage(void) {
fprintf(stderr,"Usage: mam2debug scsi-generic-file output-file-name\n");
exit(1);
}
/* Now for the actual main() routine: */
int main(int argc,char** argv) {
DEVICE_TYPE changer_fd;
static RequestSense_T *result;
int outfile;
if (argc != 3) {
usage();
}
changer_fd=SCSI_OpenDevice(argv[1]);
if (changer_fd <= 0) {
fprintf(stderr,"Could not open input device\n");
usage();
}
outfile=open(argv[2],O_CREAT|O_WRONLY);
if (outfile <=0) {
fprintf(stderr,"Could not open output file\n");
usage();
}
result=DumpM2DebugBuff(changer_fd, outfile);
if (result) {
PrintRequestSense(result);
exit(1);
}
exit(0);
}

124
mtx-1.3.12/mam2debug2.c Normal file
View File

@@ -0,0 +1,124 @@
/* Mammoth 2 Debug Buffer Dumper
Copyright 2000 Enhanced Software Technologies Inc.
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
Written by Eric Lee Green <eric@badtux.org>
Released under the terms of the GNU General Public License v2 or
above.
This is an example of how to use the mtx library file 'mtxl.c' to
do a special-purpose task -- dump the Mammoth2 debug buffer, in this case.
Note that this debug buffer is 1M-4M in size, thus may overwhelm the
SCSI generic subsystem on some supported platforms...
syntax:
mam2debug generic-filename output-filename.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "mtx.h"
#include "mtxl.h"
/* This is a TOTALLY UNDOCUMENTED feature to read the debug data buffer
* in an Exabyte Mammoth II and dump it to a file:
*/
static RequestSense_T *DumpM2DebugBuff(DEVICE_TYPE MediumChangerFD, int outfile)
{
RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
CDB_T CDB;
unsigned char *databuffer;
unsigned char buff_descriptor[4];
int numbytes;
int testbytes;
CDB[0]=0x3c; /* command. */
CDB[1]=0x03; /* mode - read buff_descriptor! */
CDB[2]=0x00; /* page -- data. */
CDB[3]=0; /* offset. */
CDB[4]=0;
CDB[5]=0;
CDB[6]=0; /* length. */
CDB[7]=0;
CDB[8]=4; /* the descriptor is 4 long. */
CDB[9]=0;
if ((testbytes=SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10,
buff_descriptor, 4, RequestSense)) != 0){
fprintf(stderr,"mam2debug: could not read buff_descriptor. [%d]\n",testbytes);
return RequestSense; /* couldn't do it. */
}
/* okay, read numbytes: */
numbytes=(buff_descriptor[1]<<16) + (buff_descriptor[2]<<8) + buff_descriptor[3];
databuffer=(unsigned char *) xmalloc(numbytes+1000000); /* see if this helps :-(. */
CDB[6]=buff_descriptor[1];
CDB[7]=buff_descriptor[2];
CDB[8]=buff_descriptor[3];
CDB[1]=0x02; /* mode -- read buffer! */
if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10,
databuffer, numbytes, RequestSense) != 0){
fprintf(stderr,"mam2debug: could not read buffer.\n");
free(databuffer);
return RequestSense; /* couldn't do it. */
}
write(outfile,databuffer,numbytes);
close(outfile);
free(databuffer);
free(RequestSense);
return NULL; /* okay! */
}
static void usage(void) {
fprintf(stderr,"Usage: mam2debug scsi-generic-file output-file-name\n");
exit(1);
}
/* Now for the actual main() routine: */
int main(int argc,char** argv) {
DEVICE_TYPE changer_fd;
static RequestSense_T *result;
int outfile;
if (argc != 3) {
usage();
}
changer_fd=SCSI_OpenDevice(argv[1]);
if (changer_fd <= 0) {
fprintf(stderr,"Could not open input device\n");
usage();
}
outfile=open(argv[2],O_CREAT|O_WRONLY);
if (outfile <=0) {
fprintf(stderr,"Could not open output file\n");
usage();
}
result=DumpM2DebugBuff(changer_fd, outfile);
if (result) {
PrintRequestSense(result);
exit(1);
}
exit(0);
}

37
mtx-1.3.12/msvc/config.h Normal file
View File

@@ -0,0 +1,37 @@
/* config.h. Generated by configure. */
/* Copyright 2001 Enhanced Software Technologies Inc.
* Released under GNU General Public License V2 or Above
* See http://www.gnu.org for more information about the terms of
* the GNU General Public License.
* $Date: 2007-01-28 19:23:33 -0800 (Sun, 28 Jan 2007) $
* $Revision: 125 $
*/
#ifndef CONFIG_H
#define CONFIG_H 1
/* autoconf changes these. */
#define HAVE_STRING_H 1
#define HAVE_UNISTD_H 0
#define HAVE_STDLIB_H 1
#define HAVE_STDARG_H 1
#define HAVE_SCSI_SCSI_H 0
#define HAVE_SCSI_SCSI_IOCTL_H 0
#define HAVE_SCSI_SG_H 0
#define HAVE_SYS_GSCDDS_H 0
#define HAVE_CAMLIB_H 0
#define HAVE_SYS_SCSI_IMPL_USCSI_H 0
#define HAVE_SYS_SCSI_CTL_H 0
#define HAVE_DSLIB_H 0
#define HAVE_DU_DEFS_H 0
#define HAVE_SYS_STAT_H 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_FCNTL_H 1
#define HAVE_SYS_IOCTL_H 0
#define HAVE_SYS_MTIO_H 0
#define HAVE_DDK_NTDDSCSI_H 0
#define WORDS_BIGENDIAN 0
#endif

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="loaderinfo"
ProjectGUID="{13712060-F1FC-4498-97A7-5DA5A38F04DD}"
RootNamespace="loaderinfo"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=".."
PreprocessorDefinitions="WIN32;_DEBUG;DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
DisableSpecificWarnings="4214"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=".."
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
DisableSpecificWarnings="4214"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\loaderinfo.c"
>
</File>
<File
RelativePath="..\..\mtxl.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

50
mtx-1.3.12/msvc/mtx.sln Normal file
View File

@@ -0,0 +1,50 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "loaderinfo", "loaderinfo\loaderinfo.vcproj", "{13712060-F1FC-4498-97A7-5DA5A38F04DD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mtx", "mtx\mtx.vcproj", "{7DD926F5-30EA-47D4-B67B-E32C0E221440}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scsitape", "scsitape\scsitape.vcproj", "{D19E95BD-87C6-4C91-A208-FB8338580F75}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tapeinfo", "tapeinfo\tapeinfo.vcproj", "{A1C8D34F-66EC-4F74-8261-C96B97727218}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsmhack", "nsmhack\nsmhack.vcproj", "{1B3C0A23-4021-4928-92FA-76743B7F7F76}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scsieject", "scsieject\scsieject.vcproj", "{E3B77A78-FD72-4AD7-933A-0503FB21551D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{13712060-F1FC-4498-97A7-5DA5A38F04DD}.Debug|Win32.ActiveCfg = Debug|Win32
{13712060-F1FC-4498-97A7-5DA5A38F04DD}.Debug|Win32.Build.0 = Debug|Win32
{13712060-F1FC-4498-97A7-5DA5A38F04DD}.Release|Win32.ActiveCfg = Release|Win32
{13712060-F1FC-4498-97A7-5DA5A38F04DD}.Release|Win32.Build.0 = Release|Win32
{7DD926F5-30EA-47D4-B67B-E32C0E221440}.Debug|Win32.ActiveCfg = Debug|Win32
{7DD926F5-30EA-47D4-B67B-E32C0E221440}.Debug|Win32.Build.0 = Debug|Win32
{7DD926F5-30EA-47D4-B67B-E32C0E221440}.Release|Win32.ActiveCfg = Release|Win32
{7DD926F5-30EA-47D4-B67B-E32C0E221440}.Release|Win32.Build.0 = Release|Win32
{D19E95BD-87C6-4C91-A208-FB8338580F75}.Debug|Win32.ActiveCfg = Debug|Win32
{D19E95BD-87C6-4C91-A208-FB8338580F75}.Debug|Win32.Build.0 = Debug|Win32
{D19E95BD-87C6-4C91-A208-FB8338580F75}.Release|Win32.ActiveCfg = Release|Win32
{D19E95BD-87C6-4C91-A208-FB8338580F75}.Release|Win32.Build.0 = Release|Win32
{A1C8D34F-66EC-4F74-8261-C96B97727218}.Debug|Win32.ActiveCfg = Debug|Win32
{A1C8D34F-66EC-4F74-8261-C96B97727218}.Debug|Win32.Build.0 = Debug|Win32
{A1C8D34F-66EC-4F74-8261-C96B97727218}.Release|Win32.ActiveCfg = Release|Win32
{A1C8D34F-66EC-4F74-8261-C96B97727218}.Release|Win32.Build.0 = Release|Win32
{1B3C0A23-4021-4928-92FA-76743B7F7F76}.Debug|Win32.ActiveCfg = Debug|Win32
{1B3C0A23-4021-4928-92FA-76743B7F7F76}.Debug|Win32.Build.0 = Debug|Win32
{1B3C0A23-4021-4928-92FA-76743B7F7F76}.Release|Win32.ActiveCfg = Release|Win32
{1B3C0A23-4021-4928-92FA-76743B7F7F76}.Release|Win32.Build.0 = Release|Win32
{E3B77A78-FD72-4AD7-933A-0503FB21551D}.Debug|Win32.ActiveCfg = Debug|Win32
{E3B77A78-FD72-4AD7-933A-0503FB21551D}.Debug|Win32.Build.0 = Debug|Win32
{E3B77A78-FD72-4AD7-933A-0503FB21551D}.Release|Win32.ActiveCfg = Release|Win32
{E3B77A78-FD72-4AD7-933A-0503FB21551D}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,49 @@
Debian
======
Maintainer: BDale Garbee (bdale@gag.com) (Official)
Stable Version: 1.2.16rel-4
Testing Version: 1.2.17rel-2
Merged: Yes
FreeBSD
Maintainer: mbr@freebsd.org
Stable Version: 1.2.17rel
Merged: Yes
Gentoo
======
Maintainer: Tom Gall (tgall@gentoo.org) (Last change)
Stable Version: 1.2.18
Merged: No changes
Mandriva
========
Maintainer: Buchan Milne <bgmilne@linux-mandrake.com>
Stable Version: 1.2.18-1mdk
Merged: No changes
Redhat
======
Maintainer: Jesse Keating <jkeating@redhat.com> (Last change) (jnovy@redhat.com)
Stable Version: 1.2.18-8
Merged: No additional changes
SuSE
====
Maintainer http://www.suse.de/feedback
Stable Version: 1.2.18rel-119
Merged: No additional changes

View File

@@ -0,0 +1,247 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="mtx"
ProjectGUID="{7DD926F5-30EA-47D4-B67B-E32C0E221440}"
RootNamespace="mtx"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="VERSION=\&quot;1.3.9-rbn\&quot;;LONG_PRINT_REQUEST_SENSE;WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
DisableSpecificWarnings="4214"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="VERSION=\&quot;1.3.11\&quot;;WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
DisableSpecificWarnings="4214"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\mtx.c"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
CompileAs="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
CompileAs="1"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\mtxl.c"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
CompileAs="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
CompileAs="1"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\..\mtx.h"
>
</File>
<File
RelativePath="..\..\mtxl.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
<File
RelativePath=".\Distributions.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,213 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="nsmhack"
ProjectGUID="{1B3C0A23-4021-4928-92FA-76743B7F7F76}"
RootNamespace="nsmhack"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=".."
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
DisableSpecificWarnings="4214"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=".."
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
DisableSpecificWarnings="4214"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\mtxl.c"
>
</File>
<File
RelativePath="..\..\nsmhack.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\..\mtx.h"
>
</File>
<File
RelativePath="..\..\mtxl.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="scsieject"
ProjectGUID="{E3B77A78-FD72-4AD7-933A-0503FB21551D}"
RootNamespace="scsieject"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
DisableSpecificWarnings="4214;4996"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
DisableSpecificWarnings="4214;4996"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\mtxl.c"
>
</File>
<File
RelativePath="..\..\scsieject.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,203 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="scsitape"
ProjectGUID="{D19E95BD-87C6-4C91-A208-FB8338580F75}"
RootNamespace="scsitape"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
DisableSpecificWarnings="4214;4996"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
DisableSpecificWarnings="4214;4996"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\mtxl.c"
>
</File>
<File
RelativePath="..\..\scsitape.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,213 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="tapeinfo"
ProjectGUID="{A1C8D34F-66EC-4F74-8261-C96B97727218}"
RootNamespace="tapeinfo"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=".."
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
DisableSpecificWarnings="4214"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=".."
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
DisableSpecificWarnings="4214"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\mtxl.c"
>
</File>
<File
RelativePath="..\..\tapeinfo.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\..\mtx.h"
>
</File>
<File
RelativePath="..\..\mtxl.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

257
mtx-1.3.12/mtx.1 Normal file
View File

@@ -0,0 +1,257 @@
.\" mtx.1 Document copyright 2000 Eric Lee Green
.\" Program Copyright 1996, 1997 Leonard Zubkoff
.\" Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
.\" Extensive changes 2000 by Eric Lee Green <eric@badtux.org>
.\"
.\" This is free documentation; 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.
.\"
.\" The GNU General Public License's references to "object code"
.\" and "executables" are to be interpreted as the output of any
.\" document formatting or typesetting system, including
.\" intermediate and printed output.
.\"
.\" This manual 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 manual; if not, write to the Free
.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
.\" USA.
.\"
.TH MTX 1 MTX1.3
.SH NAME
mtx \- control SCSI media changer devices
.SH SYNOPSIS
mtx [-f <scsi-generic-device>] [nobarcode] [invert] [noattach] command [ command ... ]
.SH DESCRIPTION
The
.B mtx
command controls single or multi-drive SCSI media changers such as
tape changers, autoloaders, tape libraries, or optical media jukeboxes.
It can also be used with media changers that use the 'ATTACHED' API,
presuming that they properly report the MChanger bit as required
by the SCSI T-10 SMC specification.
.SH OPTIONS
The first argument, given following
.B -f
, is the SCSI generic device corresponding to your media changer.
Consult your operating system's documentation for more information (for
example, under Linux these are generally /dev/sg0 through /dev/sg15,
under FreeBSD these are /dev/pass0 through /dev/passX,
under SunOS it may be a file under /dev/rdsk).
.P
The 'invert' option will invert (flip) the media (for optical jukeboxes that
allow such) before inserting it into the drive or returning it to the
storage slot.
.P
The 'noattach' option forces the regular media changer API even if the
media changer incorrectly reported that it uses the 'ATTACHED' API.
.P
The 'nobarcode' option forces the loader to not request barcodes even if
the loader is capable of reporting them.
.P
Following these options there may follow
one or more robotics control
commands. Note that the 'invert' and 'noattach'
options apply to ALL of robotics control
commands.
.SH COMMANDS
.TP 10
.B --version
Report the mtx version number (e.g. mtx 1.2.8) and exit.
.TP 10
.B inquiry
Report the product type (Medium Changer, Tape Drive, etc.), Vendor ID,
Product ID, Revision, and whether this uses the Attached Changer API
(some tape drives use this rather than reporting a Medium Changer on a
separate LUN or SCSI address).
.TP 10
.B noattach
Make further commands use the regular media changer API rather than the
_ATTACHED API, no matter what the "Attached" bit said in the Inquiry info.
Needed with some brain-dead changers that report Attached bit but don't respond
to _ATTACHED API.
.TP 10
.B inventory
Makes the robot arm go and check what elements are in the slots. This
is needed for a few libraries like the Breece Hill ones that do not
automatically check the tape inventory at system startup.
.TP 10
.B status
Reports how many drives and storage elements are contained in the
device. For each drive, reports whether it has media loaded in it, and
if so, from which storage slot the media originated. For each storage
slot, reports whether it is empty or full, and if the media changer
has a bar code, MIC reader, or some other way of uniquely identifying
media without loading it into a drive, this reports the volume tag
and/or alternate volume tag for each piece of media.
For historical reasons drives are numbered from 0 and storage slots are
numbered from 1.
.TP 10
.B load <slotnum> [ <drivenum> ]
Load media from slot <slotnum> into drive <drivenum>. Drive 0 is assumed
if the drive number is omitted.
.TP 10
.B unload [<slotnum>] [ <drivenum> ]
Unloads media from drive <drivenum> into slot <slotnum>. If <drivenum> is
omitted, defaults to drive 0 (as do all commands).
If <slotnum> is omitted, defaults to the slot
that the drive was loaded from. Note that there's currently no way to
say 'unload drive 1's media to the slot it came from', other than to
explicitly use that slot number as the destination.
.TP 10
.B [eepos <operation>] transfer <slotnum> <slotnum>
Transfers media from one slot to another, assuming that your mechanism is
capable of doing so. Usually used to move media to/from an import/export
port. 'eepos' is used to extend/retract the import/export
tray on certain mid-range to high end tape libraries (if, e.g., the tray was
slot 32, you might say say 'eepos 1 transfer 32 32' to extend the tray).
Valid values for eepos <operation>
are 0 (do nothing to the import/export tray), 1, and 2 (what 1 and 2 do varies
depending upon the library, consult your library's SCSI-level
documentation).
.TP 10
.B [eepos <operation>] [invert] [invert2] exchange <slotnum> <slotnum> [<slotnum>]
Move medium from the first slot to the second slot, placing the medium
currently in the second slot either back into the first slot or into the
optional third slot.
.TP 10
.B first [<drivenum>]
Loads drive <drivenum> from the first slot in the media
changer. Unloads the drive if there is already media in it (note: you
may need to eject the tape using your OS's tape control commands
first). Note that this command may not be what you want on large
tape libraries -- e.g. on Exabyte 220, the first slot is usually a
cleaning tape. If <drivenum> is omitted, defaults to first drive.
.TP 10
.B last [<drivenum>]
Loads drive <drivenum> from the last slot in the media changer. Unloads
the drive if there is already a tape in it. (Note: you may need to eject
the tape using your OS's tape control commands first).
.TP 10
.B next [<drivenum>]
Unloads the drive and loads the next tape in sequence. If the drive was
empty, loads the first tape into the drive.
.TP 10
.B position <slotnum>
Positions the robot at a specific slot. Needed by some changers to
move to and open the import/export, or mailbox, slot.
.SH AUTHORS
The original 'mtx' program was written by Leonard Zubkoff and extensively
revised for large multi-drive libraries with bar code readers
by Eric Lee Green <eric@badtux.org>. See 'mtx.c' for other contributors.
.SH BUGS AND LIMITATIONS
.P
You may need to do a 'mt offline' on the tape drive to eject the tape
before you can issue the 'mtx unload' command. The Exabyte EZ-17 and 220
in particular will happily sit there snapping the robot arm's claws around
thin air trying to grab a tape that's not there.
.P
For some Linux distributions, you may need to re-compile the kernel to
scan SCSI LUN's in order to detect the media changer. Check /proc/scsi/scsi
to see what's going on.
.P
If you try to unload a tape to its 'source' slot, and said slot is
full, it will instead put the tape into the first empty
slot. Unfortunately the list of empty slots is not updated between
commands on the command line, so if you try to unload another drive to
a full 'source' slot during the same invocation of 'mtx', it will try
to unload to the same (no longer empty) slot and will urp with a SCSI
error.
.P
This program reads the Mode Sense Element Address Assignment Page
(SCSI) and requests data on all available elements. For larger
libraries (more than a couple dozen elements)
this sets a big Allocation_Size in the SCSI command block for the
REQUEST_ELEMENT_STATUS command in order to be able to read the entire
result of a big tape library. Some operating systems may not be able
to handle this. Versions of Linux earlier than 2.2.6, in particular,
may fail this request due to inability to find contiguous pages of
memory for the SCSI transfer (later versions of Linux 'sg' device do
scatter-gather so that this should no longer be a problem).
.P
The
.B eepos
command remains in effect for all further commands on a command
line. Thus you might want to follow
.B eepos 1 transfer 32 32
with
.B eepos 0
as
the next command (which clears the
.B eepos
bits).
.P
Need a better name for 'eepos' command! ('eepos' is the name of the bit
field in the actual low-level SCSI command, and has nothing to do with what
it does).
.P
This program has only been tested on Linux with a limited number of
tape loaders (a dual-drive Exabyte 220 tape library, with bar-code
reader and 21 slots, an Exabyte EZ-17 7-slot autoloader, and a Seagate
DDS-4 autochanger with 6 slots). It may not work on other operating systems
with larger libraries,
due to the big SCSI request size.
Please see the projecdt page http://sourceforge.net/projects/mtx for information
on reporting bugs, requesting features and the mailing list for peer support.
.SH HINTS
Under Linux,
.B cat /proc/scsi/scsi
will tell you what SCSI devices you have.
You can then refer to them as
.B /dev/sga,
.B /dev/sgb,
etc. by the order they
are reported.
.P
Under FreeBSD,
.B camcontrol devlist
will tell you what SCSI devices you
have, along with which
.B pass
device controls them.
.P
Under Solaris, set up your 'sgen' driver so that it'll look for
tape changers (see /kernel/drv/sgen.conf and the sgen man page), type
.B touch /reconfigure
then reboot. You can find your changer in /devices by typing
.B /usr/sbin/devfsadm -C
to clean out no-longer-extant entries in your /devices directory, then
.B find /devices -name \e\(**changer -print
to find the device name. Set the symbolic link
.B /dev/changer
to point
to that device name (if it is not doing so already).
.P
With BRU, set your mount and unmount commands as described on the BRU
web site at http://www.bru.com to move to the next tape when backing up
or restoring. With GNU
.B tar,
see
.B mtx.doc
for an example of how to use
.B tar
and
.B mtx
to make multi-tape backups.
.SH AVAILABILITY
This version of
.B mtx
is currently being maintained by Robert Nelson <robertnelson@users.sourceforge.net> .
The 'mtx' home page is http://mtx.sourceforge.net and the actual code is currently available
there and via SVN from http://sourceforge.net/projects/mtx.
.SH SEE ALSO
.BR mt (1), loaderinfo (1), tapeinfo (1), scsitape (1), scsieject (1)

1075
mtx-1.3.12/mtx.c Normal file

File diff suppressed because it is too large Load Diff

209
mtx-1.3.12/mtx.doc Normal file
View File

@@ -0,0 +1,209 @@
[WARNING: This document is of historical value only! Please read
'mtxl.README.html' and 'man mtx' for current documentation! The only
thing useful here is examples of how to use the 'tar' command for
multi-tape backups.
]
MTX - SCSI Tape Medium Changer Control Program
Version 1.1 for Linux, Solaris, IRIX, Digital Unix, and OpenVMS
2 June 1998
Leonard N. Zubkoff
Dandelion Digital
lnz@dandelion.com
Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
INTRODUCTION
The MTX program controls the robotic mechanism in DDS Autoloaders such as the
Seagate 4586NP (Archive Python 28849-XXX). This program is also reported to
work with the Seagate 4584NP, HP Surestore 12000, Quantum DLT 2500 XT, and AIWA
DL-210. The 4586NP responds to both Logical Units 0 and 1 on the selected
Target ID. Logical Unit 0 supports commands from the SCSI-3 Sequential Stream
Device Command Set and must be accessed via the SCSI tape devices /dev/st<N>.
Logical Unit 1 only supports commands from the SCSI-3 Medium Changer Command
Set and must be accessed via the SCSI Generic devices /dev/sg<N>. In addition,
Logical Unit 0 also acts as an Attached Medium Changer and supports the READ
ELEMENT STATUS and MOVE MEDIUM commands. Since using the Primary Device
(Logical Unit 0) via the Attached Medium Changer interface is sufficient for
the commands provided by this program, Logical Unit 1 is not normally used.
The AIWA DL-210, by contrast, does not support the Attached Medium Changer
commands on Logical Unit 0 and so MTX must be used with Logical Unit 1 via the
SCSI Generic devices /dev/sg<N>. Note that when using the SCSI Generic devices
the Linux kernel option "max_scsi_luns=2" may be necessary.
Medium Changers support four types of elements: Medium Transport Elements,
Storage Elements, Import Export Elements, and Data Transfer Elements. For the
limited case of DDS Autoloaders, only the Data Transfer Element and Storage
Element types are really applicable. The Data Transfer Element is the primary
device where a volume can be loaded to actually perform data transfers. A
Storage Element is a place a volume can be when it is waiting to be used. For
a DDS Autoloader, the Storage Elements are the slots in the cartridge where
tapes may be placed, and the Data Transfer Element is the tape mechanism.
Every Medium Changer Element has a unique integer Element Address that
identifies it in the address space of all Elements known to the Medium Changer.
For the 4586NP, the Data Transfer Element is Address 1 and the Storage Elements
are Addresses 2-5 or 2-13. To simplify the human interface, this program does
not use Element Addresses directly. Rather, it uses Storage Element Numbers
which range from 1 to the number of Storage Elements available.
The specific tape device to be operated on can either be supplied on the
command line with the "-f <tape-device>" option, or via the TAPE environment
variable.
BUILDING MTX
The Makefile contains sections for Linux, Solaris/SPARC, SGI IRIX, and Digital
UNIX. Comment/uncomment as necessary for the target environment. For OpenVMS,
see the file "vms/000readme" for information on building MTX; a VMS release
including pre-built binaries should be available from the WKU VMS FILESERV
ARCHIVES, which are located at URLs http://www2.wku.edu/www/fileserv/ and
ftp://ftp.wku.edu/vms/fileserv/.
COMMANDS
MTX provides the following commands:
mtx [ -f <tape-device> ] inquiry
The "inquiry" command reports the Vendor ID, Product ID, and Revision from a
SCSI INQUIRY command.
kelewan:~# setenv TAPE /dev/st0
kelewan:~# mtx inquiry
Vendor ID: 'ARCHIVE ', Product ID: 'Python 28849-XXX', Revision: '4.CM'
mtx [ -f <tape-device> ] status
The "status" command reports on the status of the DDS Autloader. The report
indicates the status of the Data Transfer Element and each of the Storage
Elements. In the first example, no tape is currently loaded. In the second
example, Storage Element Number 1 is loaded. The Storage Element loaded into
the Data Transfer Element is usually reported by the DDS Autoloader, or it can
be inferred by MTX if there is only a single empty Storage Element.
kelewan:~# mtx status
Data Transfer Element: Empty
Storage Element 1: Full
Storage Element 2: Full
Storage Element 3: Full
Storage Element 4: Full
kelewan:~# mtx status
Data Transfer Element: Full (Storage Element 1 Loaded)
Storage Element 1: Empty
Storage Element 2: Full
Storage Element 3: Full
Storage Element 4: Full
mtx [ -f <tape-device> ] load <storage-element-number>
The "load" command loads the volume in Storage Element <storage-element-number>
into the Data Transfer Element. An error is signaled if the Data Transfer
Element is already full.
mtx [ -f <tape-device> ] unload [ <storage-element-number> ]
The "unload" command unloads the volume in the Data Transfer Element into
Storage Element <storage-element-number>. If <storage-element-number> is not
provided, then the volume is unloaded back into the Storage Element from which
it was originally loaded, if known. An error is signaled if the Storage
Element is already full.
mtx [ -f <tape-device> ] first
The "first" command unloads any volume in the Data Transfer Element and then
loads the volume in the lowest numbered non-empty Storage Element. If the
correct Storage Element is already loaded, the unload/load is suppressed.
mtx [ -f <tape-device> ] next
The "next" command unloads any volume in the Data Transfer Element and then
loads the volume in the lowest numbered non-empty Storage Element above the
Storage Element that was just unloaded. If there is no next Storage Element,
the unload is still performed and the Data Transfer Element will be left empty
so that the volume is not accidentally overwritten.
mtx [ -f <tape-device> ] last
The "last" command unloads any volume in the Data Transfer Element and then
loads the volume in the highest numbered non-empty Storage Element. If the
correct Storage Element is already loaded, the unload/load is suppressed.
mtx [ -f <tape-device> ] previous
The "previous" command unloads any volume in the Data Transfer Element and then
loads the volume in the highest numbered non-empty Storage Element below the
Storage Element that was just unloaded. If there is no previous Storage
Element, the unload is still performed and the Data Transfer Element will be
left empty so that the volume is not accidentally overwritten.
The interface is designed to allow both explicit control of precisely which
Storage Element is loaded, as well as sequential access among only the Storage
Elements that actually have volumes present. Thus for example, one way of
using MTX would be to perform Monday's incremental backups on Storage Element
1, Tuesday's on Storage Element 2, and so on. Multi-volume full backups are
also conveniently supported, as in:
setenv TAPE "/dev/st0"
mtx status
mtx first
time tar --create --one-file-system --atime-preserve \
--listed-incremental `date +%y%m%d`.ss \
--multi-volume --new-volume-script "mtx next" \
--directory / <wherever>
mtx first
time tar --compare --multi-volume --new-volume-script "mtx next" \
--directory /
mtx unload
LINUX KERNEL REQUIREMENTS
Because the MOVE MEDIUM command may require 60 seconds or more to perform a
volume load or unload request, a longer timeout must be provided. Linux
kernels 2.0.30 and 2.1.28 should already contain a long enough timeout. For
earlier kernels, edit the file "linux/drivers/scsi/scsi_ioctl.c" and add the
following entries to the switch statement in ioctl_command:
case MOVE_MEDIUM:
case READ_ELEMENT_STATUS:
timeout = 5 * 60 * HZ; /* 5 minutes */
retries = 1;
break;
For older kernels, you may also need to add the following definitions to
"linux/include/scsi/scsi.h":
#define MOVE_MEDIUM 0xa5
#define READ_ELEMENT_STATUS 0xb8
Note also that "/usr/include/scsi" should be a symbolic link to the directory
"linux/include/scsi" from your kernel source.
Finally, when using MTX you may see some console messages from the SCSI tape
driver mentioning that there is no tape present. These can safely be ignored.

607
mtx-1.3.12/mtx.h Normal file
View File

@@ -0,0 +1,607 @@
/* MTX -- SCSI Tape Attached Medium Control Program
Copyright 1997-1998 Leonard N. Zubkoff <lnz@dandelion.com>
Changes 1999 Eric Lee Green to add support for multi-drive tape changers.
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
See mtx.c for licensing information.
*/
#ifndef MTX_H /* protect against multiple includes... */
#define MTX_H 1
/* surround all the Unix-stuff w/ifndef VMS */
#ifdef VMS
#include "[.vms]defs.h"
#else /* all the Unix stuff: */
#ifdef _MSC_VER
#include "msvc/config.h" /* all the autoconf stuff. */
#else
#include "config.h" /* all the autoconf stuff. */
#endif
/* all the general Unix includes: */
#include <stdio.h>
#include <errno.h>
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_FCNTL_H
# include <fcntl.h>
#endif
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_STDARG_H
# include <stdarg.h>
#endif
#if HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#if HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
/* Now greatly modified to use GNU Autoconf stuff: */
/* If we use the 'sg' interface, like Linux, do this: */
#if HAVE_SCSI_SG_H
# include <scsi/scsi.h>
# include <scsi/scsi_ioctl.h>
# include <scsi/sg.h>
typedef int DEVICE_TYPE; /* the sg interface uses this. */
# define HAVE_GET_ID_LUN 1 /* signal that we have it... */
#endif
/* Windows Native programs built using MinGW */
#if HAVE_DDK_NTDDSCSI_H
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <ddk/ntddscsi.h>
# undef DEVICE_TYPE
typedef int DEVICE_TYPE;
#endif
/* Windows Native programs built using Microsoft Visual C */
#ifdef _MSC_VER
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <winioctl.h>
# include <ntddscsi.h>
# undef DEVICE_TYPE
typedef int DEVICE_TYPE;
#endif
/* The 'cam' interface, like FreeBSD: */
#if HAVE_CAMLIB_H
# include <camlib.h> /* easy (?) access to the CAM user library. */
# include <cam/cam_ccb.h>
# include <cam/scsi/scsi_message.h> /* sigh sigh sigh! */
typedef struct cam_device *DEVICE_TYPE;
#endif
/* the 'uscsi' interface, as used on Solaris: */
#if HAVE_SYS_SCSI_IMPL_USCSI_H
#include <sys/scsi/impl/uscsi.h>
typedef int DEVICE_TYPE;
#endif
/* the scsi_ctl interface, as used on HP/UX: */
#if HAVE_SYS_SCSI_CTL_H
# include <sys/wsio.h>
# include <sys/spinlock.h>
# include <sys/scsi.h>
# include <sys/scsi_ctl.h>
typedef int DEVICE_TYPE;
# ifndef VERSION
# define VERSION "1.2.12 hbb"
# endif
#endif
/* the 'gsc' interface, as used on AIX: */
#if HAVE_SYS_GSCDDS_H
# include <sys/gscdds.h>
typedef int DEVICE_TYPE;
#endif
/* the 'dslib' interface, as used on SGI. */
#if HAVE_DSLIB_H
#include <dslib.h>
typedef dsreq_t *DEVICE_TYPE; /* 64-bit pointers/32bit int on later sgi? */
#endif
#if ((defined(__alpha) && defined(__osf__)) || \
defined(ultrix) || defined(__ultrix))
#include "du/defs.h"
#endif
#endif /* VMS protect. */
/* Do a test for LITTLE_ENDIAN_BITFIELDS. Use WORDS_BIGENDIAN as set
* by configure:
*/
#if WORDS_BIGENDIAN
# define BIG_ENDIAN_BITFIELDS
#else
# define LITTLE_ENDIAN_BITFIELDS
#endif
/* Get rid of some Hocky Pux defines: */
#ifdef S_NO_SENSE
#undef S_NO_SENSE
#endif
#ifdef S_RECOVERED_ERROR
#undef S_RECOVERED_ERROR
#endif
#ifdef S_NOT_READY
#undef S_NOT_READY
#endif
#ifdef S_MEDIUM_ERROR
#undef S_MEDIUM_ERROR
#endif
#ifdef S_HARDWARE_ERROR
#undef S_HARDWARE_ERROR
#endif
#ifdef S_UNIT_ATTENTION
#undef S_UNIT_ATTENTION
#endif
#ifdef S_BLANK_CHECK
#undef S_BLANK_CHECK
#endif
#ifdef S_VOLUME_OVERFLOW
#undef S_VOLUME_OVERFLOW
#endif
/* Note: These are only used for defaults for when we don't have
* the element assignment mode page to tell us real amount...
*/
#define MAX_STORAGE_ELEMENTS 64 /* for the BIG jukeboxes! */
#define MAX_TRANSFER_ELEMENTS 2 /* we just do dual-drive for now :-} */
#define MAX_TRANSPORT_ELEMENTS 1 /* we just do one arm for now... */
#define MTX_ELEMENTSTATUS_ORIGINAL 0
#define MTX_ELEMENTSTATUS_READALL 1
/* These are flags used for the READ_ELEMENT_STATUS and MOVE_MEDIUM
* commands:
*/
typedef struct SCSI_Flags_Struct
{
unsigned char eepos;
unsigned char invert;
unsigned char no_attached; /* ignore _attached bit */
unsigned char no_barcodes; /* don't try to get barcodes. */
int numbytes;
int elementtype;
int numelements;
int attached;
int has_barcodes;
int querytype; //MTX_ELEMENTSTATUS
unsigned char invert2; /* used for EXCHANGE command, sigh. */
} SCSI_Flags_T;
#ifdef _MSC_VER
typedef unsigned char boolean;
#define false 0
#define true 1
typedef unsigned char Direction_T;
#define Input 0
#define Output 1
#else
typedef enum { false, true } boolean;
typedef enum { Input, Output } Direction_T;
#endif
typedef unsigned char CDB_T[12];
typedef struct Inquiry
{
#ifdef LITTLE_ENDIAN_BITFIELDS
unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */
unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */
unsigned char DeviceTypeModifier:7; /* Byte 1 Bits 0-6 */
boolean RMB:1; /* Byte 1 Bit 7 */
unsigned char ANSI_ApprovedVersion:3; /* Byte 2 Bits 0-2 */
unsigned char ECMA_Version:3; /* Byte 2 Bits 3-5 */
unsigned char ISO_Version:2; /* Byte 2 Bits 6-7 */
unsigned char ResponseDataFormat:4; /* Byte 3 Bits 0-3 */
unsigned char :2; /* Byte 3 Bits 4-5 */
boolean TrmIOP:1; /* Byte 3 Bit 6 */
boolean AENC:1; /* Byte 3 Bit 7 */
#else
unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */
unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */
boolean RMB:1; /* Byte 1 Bit 7 */
unsigned char DeviceTypeModifier:7; /* Byte 1 Bits 0-6 */
unsigned char ISO_Version:2; /* Byte 2 Bits 6-7 */
unsigned char ECMA_Version:3; /* Byte 2 Bits 3-5 */
unsigned char ANSI_ApprovedVersion:3; /* Byte 2 Bits 0-2 */
boolean AENC:1; /* Byte 3 Bit 7 */
boolean TrmIOP:1; /* Byte 3 Bit 6 */
unsigned char :2; /* Byte 3 Bits 4-5 */
unsigned char ResponseDataFormat:4; /* Byte 3 Bits 0-3 */
#endif
unsigned char AdditionalLength; /* Byte 4 */
unsigned char :8; /* Byte 5 */
#ifdef LITTLE_ENDIAN_BITFIELDS
boolean ADDR16:1; /* Byte 6 bit 0 */
boolean Obs6_1:1; /* Byte 6 bit 1 */
boolean Obs6_2:1; /* obsolete */ /* Byte 6 bit 2 */
boolean MChngr:1; /* Media Changer */ /* Byte 6 bit 3 */
boolean MultiP:1; /* Byte 6 bit 4 */
boolean VS:1; /* Byte 6 bit 5 */
boolean EncServ:1; /* Byte 6 bit 6 */
boolean BQue:1; /* Byte 6 bit 7 */
#else
boolean BQue:1; /* Byte 6 bit 7 */
boolean EncServ:1; /* Byte 6 bit 6 */
boolean VS:1; /* Byte 6 bit 5 */
boolean MultiP:1; /* Byte 6 bit 4 */
boolean MChngr:1; /* Media Changer */ /* Byte 6 bit 3 */
boolean Obs6_2:1; /* obsolete */ /* Byte 6 bit 2 */
boolean Obs6_1:1; /* Byte 6 bit 1 */
boolean ADDR16:1; /* Byte 6 bit 0 */
#endif
#ifdef LITTLE_ENDIAN_BITFIELDS
boolean SftRe:1; /* Byte 7 Bit 0 */
boolean CmdQue:1; /* Byte 7 Bit 1 */
boolean :1; /* Byte 7 Bit 2 */
boolean Linked:1; /* Byte 7 Bit 3 */
boolean Sync:1; /* Byte 7 Bit 4 */
boolean WBus16:1; /* Byte 7 Bit 5 */
boolean WBus32:1; /* Byte 7 Bit 6 */
boolean RelAdr:1; /* Byte 7 Bit 7 */
#else
boolean RelAdr:1; /* Byte 7 Bit 7 */
boolean WBus32:1; /* Byte 7 Bit 6 */
boolean WBus16:1; /* Byte 7 Bit 5 */
boolean Sync:1; /* Byte 7 Bit 4 */
boolean Linked:1; /* Byte 7 Bit 3 */
boolean :1; /* Byte 7 Bit 2 */
boolean CmdQue:1; /* Byte 7 Bit 1 */
boolean SftRe:1; /* Byte 7 Bit 0 */
#endif
unsigned char VendorIdentification[8]; /* Bytes 8-15 */
unsigned char ProductIdentification[16]; /* Bytes 16-31 */
unsigned char ProductRevisionLevel[4]; /* Bytes 32-35 */
unsigned char FullProductRevisionLevel[19]; /* bytes 36-54 */
unsigned char VendorFlags; /* byte 55 */
}
Inquiry_T;
/* Hockey Pux may define these. If so, *UN*define them. */
#ifdef ILI
#undef ILI
#endif
#ifdef EOM
#undef EOM
#endif
typedef struct RequestSense
{
#ifdef LITTLE_ENDIAN_BITFIELDS
unsigned char ErrorCode:7; /* Byte 0 Bits 0-6 */
boolean Valid:1; /* Byte 0 Bit 7 */
#else
boolean Valid:1; /* Byte 0 Bit 7 */
unsigned char ErrorCode:7; /* Byte 0 Bits 0-6 */
#endif
unsigned char SegmentNumber; /* Byte 1 */
#ifdef LITTLE_ENDIAN_BITFIELDS
unsigned char SenseKey:4; /* Byte 2 Bits 0-3 */
unsigned char :1; /* Byte 2 Bit 4 */
boolean ILI:1; /* Byte 2 Bit 5 */
boolean EOM:1; /* Byte 2 Bit 6 */
boolean Filemark:1; /* Byte 2 Bit 7 */
#else
boolean Filemark:1; /* Byte 2 Bit 7 */
boolean EOM:1; /* Byte 2 Bit 6 */
boolean ILI:1; /* Byte 2 Bit 5 */
unsigned char :1; /* Byte 2 Bit 4 */
unsigned char SenseKey:4; /* Byte 2 Bits 0-3 */
#endif
unsigned char Information[4]; /* Bytes 3-6 */
unsigned char AdditionalSenseLength; /* Byte 7 */
unsigned char CommandSpecificInformation[4]; /* Bytes 8-11 */
unsigned char AdditionalSenseCode; /* Byte 12 */
unsigned char AdditionalSenseCodeQualifier; /* Byte 13 */
unsigned char :8; /* Byte 14 */
#ifdef LITTLE_ENDIAN_BITFIELDS
unsigned char BitPointer:3; /* Byte 15 */
boolean BPV:1;
unsigned char :2;
boolean CommandData :1;
boolean SKSV:1;
#else
boolean SKSV:1;
boolean CommandData :1;
unsigned char :2;
boolean BPV:1;
unsigned char BitPointer:3; /* Byte 15 */
#endif
unsigned char FieldData[2]; /* Byte 16,17 */
}
RequestSense_T;
/* Okay, now for the element status mode sense page (0x1d): */
typedef struct ElementModeSensePageHeader {
unsigned char PageCode; /* byte 0 */
unsigned char ParameterLengthList; /* byte 1; */
unsigned char MediumTransportStartHi; /* byte 2,3 */
unsigned char MediumTransportStartLo;
unsigned char NumMediumTransportHi; /* byte 4,5 */
unsigned char NumMediumTransportLo; /* byte 4,5 */
unsigned char StorageStartHi; /* byte 6,7 */
unsigned char StorageStartLo; /* byte 6,7 */
unsigned char NumStorageHi; /* byte 8,9 */
unsigned char NumStorageLo; /* byte 8,9 */
unsigned char ImportExportStartHi; /* byte 10,11 */
unsigned char ImportExportStartLo; /* byte 10,11 */
unsigned char NumImportExportHi; /* byte 12,13 */
unsigned char NumImportExportLo; /* byte 12,13 */
unsigned char DataTransferStartHi; /* byte 14,15 */
unsigned char DataTransferStartLo; /* byte 14,15 */
unsigned char NumDataTransferHi; /* byte 16,17 */
unsigned char NumDataTransferLo; /* byte 16,17 */
unsigned char Reserved1; /* byte 18, 19 */
unsigned char Reserved2; /* byte 18, 19 */
} ElementModeSensePage_T;
typedef struct ElementModeSenseHeader {
int MaxReadElementStatusData; /* 'nuff for all of below. */
int NumElements; /* total # of elements. */
int MediumTransportStart;
int NumMediumTransport;
int StorageStart;
int NumStorage;
int ImportExportStart;
int NumImportExport;
int DataTransferStart;
int NumDataTransfer;
} ElementModeSense_T;
#ifdef _MSC_VER
typedef char ElementTypeCode_T;
#define AllElementTypes 0
#define MediumTransportElement 1
#define StorageElement 2
#define ImportExportElement 3
#define DataTransferElement 4
#else
typedef enum ElementTypeCode
{
AllElementTypes = 0,
MediumTransportElement = 1,
StorageElement = 2,
ImportExportElement = 3,
DataTransferElement = 4
}
ElementTypeCode_T;
#endif
typedef struct ElementStatusDataHeader
{
unsigned char FirstElementAddressReported[2]; /* Bytes 0-1 */
unsigned char NumberOfElementsAvailable[2]; /* Bytes 2-3 */
unsigned char :8; /* Byte 4 */
unsigned char ByteCountOfReportAvailable[3]; /* Bytes 5-7 */
}
ElementStatusDataHeader_T;
typedef struct ElementStatusPage
{
ElementTypeCode_T ElementTypeCode:8; /* Byte 0 */
#ifdef LITTLE_ENDIAN_BITFIELDS
unsigned char :6; /* Byte 1 Bits 0-5 */
boolean AVolTag:1; /* Byte 1 Bit 6 */
boolean PVolTag:1; /* Byte 1 Bit 7 */
#else
boolean PVolTag:1; /* Byte 1 Bit 7 */
boolean AVolTag:1; /* Byte 1 Bit 6 */
unsigned char :6; /* Byte 1 Bits 0-5 */
#endif
unsigned char ElementDescriptorLength[2]; /* Bytes 2-3 */
unsigned char :8; /* Byte 4 */
unsigned char ByteCountOfDescriptorDataAvailable[3]; /* Bytes 5-7 */
}
ElementStatusPage_T;
typedef struct Element2StatusPage
{
ElementTypeCode_T ElementTypeCode:8; /* Byte 0 */
unsigned char VolBits ; /* byte 1 */
#define E2_PVOLTAG 0x80
#define E2_AVOLTAG 0x40
unsigned char ElementDescriptorLength[2]; /* Bytes 2-3 */
unsigned char :8; /* Byte 4 */
unsigned char ByteCountOfDescriptorDataAvailable[3]; /* Bytes 5-7 */
}
Element2StatusPage_T;
typedef struct TransportElementDescriptorShort
{
unsigned char ElementAddress[2]; /* Bytes 0-1 */
#ifdef LITTLE_ENDIAN_BITFIELDS
boolean Full:1; /* Byte 2 Bit 0 */
unsigned char :1; /* Byte 2 Bit 1 */
boolean Except:1; /* Byte 2 Bit 2 */
unsigned char :5; /* Byte 2 Bits 3-7 */
#else
unsigned char :5; /* Byte 2 Bits 3-7 */
boolean Except:1; /* Byte 2 Bit 2 */
unsigned char :1; /* Byte 2 Bit 1 */
boolean Full:1; /* Byte 2 Bit 0 */
#endif
unsigned char :8; /* Byte 3 */
unsigned char AdditionalSenseCode; /* Byte 4 */
unsigned char AdditionalSenseCodeQualifier; /* Byte 5 */
unsigned char :8; /* Byte 6 */
unsigned char :8; /* Byte 7 */
unsigned char :8; /* Byte 8 */
#ifdef LITTLE_ENDIAN_BITFIELDS
unsigned char :6; /* Byte 9 Bits 0-5 */
boolean SValid:1; /* Byte 9 Bit 6 */
boolean Invert:1; /* Byte 9 Bit 7 */
#else
boolean Invert:1; /* Byte 9 Bit 7 */
boolean SValid:1; /* Byte 9 Bit 6 */
unsigned char :6; /* Byte 9 Bits 0-5 */
#endif
unsigned char SourceStorageElementAddress[2]; /* Bytes 10-11 */
#ifdef HAS_LONG_DESCRIPTORS
unsigned char Reserved[4]; /* Bytes 12-15 */
#endif
}
TransportElementDescriptorShort_T;
typedef struct TransportElementDescriptor
{
unsigned char ElementAddress[2]; /* Bytes 0-1 */
#ifdef LITTLE_ENDIAN_BITFIELDS
boolean Full:1; /* Byte 2 Bit 0 */
unsigned char :1; /* Byte 2 Bit 1 */
boolean Except:1; /* Byte 2 Bit 2 */
unsigned char :5; /* Byte 2 Bits 3-7 */
#else
unsigned char :5; /* Byte 2 Bits 3-7 */
boolean Except:1; /* Byte 2 Bit 2 */
unsigned char :1; /* Byte 2 Bit 1 */
boolean Full:1; /* Byte 2 Bit 0 */
#endif
unsigned char :8; /* Byte 3 */
unsigned char AdditionalSenseCode; /* Byte 4 */
unsigned char AdditionalSenseCodeQualifier; /* Byte 5 */
unsigned char :8; /* Byte 6 */
unsigned char :8; /* Byte 7 */
unsigned char :8; /* Byte 8 */
#ifdef LITTLE_ENDIAN_BITFIELDS
unsigned char :6; /* Byte 9 Bits 0-5 */
boolean SValid:1; /* Byte 9 Bit 6 */
boolean Invert:1; /* Byte 9 Bit 7 */
#else
boolean Invert:1; /* Byte 9 Bit 7 */
boolean SValid:1; /* Byte 9 Bit 6 */
unsigned char :6; /* Byte 9 Bits 0-5 */
#endif
unsigned char SourceStorageElementAddress[2]; /* Bytes 10-11 */
unsigned char PrimaryVolumeTag[36]; /* barcode */
unsigned char AlternateVolumeTag[36];
#ifdef HAS_LONG_DESCRIPTORS
unsigned char Reserved[4]; /* 4 extra bytes? */
#endif
}
TransportElementDescriptor_T;
/* Now for element status data; */
typedef unsigned char barcode[37];
typedef struct ElementStatus {
int StorageElementCount;
int ImportExportCount;
int DataTransferElementCount;
int *DataTransferElementAddress; /* array. */
int *DataTransferElementSourceStorageElementNumber; /* array */
barcode *DataTransferPrimaryVolumeTag; /* array. */
barcode *DataTransferAlternateVolumeTag; /* array. */
barcode *PrimaryVolumeTag; /* array */
barcode *AlternateVolumeTag; /* array */
int *StorageElementAddress; /* array */
boolean *StorageElementIsImportExport; /* array */
int TransportElementAddress; /* assume only one of those... */
boolean *DataTransferElementFull; /* array */
boolean *StorageElementFull; /* array */
} ElementStatus_T;
/* Now for the SCSI ID and LUN information: */
typedef struct scsi_id {
int id;
int lun;
} scsi_id_t;
#define MEDIUM_CHANGER_TYPE 8 /* what type bits are set for medium changers. */
/* The following two structs are used for the brain-dead functions of the
* NSM jukebox.
*/
typedef struct NSM_Param {
unsigned char page_code;
unsigned char reserved;
unsigned char page_len_msb;
unsigned char page_len_lsb;
unsigned char allocation_msb;
unsigned char allocation_lsb;
unsigned char reserved2[2];
unsigned char command_code[4];
unsigned char command_params[2048]; /* egregious overkill. */
} NSM_Param_T;
extern RequestSense_T scsi_error_sense;
typedef struct NSM_Result {
unsigned char page_code;
unsigned char reserved;
unsigned char page_len_msb;
unsigned char page_len_lsb;
unsigned char command_code[4];
unsigned char ces_code[2];
unsigned char return_data[0xffff]; /* egregioius overkill */
} NSM_Result_T;
#endif /* of multi-include protection. */

109
mtx-1.3.12/mtx.spec Normal file
View File

@@ -0,0 +1,109 @@
Name: mtx
Version: 1.3.12
Release: 1%{?dist}
Summary: SCSI media changer control program
License: GPL
Group: Utilities/System
Source0: ftp://ftp.opensource-sw.net/pub/mtx/stable/%{name}-%{version}.tar.gz
Url: http://%{name}.sourceforge.net
BuildRoot: /var/tmp/%{name}-%{version}
%description
The MTX program controls the robotic mechanism in autoloaders and tape
libraries such as the HP SureStore DAT 40x6, Exabyte EZ-17, and
Exabyte 220. This program is also reported to work with a variety of other tape
libraries and autochangers from Tandberg/Overland, Breece Hill, HP, and
Seagate.
%prep
%setup -q
%build
%configure
make
%install
mkdir -p $RPM_BUILD_ROOT/sbin
install mtx $RPM_BUILD_ROOT/sbin/mtx
mkdir -p $RPM_BUILD_ROOT/usr/sbin
install loaderinfo $RPM_BUILD_ROOT/usr/sbin/loaderinfo
install scsieject $RPM_BUILD_ROOT/usr/sbin/scsieject
install scsitape $RPM_BUILD_ROOT/usr/sbin/scsitape
install tapeinfo $RPM_BUILD_ROOT/usr/sbin/tapeinfo
mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man1
install mtx.1 $RPM_BUILD_ROOT/%{_mandir}/man1/mtx.1
install loaderinfo.1 $RPM_BUILD_ROOT/%{_mandir}/man1/loaderinfo.1
install scsieject.1 $RPM_BUILD_ROOT/%{_mandir}/man1/scsieject.1
install scsitape.1 $RPM_BUILD_ROOT/%{_mandir}/man1/scsitape.1
install tapeinfo.1 $RPM_BUILD_ROOT/%{_mandir}/man1/tapeinfo.1
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
%doc mtx.doc CHANGES README mtxl.README.html
%doc COMPATABILITY FAQ LICENSE* TODO contrib
%{_mandir}/man1/*
/sbin/mtx
/usr/sbin/*
%changelog
* Fri Sep 27 2002 Eric Green <eric@badtux.org>
- 1.3.0rel
- move changelog to end.
- change source directory to ftp.badtux.net.
- use * for files to catch new files.
* Wed Apr 18 2001 Kenneth Porter <shiva@well.com>
- 1.2.12pre1
- Need to create usr/sbin for install
* Fri Mar 02 2001 Eric Green <eric@estinc.com>
- 1.2.11pre6
- Move tapeinfo,loaderinfo, scsitape to /usr/sbin rather than /sbin
* Wed Feb 28 2001 Kenneth Porter <shiva@well.com>
- 1.2.11pre5
- Remove commented-out patch.
- Use mandir FHS macro and configure macro.
- Install more stuff.
- Use build policy for stripping.
* Wed Jan 17 2001 Eric Green <eric@estinc.com>
- 1.2.11pre3
- Removed patch, now use ./configure.
* Mon Nov 27 2000 Eric Green <eric@estinc.com>
- 1.2.10
- Fixed patching to use the portable.patch.
* Tue Jul 25 2000 Eric Green <eric@estinc.com>
- 1.2.8
- Added portability patch to mtx.spec so should compile on Red Hat Alpha etc.
* Thu Jun 6 2000 Eric Green <eric@estinc.com>
- 1.2.7
- Fixed single-drive Exabyte 220 special case.
- Fixed ADIC DAT Autochanger special case.
- Fixed mtx.spec to move the binaries to /sbin since we need root access
* Fri May 12 2000 Kenneth Porter <shiva@well.com>
- 1.2.6
- Fixed 'eepos' stuff to use | rather than || (whoops!)
- Accept a 4-byte element descriptor for the robot arm for certain older
- autochangers.
* Mon May 8 2000 Kenneth Porter <shiva@well.com>
- Spell sourceforge right so the link at rpmfind.net will work.
* Thu May 4 2000 Kenneth Porter <shiva@well.com>
- 1.2.5
* Thu Oct 29 1998 Ian Macdonald <ianmacd@xs4all.nl>
- moved mtx from /sbin to /bin, seeing as mt is also located there
* Fri Oct 23 1998 Ian Macdonald <ianmacd@xs4all.nl>
- first RPM release

109
mtx-1.3.12/mtx.spec.in Normal file
View File

@@ -0,0 +1,109 @@
Name: mtx
Version: @@VERSION@@
Release: 1%{?dist}
Summary: SCSI media changer control program
License: GPL
Group: Utilities/System
Source0: ftp://ftp.opensource-sw.net/pub/mtx/stable/%{name}-%{version}.tar.gz
Url: http://%{name}.sourceforge.net
BuildRoot: /var/tmp/%{name}-%{version}
%description
The MTX program controls the robotic mechanism in autoloaders and tape
libraries such as the HP SureStore DAT 40x6, Exabyte EZ-17, and
Exabyte 220. This program is also reported to work with a variety of other tape
libraries and autochangers from Tandberg/Overland, Breece Hill, HP, and
Seagate.
%prep
%setup -q
%build
%configure
make
%install
mkdir -p $RPM_BUILD_ROOT/sbin
install mtx $RPM_BUILD_ROOT/sbin/mtx
mkdir -p $RPM_BUILD_ROOT/usr/sbin
install loaderinfo $RPM_BUILD_ROOT/usr/sbin/loaderinfo
install scsieject $RPM_BUILD_ROOT/usr/sbin/scsieject
install scsitape $RPM_BUILD_ROOT/usr/sbin/scsitape
install tapeinfo $RPM_BUILD_ROOT/usr/sbin/tapeinfo
mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man1
install mtx.1 $RPM_BUILD_ROOT/%{_mandir}/man1/mtx.1
install loaderinfo.1 $RPM_BUILD_ROOT/%{_mandir}/man1/loaderinfo.1
install scsieject.1 $RPM_BUILD_ROOT/%{_mandir}/man1/scsieject.1
install scsitape.1 $RPM_BUILD_ROOT/%{_mandir}/man1/scsitape.1
install tapeinfo.1 $RPM_BUILD_ROOT/%{_mandir}/man1/tapeinfo.1
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
%doc mtx.doc CHANGES README mtxl.README.html
%doc COMPATABILITY FAQ LICENSE* TODO contrib
%{_mandir}/man1/*
/sbin/mtx
/usr/sbin/*
%changelog
* Fri Sep 27 2002 Eric Green <eric@badtux.org>
- 1.3.0rel
- move changelog to end.
- change source directory to ftp.badtux.net.
- use * for files to catch new files.
* Wed Apr 18 2001 Kenneth Porter <shiva@well.com>
- 1.2.12pre1
- Need to create usr/sbin for install
* Fri Mar 02 2001 Eric Green <eric@estinc.com>
- 1.2.11pre6
- Move tapeinfo,loaderinfo, scsitape to /usr/sbin rather than /sbin
* Wed Feb 28 2001 Kenneth Porter <shiva@well.com>
- 1.2.11pre5
- Remove commented-out patch.
- Use mandir FHS macro and configure macro.
- Install more stuff.
- Use build policy for stripping.
* Wed Jan 17 2001 Eric Green <eric@estinc.com>
- 1.2.11pre3
- Removed patch, now use ./configure.
* Mon Nov 27 2000 Eric Green <eric@estinc.com>
- 1.2.10
- Fixed patching to use the portable.patch.
* Tue Jul 25 2000 Eric Green <eric@estinc.com>
- 1.2.8
- Added portability patch to mtx.spec so should compile on Red Hat Alpha etc.
* Thu Jun 6 2000 Eric Green <eric@estinc.com>
- 1.2.7
- Fixed single-drive Exabyte 220 special case.
- Fixed ADIC DAT Autochanger special case.
- Fixed mtx.spec to move the binaries to /sbin since we need root access
* Fri May 12 2000 Kenneth Porter <shiva@well.com>
- 1.2.6
- Fixed 'eepos' stuff to use | rather than || (whoops!)
- Accept a 4-byte element descriptor for the robot arm for certain older
- autochangers.
* Mon May 8 2000 Kenneth Porter <shiva@well.com>
- Spell sourceforge right so the link at rpmfind.net will work.
* Thu May 4 2000 Kenneth Porter <shiva@well.com>
- 1.2.5
* Thu Oct 29 1998 Ian Macdonald <ianmacd@xs4all.nl>
- moved mtx from /sbin to /bin, seeing as mt is also located there
* Fri Oct 23 1998 Ian Macdonald <ianmacd@xs4all.nl>
- first RPM release

165
mtx-1.3.12/mtxl.README.html Normal file
View File

@@ -0,0 +1,165 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>SCSI Media Changer and Backup Device Control System</title>
</head>
<body>
<center>
<h1>SCSI Media Changer and Backup Device Control System</h1>
</center>
<p>
[Also see the SourceForge <a href="http://sourceforge.net/projects/mtx">
project page</a>.]
<p>
<i>mtx</i> is a set of low level driver programs to control features
of SCSI backup related devices such as autoloaders, tape changers,
media jukeboxes, and tape drives. It can also report much data,
including serial numbers, maximum block sizes, and TapeAlert(tm)
messages that most modern tape drives implement (to tell you the exact
reason why a backup or restore failed), as well as do raw SCSI READ
and WRITE commands to tape drives (not important on Linux, but
important on Solaris due to the fact that the Solaris tape driver
supports none of the additional features of tape drives invented after
1988). <i>mtx</i> is designed to be a low level driver in a larger
scripted backup solution, such as <a
href="http://amanda.sourceforge.net">Amanda</a>.
<i>mtx</i> is not
supposed to itself be a high level interface to the SCSI devices that
it controls.
<p>
This version has the following features:
<ul>
<li> Will deal with LARGE media libraries (over a hundred elements).
<li> Supports multi-drive media changers such as the Exabyte 220 dual-
drive tape library.
<li> Supports the 'invert' bit for optical jukeboxes that need that in
order to flip their media.
<li> Supports the 'eepos' bits for libraries that need this to extend/retract
their import/export tray.
<li> Now supports import/export elements!
<li> Reports volume tags (bar codes) and "alternate volume tags"
(whatever those are!) for those tape libraries
that support them.
<li> Now runs under FreeBSD and at least Solaris 8.
<li> Now has a 'man' page!
<li> The actual SCSI manipulation has been separated out into a library, so
that you can create your own "C" programs that manipulate SCSI media changers
directly. (Please note: this is under GPL, so any such programs will have
to be under GPL also).
</ul>
<p>
This program supposedly supports FreeBSD, Solaris, Linux, HP/UX, and
IRIX. Tru64 Unix and VMS are probably irretrievably broken at this
time. This program has been tested under FreeBSD, Solaris, and Linux,
and there only with a limited set of hardware. See the COMPATIBILITY
list in the source code.
</ul>
<h2> Source Code </h2>
The current source code is:
<ul>
<li> <a href="http://mtx.sourceforge.net/mtx-1.2.13.tar.gz">http://mtx.sourceforge.net/mtx-1.2.13.tar.gz</a>
</ul>
RPMs may be available from the following place:
<ul>
<li> <a href="http://rpmfind.net/linux/RPM/mtx.html">RPMfind's 'mtx' page</a>
</ul>
A .spec file is now included in the 'mtx' distribution for building your
own RPM's.
<p>
Note that RPMs
are courtesy of <a href="http://www.sewingwitch.com/ken/">Kenneth Porter</a>,
who should be contacted regarding rpm-related problems.
<p>
<h2> Known Bugs And Limitations </h2>
<ul>
<li>
You may need to do a 'mt offline' (or equivalent for your OS)
on the tape drive to
eject the tape before you can issue the 'mtx unload' command.
The Exabyte EZ-17 and 220 in particular will happily
sit there snapping the robot arm's claws around thin air
trying to grab a tape that's not there.
<li>
The 'next' command does not understand the 'invert' bit (i.e., does not
recognize that for optical jukeboxes, the 'next' of side one is to unload,
invert, and reload the same disk). It always advances to the next
slot instead.
<li>
For some Linux distributions, you may need to re-compile
the kernel to scan SCSI LUN's in order to detect the media
changer. Check /proc/scsi/scsi to see what's going on.
<li>
If you try to unload a tape to its 'source' slot, and said
slot is full, it will instead put the tape into the first
empty slot. Unfortunately the list of empty slots is not
updated between commands on the command line, so if you
try to unload another drive to a full 'source' slot during
the same invocation of 'mtx', it will try to unload to the
same (no longer empty) slot and will urp with a SCSI
error.
<li> For big tape libraries (more than a couple dozen elements) this
may set a big Allocation_Size in the SCSI command block for the
REQUEST_ELEMENT_STATUS command. Some operating systems may not be able
to handle this. Versions of Linux earlier than 2.2.6, in particular,
may fail this request due to inability to find contiguous pages of
memory for the SCSI transfer (later versions of Linux 'sg' device do
scatter-gather so that this should no longer be a problem).
<li> VMS and Tru64 support are probably irretrievably busted.
<li> This program will only use the first arm of multiple-arm robots unless
the robot re-maps all arms to one element ID.
<li> It has been reported that this program works on Solaris 7 using the 'sst'
driver, and may work on Solaris 8 using the 'sgen' driver. 'sst' can
be gotten from the Amanda contrib directory at
<a href="http://download.sourceforge.net/amanda/">http://download.sourceforge.net/amanda</a>.
</ul>
<h2> Philosophy </h2>
The Unix philosophy is "many small tools chained together". <i>mtx</i> supplies
those small tools so that you can create your own backup and
recovery tools by chaining
<i>mtx</i> pieces together, whether it be with /bin/sh, Perl, Python, or
CAML.
<h2> Support </h2>
<ul>
<li>There is now a 'mtx' mailing list at <a href="http://sourceforge.net/projects/mtx/">http://sourceforge.net/projects/mtx/</a>.
<li>There is now a 'mtx' home page at <a href="http://mtx.sourceforge.net">http://mtx.sourceforge.net</a>.
<li> There is now a FAQ that is part of the source code. Please read the
FAQ first.
<li>Report problems to Eric Lee Green (<a
href="mailto:eric@badtux.org">eric@badtux.org</a>). READ THE FAQ FIRST!
</ul>
<h2> See Also: </h2>
<ul>
<li>The man page for 'mtx'! (once you get it installed).
<li>T-10 SCSI Working Group home page at <a href="http://www.t10.org">www.t10.org</a>.
<li>The Linux 'sg' SCSI generic driver home page at <a href="http://www.torque.net/sg/">http://www.torque.net/sg/</a>.
<li> <a href="http://badtux.org/eric">The Home Page Of &lt;UL&gt; Tags Anonymous</a> Hi, my name is Eric, and I am addicted to the &lt;UL&gt; tag...
</ul>
<hr>
<address>Maintained by <a href="mailto:eric@badtux.org">Eric Lee Green</a><br>
Hosted by <a href="http://www.valinux.com">VA Linux</a>'s <a href="http://www.sourceforge.net">SourceForge</a></address><br>
<!-- Created: Fri Mar 3 12:19:38 MST 2000 -->
<!-- hhmts start -->
Last modified: Mon Jun 25 15:37:22 MST 2001
<!-- hhmts end -->
</body>
</html>

1907
mtx-1.3.12/mtxl.c Normal file

File diff suppressed because it is too large Load Diff

109
mtx-1.3.12/mtxl.h Normal file
View File

@@ -0,0 +1,109 @@
/*
MTX -- SCSI Tape Attached Medium Changer Control Program
Copyright 1997-1998 Leonard N. Zubkoff <lnz@dandelion.com>
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
This file created by Eric Lee Green <eric@badtux.org>
This program is free software; you may redistribute and/or modify it under
the terms of the GNU General Public License Version 2 as published by the
Free Software Foundation.
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 complete details.
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
*/
/* Much of the guts of mtx.c has been extracted to mtxl.c, a library file
* full of utility routines. This file is the header file for that library.
* -E
*/
#ifndef MTXL_H
#define MTXL_H 1
#include "mtx.h"
#undef min
#undef max
void FatalError(char *ErrorMessage, ...);
void *xmalloc(size_t Size);
void *xzmalloc(size_t Size);
void slow_bzero(char *buffer, int numchars);
DEVICE_TYPE SCSI_OpenDevice(char *DeviceName);
void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD);
int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense);
void PrintRequestSense(RequestSense_T *RequestSense);
int BigEndian16(unsigned char *BigEndianData);
int BigEndian24(unsigned char *BigEndianData);
int min(int x, int y);
int max(int x, int y);
void PrintHex(int Indent, unsigned char *Buffer, int Length);
int ClearUnitAttention(DEVICE_TYPE fd, RequestSense_T *RequestSense);
ElementStatus_T *ReadElementStatus( DEVICE_TYPE MediumChangerFD,
RequestSense_T *RequestSense,
Inquiry_T *inquiry_info,
SCSI_Flags_T *flags);
Inquiry_T *RequestInquiry( DEVICE_TYPE fd,
RequestSense_T *RequestSense);
RequestSense_T *MoveMedium( DEVICE_TYPE MediumChangerFD,
int SourceAddress,
int DestinationAddress,
ElementStatus_T *ElementStatus,
Inquiry_T *inquiry_info,
SCSI_Flags_T *flags);
RequestSense_T *ExchangeMedium( DEVICE_TYPE MediumChangerFD,
int SourceAddress,
int DestinationAddress,
int Dest2Address,
ElementStatus_T *ElementStatus,
SCSI_Flags_T *flags);
RequestSense_T *PositionElement(DEVICE_TYPE MediumChangerFD,
int DestinationAddress,
ElementStatus_T *ElementStatus);
int Inventory(DEVICE_TYPE MediumChangerFD); /* inventory library */
int LoadUnload(DEVICE_TYPE fd, int bLoad); /* load/unload tape, magazine or disc */
int StartStop(DEVICE_TYPE fd, int bStart); /* start/stop device */
int LockUnlock(DEVICE_TYPE fd, int bLock); /* lock/unlock medium in device */
RequestSense_T *Erase(DEVICE_TYPE fd); /* send SHORT erase to drive */
void SCSI_Set_Timeout(int secs); /* set the SCSI timeout */
void SCSI_Default_Timeout(void); /* go back to default timeout */
/* we may not have this function :-(. */
#ifdef HAVE_GET_ID_LUN
scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd);
#endif
/* These two hacks are so that I can stick the tongue out on an
* NSM optical jukebox.
*/
NSM_Result_T *RecNSMHack(DEVICE_TYPE MediumChangerFD,
int param_len, int timeout);
int SendNSMHack(DEVICE_TYPE MediumChangerFD, NSM_Param_T *nsm_command,
int param_len, int timeout);
#endif

342
mtx-1.3.12/nsmhack.c Normal file
View File

@@ -0,0 +1,342 @@
/* Copyright 2001 DISC Inc.
* Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
* Released under terms of the GNU General Public License as required
* by the license on the file "mtxl.c". See file "LICENSE" for details.
*/
#define DEBUG_NSM 1
/* This is a hack to make the NSM modular series jukeboxes stick out
* their tongue, then retract tongue, so we can import media. They
* automatically stick out their tongue when exporting media, but
* importing media is not working, you try to do a MOVE_MEDIUM and
* it says "What medium?" before even sticking out its tongue.
* My manager has turned in a change request to NSM engineering to direct
* their firmware guys to add EEPOS support to the NSM modular jukeboxes so
* that we have tongue firmware that's compatible with Exabyte, Sony, Breece
* Hill, etc., but until that new firmware is here, this hack will work.
*/
/* Note: Perhaps "hack" is an overstatement, since this will also
* eventually add pack management and other things of that nature
* that are extremely loader dependent.
*/
/* Commands:
-f <devicenode>
tongue_out <sourceslot>
tongue_in
tongue_button_wait
tongue_button_enable
tongue_button_disable
*/
#include "mtxl.h" /* get the SCSI routines out of the main file */
/****************************************************************/
/* Variables: */
/****************************************************************/
/* the device handle we're operating upon, sigh. */
static char *device; /* the text of the device thingy. */
static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) -1;
char *argv0;
int arg[4]; /* arguments for the command. */
#define arg1 (arg[0]) /* for backward compatibility, sigh */
static SCSI_Flags_T SCSI_Flags = { 0, 0, 0,0 };
static ElementStatus_T *ElementStatus = NULL;
/* Okay, now let's do the main routine: */
void Usage(void) {
FatalError("Usage: nsmhack -f <generic-device> <command> where <command> is:\n [tongue_out] | [tongue_in] | [tongue_button_wait] | [tongue_button_enable]\n | tongue_button_disable. \n");
}
static int S_tongue_out(void);
static int S_tongue_in(void);
static int S_slotinfo(void);
static int S_jukeinfo(void);
struct command_table_struct {
int num_args;
char *name;
int (*command)(void);
} command_table[] = {
{ 1, "tongue_out", S_tongue_out },
{ 0, "tongue_in", S_tongue_in },
{ 0, "slotinfo", S_slotinfo },
{ 0, "jukeinfo", S_jukeinfo },
{ 0, NULL, NULL }
};
/* open_device() -- set the 'fh' variable.... */
void open_device(void) {
if (MediumChangerFD != -1) {
SCSI_CloseDevice("Unknown",MediumChangerFD); /* close it, sigh... new device now! */
}
MediumChangerFD = SCSI_OpenDevice(device);
}
static int get_arg(char *arg) {
int retval=-1;
if (*arg < '0' || *arg > '9') {
return -1; /* sorry! */
}
retval=atoi(arg);
return retval;
}
/* we see if we've got a file open. If not, we open one :-(. Then
* we execute the actual command. Or not :-(.
*/
int execute_command(struct command_table_struct *command) {
/* if the device is not already open, then open it from the
* environment.
*/
if (MediumChangerFD == -1) {
/* try to get it from STAPE or TAPE environment variable... */
device=getenv("STAPE");
if (device==NULL) {
device=getenv("TAPE");
if (device==NULL) {
Usage();
}
}
open_device();
}
/* okay, now to execute the command... */
return command->command();
}
/* parse_args():
* Basically, we are parsing argv/argc. We can have multiple commands
* on a line now, such as "unload 3 0 load 4 0" to unload one tape and
* load in another tape into drive 0, and we execute these commands one
* at a time as we come to them. If we don't have a -f at the start, we
* barf. If we leave out a drive #, we default to drive 0 (the first drive
* in the cabinet).
*/
int parse_args(int argc,char **argv) {
int i,cmd_tbl_idx,retval,arg_idx;
struct command_table_struct *command;
i=1;
arg_idx=0;
while (i<argc) {
if (strcmp(argv[i],"-f") == 0) {
i++;
if (i>=argc) {
Usage();
}
device=argv[i++];
open_device(); /* open the device and do a status scan on it... */
} else {
cmd_tbl_idx=0;
command=&command_table[0]; /* default to the first command... */
command=&command_table[cmd_tbl_idx];
while (command->name) {
if (!strcmp(command->name,argv[i])) {
/* we have a match... */
break;
}
/* otherwise we don't have a match... */
cmd_tbl_idx++;
command=&command_table[cmd_tbl_idx];
}
/* if it's not a command, exit.... */
if (!command->name) {
Usage();
}
i++; /* go to the next argument, if possible... */
/* see if we need to gather arguments, though! */
arg1=-1; /* default it to something */
for (arg_idx=0;arg_idx < command->num_args ; arg_idx++) {
if (i < argc) {
arg[arg_idx]=get_arg(argv[i]);
if (arg[arg_idx] != -1) {
i++; /* increment i over the next cmd. */
}
} else {
arg[arg_idx]=0; /* default to 0 setmarks or whatever */
}
}
retval=execute_command(command); /* execute_command handles 'stuff' */
exit(retval);
}
}
return 0; /* should never get here */
}
static void init_param(NSM_Param_T *param, char *command, int paramlen, int resultlen) {
int i;
/* zero it out first: */
memset((char *)param,0,sizeof(NSM_Param_T));
resultlen=resultlen+sizeof(NSM_Result_T)-0xffff;
param->page_code=0x80;
param->reserved=0;
param->page_len_msb=((paramlen+8)>>8) & 0xff;
param->page_len_lsb=(paramlen+8) & 0xff;
param->allocation_msb=((resultlen + 10) >> 8) & 0xff;
param->allocation_lsb= (resultlen+10) & 0xff;
param->reserved2[0]=0;
param->reserved2[1]=0;
for (i=0;i<4;i++) {
param->command_code[i]=command[i];
}
}
static NSM_Result_T *SendRecHack(NSM_Param_T *param,int param_len,
int read_len) {
NSM_Result_T *result;
/* send the command: */
if (SendNSMHack(MediumChangerFD,param,param_len,0)) {
PrintRequestSense(&scsi_error_sense);
FatalError("SendNSMHack failed.\n");
}
/* Now read the result: */
result=RecNSMHack(MediumChangerFD,read_len,0);
if (!result) {
PrintRequestSense(&scsi_error_sense);
FatalError("RecNSMHack failed.\n");
}
return result;
}
/* Print some info about the NSM jukebox. */
static int S_jukeinfo(void) {
NSM_Result_T *result;
NSM_Param_T param;
if (!device)
Usage();
/* okay, we have a device: Let's get vendor ID: */
init_param(&param,"1010",0,8);
result=SendRecHack(&param,0,8);
/* Okay, we got our result, print out the vendor ID: */
result->return_data[8]=0;
printf("Vendor ID: %s\n",result->return_data);
free(result);
/* Get our product ID: */
init_param(&param,"1011",0,16);
result=SendRecHack(&param,0,16);
result->return_data[16]=0;
printf("Product ID: %s\n",result->return_data);
free(result);
init_param(&param,"1012",0,4);
result=SendRecHack(&param,0,4);
result->return_data[4]=0;
printf("Product Revision: %s\n",result->return_data);
free(result);
init_param(&param,"1013",0,8);
result=SendRecHack(&param,0,8);
result->return_data[8]=0;
printf("Production Date: %s\n",result->return_data);
free(result);
init_param(&param,"1014",0,8);
result=SendRecHack(&param,0,8);
result->return_data[8]=0;
printf("Part Number: %s\n",result->return_data);
free(result);
init_param(&param,"1015",0,12);
result=SendRecHack(&param,0,12);
result->return_data[12]=0;
printf("Serial Number: %s\n",result->return_data);
free(result);
init_param(&param,"1016",0,4);
result=SendRecHack(&param,0,4);
result->return_data[4]=0;
printf("Firmware Release: %s\n",result->return_data);
free(result);
init_param(&param,"1017",0,8);
result=SendRecHack(&param,0,8);
result->return_data[8]=0;
printf("Firmware Date: %s\n",result->return_data);
free(result);
return 0;
}
static int S_slotinfo(void) {
NSM_Result_T *result;
NSM_Param_T param;
if (!device)
Usage();
/* Okay, let's see what I can get from slotinfo: */
init_param(&param,"1020",0,6);
result=SendRecHack(&param,0,6);
result->return_data[6]=0;
printf("Layout: %s\n",result->return_data);
free(result);
return 0;
}
static int S_tongue_in(void) {
return 0;
}
/* okay, stick our tongue out. We need a slot ID to grab a caddy from. */
static int S_tongue_out(void) {
int slotnum=arg1;
Inquiry_T *inquiry_info; /* needed by MoveMedium etc... */
RequestSense_T RequestSense;
/* see if we have element status: */
if (ElementStatus==NULL) {
inquiry_info=RequestInquiry(MediumChangerFD,&RequestSense);
if (!inquiry_info) {
PrintRequestSense(&RequestSense);
FatalError("INQUIRY Command Failed\n");
}
ElementStatus = ReadElementStatus(MediumChangerFD,&RequestSense,inquiry_info,&SCSI_Flags);
if (!ElementStatus) {
PrintRequestSense(&RequestSense);
FatalError("READ ELEMENT STATUS Command Failed\n");
}
}
/* Okay, we have element status, so now let's assume that */
return 0;
}
/* See parse_args for the scoop. parse_args does all. */
int main(int argc, char **argv) {
argv0=argv[0];
parse_args(argc,argv);
if (device)
SCSI_CloseDevice(device,MediumChangerFD);
exit(0);
}

159
mtx-1.3.12/scsi_aix.c Normal file
View File

@@ -0,0 +1,159 @@
/* Changes 2003 Steve Heck <steve.heck@am.sony.com>
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
This program is free software; you may redistribute and/or modify it under
the terms of the GNU General Public License Version 2 as published by the
Free Software Foundation.
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 complete details.
*/
/* This is the SCSI commands for AIX using GSC Generic SCSI Interface. */
#define LONG_PRINT_REQUEST_SENSE /* sigh! */
DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
{
int DeviceFD = open(DeviceName, 0);
if (DeviceFD < 0)
FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
return (DEVICE_TYPE) DeviceFD;
}
void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD)
{
if (close(DeviceFD) < 0)
FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
}
#define HAS_SCSI_TIMEOUT
static int timeout = 9 * 60;
void SCSI_Set_Timeout(int to)
{
timeout = to;
}
void SCSI_Default_Timeout(void)
{
timeout = 9 * 60; /* the default */
}
#ifdef DEBUG
int SCSI_DumpBuffer(int DataBufferLength, unsigned char *DataBuffer)
{
int i, j;
j = 0;
for (i = 0; i < DataBufferLength; i++)
{
if (j == 25)
{
fprintf(stderr, "\n");
j = 0;
}
if (j == 0)
{
fprintf(stderr, "%04x:", i);
}
if (j > 0)
{
fprintf(stderr, " ");
}
fprintf(stderr, "%02x", (int)DataBuffer[i]);
j++;
}
fprintf(stderr, "\n");
}
#endif
int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
int ioctl_result;
char sbyte;
scmd_t scmd;
#ifdef DEBUG_SCSI
fprintf(stderr,"------CDB--------\n");
SCSI_DumpBuffer(CDB_Length,(char *)CDB);
#endif
/* memset(&scmd, 0, sizeof(struct scmd_t)); */
/* memset(RequestSense, 0, sizeof(RequestSense_T)); */
switch (Direction)
{
case Input:
scmd.rw = 1;
if (DataBufferLength > 0)
{
memset(DataBuffer, 0, DataBufferLength);
}
break;
case Output:
scmd.rw = 2;
break;
}
/* Set timeout to 5 minutes. */
#ifdef DEBUG_TIMEOUT
fprintf(stderr,"timeout=%d\n",timeout);
fflush(stderr);
#endif
scmd.timeval = timeout;
scmd.cdb = (caddr_t) CDB;
scmd.cdblen = CDB_Length;
scmd.data_buf = DataBuffer;
scmd.datalen = DataBufferLength;
scmd.sense_buf = (caddr_t) RequestSense;
scmd.senselen = sizeof(RequestSense_T);
scmd.statusp = &sbyte;
ioctl_result = ioctl(DeviceFD, GSC_CMD, (caddr_t) &scmd);
SCSI_Default_Timeout(); /* set it back to default, sigh. */
if (ioctl_result < 0)
{
#ifdef DEBUG
perror("mtx");
#endif
return ioctl_result;
}
if (sbyte != 0)
{
return -1;
}
#ifdef DEBUG_SCSI
if (Direction==Input)
{
fprintf(stderr,"--------input data-----------\n");
SCSI_DumpBuffer(DataBufferLength,DataBuffer);
}
#endif
return 0;
}

116
mtx-1.3.12/scsi_freebsd.c Normal file
View File

@@ -0,0 +1,116 @@
/* Copyright 2000 Enhanced Software Technologies Inc. (http://www.estinc.com)
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
Written by Eric Lee Green <eric@badtux.org>
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
This program is free software; you may redistribute and/or modify it under
the terms of the GNU General Public License Version 2 as published by the
Free Software Foundation.
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 complete details.
*/
/* This is the SCSI commands for FreeBSD */
DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
{
struct cam_device *DeviceFD = cam_open_pass(DeviceName, O_RDWR | O_EXCL, NULL);
if (DeviceFD == 0)
FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
return (DEVICE_TYPE) DeviceFD;
}
void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD)
{
cam_close_device((struct cam_device *) DeviceFD);
}
#define PASS_HZ 1000*60
#define PASS_DEFAULT_TIMEOUT 5*PASS_HZ
static int pass_timeout = PASS_DEFAULT_TIMEOUT;
void SCSI_Set_Timeout(int secs)
{
pass_timeout=secs*PASS_HZ;
}
void SCSI_Default_Timeout(void) {
pass_timeout=5*PASS_HZ;
}
int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
struct cam_device *dsp = (struct cam_device *) DeviceFD;
int retval;
union ccb *ccb;
CDB_T *cdb;
int Result;
ccb = cam_getccb(dsp);
cdb = (CDB_T *) &ccb->csio.cdb_io.cdb_bytes; /* pointer to actual cdb. */
/* cam_getccb() zeros the CCB header only. So now clear the
* payload portion of the ccb.
*/
bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
/* copy the CDB... */
memcpy(cdb,CDB,CDB_Length);
/* set the command control block stuff.... the rather involved
* conditional expression sets the direction to NONE if there is no
* data to go in or out, and IN or OUT if we want data. Movement
* commands will have no data buffer, just a CDB, while INQUIRY and
* READ_ELEMENT_STATUS will have input data, and we don't have any
* stuff that outputs data -- yet -- but we may eventually.
*/
cam_fill_csio( &ccb->csio,
1, /* retries */
NULL, /* cbfcnp*/
(DataBufferLength ?
(Direction == Input ? CAM_DIR_IN : CAM_DIR_OUT) :
CAM_DIR_NONE), /* flags */
MSG_SIMPLE_Q_TAG, /* tag action */
DataBuffer, /* data ptr */
DataBufferLength, /* xfer_len */
SSD_FULL_SIZE, /* sense_len */
CDB_Length, /* cdb_len */
pass_timeout /* timeout */ /* should be 5 minutes or more?! */
);
pass_timeout = PASS_DEFAULT_TIMEOUT; /* make sure it gets reset. */
memset(RequestSense, 0, sizeof(RequestSense_T)); /* clear sense buffer... */
if (Direction == Input)
{
memset(DataBuffer, 0, DataBufferLength);
}
Result = cam_send_ccb(DeviceFD,ccb);
if (Result < 0 ||
(ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
{
/* copy our sense data, sigh... */
memcpy(RequestSense,(void *) &ccb->csio.sense_data,
min(sizeof(RequestSense_T), sizeof(struct scsi_sense_data)));
cam_freeccb(ccb);
return -1; /* sorry! */
}
/* okay, we did good, maybe? */
cam_freeccb(ccb);
return 0; /* and done? */
}

134
mtx-1.3.12/scsi_hpux.c Normal file
View File

@@ -0,0 +1,134 @@
/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
Changes copyright 2000 Eric Green <eric@badtux.org>
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
This program is free software; you may redistribute and/or modify it under
the terms of the GNU General Public License Version 2 as published by the
Free Software Foundation.
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 complete details.
struct sctl_io
{
unsigned flags; // IN: SCTL_READ
unsigned cdb_length; // IN
unsigned char cdb[16]; // IN
void *data; // IN
unsigned data_length; // IN
unsigned max_msecs; // IN: milli-seconds before abort
unsigned data_xfer; // OUT
unsigned cdb_status; // OUT: SCSI status
unsigned char sense[256]; // OUT
unsigned sense_status; // OUT: SCSI status
unsigned sense_xfer; // OUT: bytes of sense data received
unsigned reserved[16]; // IN: Must be zero; OUT: undefined
};
*/
/* Hockey Pux may define these. If so, *UN*define them. */
#ifdef ILI
#undef ILI
#endif
#ifdef EOM
#undef EOM
#endif
/* This is the SCSI commands for HPUX. */
#define LONG_PRINT_REQUEST_SENSE /* Sigh! */
DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
{
int DeviceFD = open(DeviceName, O_RDWR | O_NDELAY);
if (DeviceFD < 0)
FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
return (DEVICE_TYPE) DeviceFD;
}
void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD)
{
if (close(DeviceFD) < 0)
FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
}
#define MTX_HZ 1000
#define DEFAULT_HZ (5*60*MTX_HZ)
static int sctl_io_timeout=DEFAULT_HZ; /* default timeout is 5 minutes. */
void SCSI_Set_Timeout(int to)
{
sctl_io_timeout=to*60*MTX_HZ;
}
void SCSI_Default_Timeout(void)
{
sctl_io_timeout=DEFAULT_HZ;
}
int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
int ioctl_result;
struct sctl_io Command;
int i;
memset(&Command, 0, sizeof(struct sctl_io));
memset(RequestSense, 0, sizeof(RequestSense_T));
switch (Direction)
{
case Input:
if (DataBufferLength > 0)
memset(DataBuffer, 0, DataBufferLength);
Command.flags = SCTL_READ | SCTL_INIT_SDTR;
break;
case Output:
Command.flags = SCTL_INIT_WDTR | SCTL_INIT_SDTR;
break;
}
Command.max_msecs = sctl_io_timeout; /* Set timeout to <n> minutes. */
memcpy(Command.cdb, CDB, CDB_Length);
Command.cdb_length = CDB_Length;
Command.data = DataBuffer;
Command.data_length = DataBufferLength;
ioctl_result=ioctl(DeviceFD, SIOC_IO, &Command);
SCSI_Default_Timeout(); /* change the default back to 5 minutes */
if (ioctl_result < 0)
{
perror("mtx");
return ioctl_result;
}
if (Command.sense_xfer > sizeof(RequestSense_T))
{
Command.sense_xfer=sizeof(RequestSense_T);
}
if (Command.sense_xfer)
{
memcpy(RequestSense, Command.sense, Command.sense_xfer);
}
return Command.sense_status;
}

491
mtx-1.3.12/scsi_linux.c Normal file
View File

@@ -0,0 +1,491 @@
/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
Changes in Feb 2000 Eric Green <eric@badtux.org>
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
This program is free software; you may redistribute and/or modify it under
the terms of the GNU General Public License Version 2 as published by the
Free Software Foundation.
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 complete details.
*/
/* this is the SCSI commands for Linux. Note that <eric@badtux.org> changed
* it from using SCSI_IOCTL_SEND_COMMAND to using the SCSI generic interface.
*/
#ifndef HZ
#warning "HZ is not defined, mtx might not work correctly!"
#define HZ 100 /* Jiffys for SG_SET_TIMEOUT */
#endif
/* These are copied out of BRU 16.1, with all the boolean masks changed
* to our bitmasks.
*/
#define S_NO_SENSE(s) ((s)->SenseKey == 0x0)
#define S_RECOVERED_ERROR(s) ((s)->SenseKey == 0x1)
#define S_NOT_READY(s) ((s)->SenseKey == 0x2)
#define S_MEDIUM_ERROR(s) ((s)->SenseKey == 0x3)
#define S_HARDWARE_ERROR(s) ((s)->SenseKey == 0x4)
#define S_UNIT_ATTENTION(s) ((s)->SenseKey == 0x6)
#define S_BLANK_CHECK(s) ((s)->SenseKey == 0x8)
#define S_VOLUME_OVERFLOW(s) ((s)->SenseKey == 0xd)
#define DEFAULT_TIMEOUT 3*60 /* 3 minutes here */
/* Sigh, the T-10 SSC spec says all of the following is needed to
* detect a short read while in variable block mode, and that even
* though we got a BLANK_CHECK or MEDIUM_ERROR, it's still a valid read.
*/
#define HIT_FILEMARK(s) (S_NO_SENSE((s)) && (s)->Filemark && (s)->Valid)
#define SHORT_READ(s) (S_NO_SENSE((s)) && (s)->ILI && (s)->Valid && (s)->AdditionalSenseCode==0 && (s)->AdditionalSenseCodeQualifier==0)
#define HIT_EOD(s) (S_BLANK_CHECK((s)) && (s)->Valid)
#define HIT_EOP(s) (S_MEDIUM_ERROR((s)) && (s)->EOM && (s)->Valid)
#define HIT_EOM(s) ((s)->EOM && (s)->Valid)
#define STILL_A_VALID_READ(s) (HIT_FILEMARK(s) || SHORT_READ(s) || HIT_EOD(s) || HIT_EOP(s) || HIT_EOM(s))
#define SG_SCSI_DEFAULT_TIMEOUT (HZ*60*5) /* 5 minutes? */
static int pack_id;
static int sg_timeout;
DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
{
int timeout = SG_SCSI_DEFAULT_TIMEOUT;
#ifdef SG_IO
int k; /* version */
#endif
int DeviceFD = open(DeviceName, O_RDWR);
if (DeviceFD < 0)
FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
#ifdef SG_IO
/* It is prudent to check we have a sg device by trying an ioctl */
if ((ioctl(DeviceFD, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000))
{
FatalError("%s is not an sg device, or old sg driver\n", DeviceName);
}
#endif
if (ioctl(DeviceFD, SG_SET_TIMEOUT, &timeout))
{
FatalError("failed to set sg timeout - %m\n");
}
pack_id = 1; /* used for SG v3 interface if possible. */
return (DEVICE_TYPE) DeviceFD;
}
void SCSI_Set_Timeout(int secs)
{
sg_timeout = secs * HZ;
}
void SCSI_Default_Timeout(void)
{
sg_timeout = SG_SCSI_DEFAULT_TIMEOUT;
}
void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD)
{
if (close(DeviceFD) < 0)
FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
}
/* Added by Eric Green <eric@estinc.com> to deal with burping
* Seagate autoloader (hopefully!).
*/
/* Get the SCSI ID and LUN... */
scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd)
{
int status;
scsi_id_t *retval;
struct my_scsi_idlun
{
int word1;
int word2;
} idlun;
status = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun);
if (status)
{
return NULL; /* sorry! */
}
retval = (scsi_id_t *)xmalloc(sizeof(scsi_id_t));
retval->id = idlun.word1 & 0xff;
retval->lun = idlun.word1 >> 8 & 0xff;
#ifdef DEBUG
fprintf(stderr, "SCSI:ID=%d LUN=%d\n", retval->id, retval->lun);
#endif
return retval;
}
/* Changed January 2001 by Eric Green <eric@badtux.org> to
* use the Linux version 2.4 SCSI Generic facility if available.
* Liberally cribbed code from Doug Gilbert's sg3 utils.
*/
#ifdef SG_IO
#include "sg_err.h" /* error stuff. */
#include "sg_err.c" /* some of Doug Gilbert's routines */
/* Use the new SG_IO structure */
int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
unsigned int status;
sg_io_hdr_t io_hdr;
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
memset(RequestSense, 0, sizeof(RequestSense_T));
/* Fill in the common stuff... */
io_hdr.interface_id = 'S';
io_hdr.cmd_len = CDB_Length;
io_hdr.mx_sb_len = sizeof(RequestSense_T);
io_hdr.dxfer_len = DataBufferLength;
io_hdr.cmdp = (unsigned char *) CDB;
io_hdr.sbp = (unsigned char *) RequestSense;
io_hdr.dxferp = DataBuffer;
io_hdr.timeout = sg_timeout * 10; /* Convert from Jiffys to milliseconds */
if (Direction==Input)
{
/* fprintf(stderr,"direction=input\n"); */
io_hdr.dxfer_direction=SG_DXFER_FROM_DEV;
}
else
{
/* fprintf(stderr,"direction=output\n"); */
io_hdr.dxfer_direction=SG_DXFER_TO_DEV;
}
/* Now do it: */
if ((status = ioctl(DeviceFD, SG_IO , &io_hdr)) || io_hdr.masked_status)
{
/* fprintf(stderr, "smt_scsi_cmd: Rval=%d Status=%d, errno=%d [%s]\n",status, io_hdr.masked_status,
errno,
strerror(errno)); */
switch (sg_err_category3(&io_hdr))
{
case SG_ERR_CAT_CLEAN:
case SG_ERR_CAT_RECOVERED:
break;
case SG_ERR_CAT_MEDIA_CHANGED:
return 2;
default:
return -1;
}
/* fprintf(stderr,"host_status=%d driver_status=%d residual=%d writelen=%d\n",io_hdr.host_status,io_hdr.driver_status,io_hdr.resid,io_hdr.sb_len_wr ); */
return -errno;
}
/* Now check the returned statuses: */
/* fprintf(stderr,"host_status=%d driver_status=%d residual=%d writelen=%d\n",io_hdr.host_status,io_hdr.driver_status,io_hdr.resid,io_hdr.sb_len_wr ); */
SCSI_Default_Timeout(); /* reset back to default timeout, sigh. */
return 0;
}
#else
/* Changed February 2000 by Eric Green <eric@estinc.com> to
* use the SCSI generic interface rather than SCSI_IOCTL_SEND_COMMAND
* so that we can get more than PAGE_SIZE data....
*
* Note that the SCSI generic interface abuses READ and WRITE calls to serve
* the same purpose as IOCTL calls, i.e., for "writes", the contents of the
* buffer that you send as the argument to the write() call are actually
* altered to fill in result status and sense data (if needed).
* Also note that this brain-dead interface does not have any sort of
* provisions for expanding the sg_header struct in a backward-compatible
* manner. This sucks. But sucks less than SCSI_IOCTL_SEND_COMMAND, sigh.
*/
#ifndef OLD_EXECUTE_COMMAND_STUFF
static void slow_memcopy(unsigned char *src, unsigned char *dest, int numbytes)
{
while (numbytes--)
{
*dest++ = *src++;
}
}
int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
unsigned char *Command=NULL; /* the command data struct sent to them... */
unsigned char *ResultBuf=NULL; /* the data we read in return... */
unsigned char *src; /* for copying stuff, sigh. */
unsigned char *dest; /* for copy stuff, again, sigh. */
int write_length = sizeof(struct sg_header)+CDB_Length;
int i; /* a random index... */
int result; /* the result of the write... */
struct sg_header *Header; /* we actually point this into Command... */
struct sg_header *ResultHeader; /* we point this into ResultBuf... */
/* First, see if we need to set our SCSI timeout to something different */
if (sg_timeout != SG_SCSI_DEFAULT_TIMEOUT)
{
/* if not default, set it: */
#ifdef DEBUG_TIMEOUT
fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
fflush(stderr);
#endif
if(ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout))
{
FatalError("failed to set sg timeout - %m\n");
}
}
if (Direction == Output)
{
/* if we're writing, our length is longer... */
write_length += DataBufferLength;
}
/* allocate some memory... enough for the command plus the header +
* any other data that we may need here...
*/
Command = (unsigned char *)xmalloc(write_length);
Header = (struct sg_header *) Command; /* make it point to start of buf */
dest = Command; /* now to copy the CDB... from start of buffer,*/
dest += sizeof(struct sg_header); /* increment it past the header. */
slow_memcopy((char *)CDB, dest, CDB_Length);
/* if we are writing additional data, tack it on here! */
if (Direction == Output)
{
dest += CDB_Length;
slow_memcopy(DataBuffer, dest, DataBufferLength); /* copy to end of command */
}
/* Now to fill in the Header struct: */
Header->reply_len=DataBufferLength+sizeof(struct sg_header);
#ifdef DEBUG
fprintf(stderr,"sg:reply_len(sent)=%d\n",Header->reply_len);
#endif
Header->twelve_byte = CDB_Length == 12;
Header->result = 0;
Header->pack_len = write_length; /* # of bytes written... */
Header->pack_id = 0; /* not used */
Header->other_flags = 0; /* not used. */
Header->sense_buffer[0]=0; /* used? */
/* Now to do the write... */
result = write(DeviceFD,Command,write_length);
/* Now to check the result :-(. */
/* Note that we don't have any request sense here. So we have no
* idea what's going on.
*/
if (result < 0 || result != write_length || Header->result || Header->sense_buffer[0])
{
#ifdef DEBUG_SCSI
fprintf(stderr,"scsi:result=%d Header->result=%d Header->sense_buffer[0]=%d\n",
result,Header->result,Header->sense_buffer[0]);
#endif
/* we don't have any real sense data, sigh :-(. */
if (Header->sense_buffer[0])
{
/* well, I guess we DID have some! eep! copy the sense data! */
slow_memcopy((char *)Header->sense_buffer,(char *)RequestSense,
sizeof(Header->sense_buffer));
}
else
{
dest=(unsigned char *)RequestSense;
*dest=(unsigned char)Header->result; /* may chop, sigh... */
}
/* okay, now, we may or may not need to find a non-zero value to return.
* For tape drives, we may get a BLANK_CHECK or MEDIUM_ERROR and find
* that it's *STILL* a good read! Use the STILL_A_VALID_READ macro
* that calls all those macros I cribbed from Richard.
*/
if (!STILL_A_VALID_READ(RequestSense))
{
free(Command); /* zap memory leak, sigh */
/* okay, find us a non-zero value to return :-(. */
if (result)
{
return result;
}
else if (Header->result)
{
return Header->result;
}
else
{
return -1; /* sigh */
}
}
else
{
result=-1;
}
}
else
{
result=0; /* we're okay! */
}
/* now to allocate the new block.... */
ResultBuf=(unsigned char *)xmalloc(Header->reply_len);
/* now to clear ResultBuf... */
slow_bzero(ResultBuf,Header->reply_len);
ResultHeader=(struct sg_header *)ResultBuf;
/* copy the original Header... */
ResultHeader->result=0;
ResultHeader->pack_id=0;
ResultHeader->other_flags=0;
ResultHeader->reply_len=Header->reply_len;
ResultHeader->twelve_byte = CDB_Length == 12;
ResultHeader->pack_len = write_length; /* # of bytes written... */
ResultHeader->sense_buffer[0]=0; /* whoops! Zero that! */
#ifdef DEBUG
fprintf(stderr,"sg:Reading %d bytes from DeviceFD\n",Header->reply_len);
fflush(stderr);
#endif
result=read(DeviceFD,ResultBuf,Header->reply_len);
#ifdef DEBUG
fprintf(stderr,"sg:result=%d ResultHeader->result=%d\n",
result,ResultHeader->result);
fflush(stderr);
#endif
/* New: added check to see if the result block is still all zeros! */
if (result < 0 ||
result != Header->reply_len ||
ResultHeader->result ||
ResultHeader->sense_buffer[0])
{
#ifdef DEBUG
fprintf(stderr,
"scsi: result=%d Header->reply_len=%d ResultHeader->result=%d ResultHeader->sense_buffer[0]=%d\n",
result,
Header->reply_len,
ResultHeader->result,
ResultHeader->sense_buffer[0]);
#endif
/* eep! copy the sense data! */
slow_memcopy((char *)ResultHeader->sense_buffer,(char *)RequestSense,
sizeof(ResultHeader->sense_buffer));
/* sense data copied, now find us a non-zero value to return :-(. */
/* NOTE: Some commands return sense data even though they validly
* executed! We catch a few of those with the macro STILL_A_VALID_READ.
*/
if (!STILL_A_VALID_READ(RequestSense))
{
free(Command);
if (result)
{
free(ResultBuf);
return result;
}
else if (ResultHeader->result)
{
free(ResultBuf);
return ResultHeader->result;
}
else
{
free(ResultBuf);
return -1; /* sigh! */
}
}
else
{
result=-1; /* if it was a valid read, still have -1 result. */
}
}
else
{
result=0;
}
/* See if we need to reset our SCSI timeout */
if (sg_timeout != SG_SCSI_DEFAULT_TIMEOUT)
{
sg_timeout = SG_SCSI_DEFAULT_TIMEOUT; /* reset it back to default */
#ifdef DEBUG_TIMEOUT
fprintf(stderr,"Setting timeout to %d\n", sg_timeout);
fflush(stderr);
#endif
/* if not default, set it: */
if (ioctl(DeviceFD, SG_SET_TIMEOUT, &sg_timeout))
{
FatalError("failed to set sg timeout - %m\n");
}
}
/* now for the crowning moment: copying any result into the DataBuffer! */
/* (but only if it were an input command and not an output command :-} */
if (Direction == Input)
{
#ifdef DEBUG
fprintf(stderr,"Header->reply_len=%d,ResultHeader->reply_len=%d\n",
Header->reply_len,ResultHeader->reply_len);
#endif
src=ResultBuf+sizeof(struct sg_header);
dest=DataBuffer;
for (i = 0; i < ResultHeader->reply_len; i++)
{
if (i >= DataBufferLength)
break; /* eep! */
*dest++ = *src++;
}
}
/* and return! */
free(Command); /* clean up memory leak... */
free(ResultBuf);
return result; /* good stuff ! */
}
#endif
#endif /* #ifdef SG_IO */

81
mtx-1.3.12/scsi_sgi.c Normal file
View File

@@ -0,0 +1,81 @@
/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
Changes copyright 2000 Eric Green <eric@badtux.org>
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
This program is free software; you may redistribute and/or modify it under
the terms of the GNU General Public License Version 2 as published by the
Free Software Foundation.
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 complete details.
*/
/* This is the SCSI commands for SGI Iris */
DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
{
dsreq_t *DeviceFD = dsopen(DeviceName, O_RDWR | O_EXCL);
if (DeviceFD == 0)
FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
return (DEVICE_TYPE) DeviceFD;
}
void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD)
{
dsclose((dsreq_t *) DeviceFD);
}
#define MTX_HZ 1000
#define MTX_DEFAULT_SCSI_TIMEOUT 60*5*MTX_HZ /* 5 minutes! */
static int mtx_default_timeout = MTX_DEFAULT_SCSI_TIMEOUT ;
void SCSI_Set_Timeout(int sec)
{
mtx_default_timeout=sec*MTX_HZ;
}
void SCSI_Default_Timeout()
{
mtx_default_timeout=MTX_DEFAULT_SCSI_TIMEOUT;
}
int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
dsreq_t *dsp = (dsreq_t *) DeviceFD;
int Result;
memset(RequestSense, 0, sizeof(RequestSense_T));
memcpy(CMDBUF(dsp), CDB, CDB_Length);
if (Direction == Input)
{
memset(DataBuffer, 0, DataBufferLength);
filldsreq(dsp, (unsigned char *) DataBuffer, DataBufferLength, DSRQ_READ | DSRQ_SENSE);
}
else
filldsreq(dsp, (unsigned char *) DataBuffer, DataBufferLength, DSRQ_WRITE | DSRQ_SENSE);
/* Set 5 minute timeout. */
/* TIME(dsp) = 300 * 1000; */
TIME(dsp) = mtx_default_timeout;
Result = doscsireq(getfd((dsp)), dsp);
if (SENSESENT(dsp) > 0)
{
memcpy(RequestSense, SENSEBUF(dsp), min(sizeof(RequestSense_T), SENSESENT(dsp)));
}
SCSI_Default_Timeout(); /* reset the mtx default timeout */
return Result;
}

156
mtx-1.3.12/scsi_sun.c Normal file
View File

@@ -0,0 +1,156 @@
/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
Changes copyright 2000 Eric Green <eric@badtux.org>
Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
This program is free software; you may redistribute and/or modify it under
the terms of the GNU General Public License Version 2 as published by the
Free Software Foundation.
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 complete details.
*/
/* This is the SCSI commands for Sun Solaris. */
#define LONG_PRINT_REQUEST_SENSE /* sigh! */
DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
{
int DeviceFD = open(DeviceName, O_RDWR | O_NDELAY);
if (DeviceFD < 0)
FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
return (DEVICE_TYPE) DeviceFD;
}
void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD)
{
if (close(DeviceFD) < 0)
FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
}
#define HAS_SCSI_TIMEOUT
static int uscsi_timeout=5*60;
void SCSI_Set_Timeout(int to)
{
uscsi_timeout = to;
}
void SCSI_Default_Timeout(void)
{
uscsi_timeout=5*60; /* the default */
}
#ifdef DEBUG
int SCSI_DumpBuffer(int DataBufferLength, unsigned char *DataBuffer)
{
int i,j;
j = 0;
for (i = 0; i < DataBufferLength; i++)
{
if (j == 25)
{
fprintf(stderr,"\n");
j = 0;
}
if (j == 0)
{
fprintf(stderr, "%04x:", i);
}
if (j > 0)
{
fprintf(stderr," ");
}
fprintf(stderr, "%02x", (int)DataBuffer[i]);
j++;
}
fprintf(stderr, "\n");
}
#endif
int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
int ioctl_result;
struct uscsi_cmd Command;
#ifdef DEBUG_SCSI
fprintf(stderr,"------CDB--------\n");
SCSI_DumpBuffer(CDB_Length,(char *)CDB);
#endif
memset(&Command, 0, sizeof(struct uscsi_cmd));
memset(RequestSense, 0, sizeof(RequestSense_T));
switch (Direction)
{
case Input:
Command.uscsi_flags = USCSI_DIAGNOSE | USCSI_ISOLATE | USCSI_RQENABLE;
if (DataBufferLength > 0)
{
memset(DataBuffer, 0, DataBufferLength);
Command.uscsi_flags |= USCSI_READ;
}
break;
case Output:
Command.uscsi_flags = USCSI_DIAGNOSE | USCSI_ISOLATE |
USCSI_WRITE | USCSI_RQENABLE;
break;
}
/* Set timeout to 5 minutes. */
#ifdef DEBUG_TIMEOUT
fprintf(stderr,"uscsi_timeout=%d\n",uscsi_timeout);
fflush(stderr);
#endif
Command.uscsi_timeout = uscsi_timeout;
Command.uscsi_cdb = (caddr_t) CDB;
Command.uscsi_cdblen = CDB_Length;
Command.uscsi_bufaddr = DataBuffer;
Command.uscsi_buflen = DataBufferLength;
Command.uscsi_rqbuf = (caddr_t) RequestSense;
Command.uscsi_rqlen = sizeof(RequestSense_T);
ioctl_result = ioctl(DeviceFD, USCSICMD, &Command);
SCSI_Default_Timeout(); /* set it back to default, sigh. */
if (ioctl_result < 0)
{
#ifdef DEBUG
perror("mtx");
#endif
return ioctl_result;
}
if (RequestSense->ErrorCode > 1)
{
return -1;
}
#ifdef DEBUG_SCSI
if (Direction==Input)
{
fprintf(stderr,"--------input data-----------\n");
SCSI_DumpBuffer(DataBufferLength, DataBuffer);
}
#endif
return 0;
}

355
mtx-1.3.12/scsi_win32.c Normal file
View File

@@ -0,0 +1,355 @@
/* Copyright 2006-2008 Robert Nelson <robertn@the-nelsons.org>
$Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
$Revision: 193 $
This program is free software; you may redistribute and/or modify it under
the terms of the GNU General Public License Version 2 as published by the
Free Software Foundation.
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 complete details.
*/
/*
* This is the SCSI commands for Windows.
*/
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef _MSC_VER
#include <ntddscsi.h>
#else
#include <ddk/ntddscsi.h>
#endif
#define SCSI_DEFAULT_TIMEOUT 300 /* 1 minutes */
#define SCSI_MAX_TIMEOUT 108000 /* 30 hours */
typedef struct _HANDLE_ENTRY
{
HANDLE hDevice;
UCHAR PortId;
UCHAR PathId;
UCHAR TargetId;
UCHAR Lun;
} HANDLE_ENTRY, *PHANDLE_ENTRY;
PHANDLE_ENTRY HandleTable = NULL;
int nEntries = 0;
DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
{
int DeviceIndex;
TCHAR szDevicePath[256];
int nColons = 0;
int index;
int port, path, target, lun;
for (DeviceIndex = 0; DeviceIndex < nEntries; DeviceIndex++)
{
if (HandleTable[DeviceIndex].hDevice == INVALID_HANDLE_VALUE)
break;
}
if (DeviceIndex >= nEntries)
{
PHANDLE_ENTRY pNewTable;
nEntries += 4;
if (HandleTable == NULL)
{
pNewTable = (PHANDLE_ENTRY)malloc(nEntries * sizeof(HANDLE_ENTRY));
}
else
{
pNewTable = (PHANDLE_ENTRY)realloc(HandleTable, nEntries * sizeof(HANDLE_ENTRY));
}
if (pNewTable == NULL)
{
FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
}
HandleTable = pNewTable;
}
for (index = 0; DeviceName[index] != '\0'; index++)
{
if (DeviceName[index] == ':')
nColons++;
else if (DeviceName[index] < '0' || DeviceName[index] > '9')
break;
}
if (DeviceName[index] == '\0' && nColons == 3 &&
sscanf(DeviceName, "%d:%d:%d:%d", &port, &path, &target, &lun) == 4)
{
HandleTable[DeviceIndex].PortId = (UCHAR)port;
HandleTable[DeviceIndex].PathId = (UCHAR)path;
HandleTable[DeviceIndex].TargetId = (UCHAR)target;
HandleTable[DeviceIndex].Lun = (UCHAR)lun;
sprintf(szDevicePath, "\\\\.\\scsi%d:", port);
}
else
{
int nPrefixLength = 0;
if (DeviceName[0] != '\\') {
memcpy(szDevicePath, "\\\\.\\", 4 * sizeof(TCHAR));
nPrefixLength = 4;
}
HandleTable[DeviceIndex].PortId = 0;
HandleTable[DeviceIndex].PathId = 0;
HandleTable[DeviceIndex].TargetId = 0;
HandleTable[DeviceIndex].Lun = 0;
strncpy(&szDevicePath[nPrefixLength],
DeviceName,
sizeof(szDevicePath) / sizeof(TCHAR) - nPrefixLength - 1);
szDevicePath[sizeof(szDevicePath) / sizeof(TCHAR) - 1] = '\0';
}
HandleTable[DeviceIndex].hDevice = CreateFile(szDevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (HandleTable[DeviceIndex].hDevice == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
#if DEBUG
LPSTR lpszMessage;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, (LPSTR)&lpszMessage, 0, NULL);
fputs(lpszMessage, stderr);
#endif
switch (dwError)
{
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
errno = ENOENT;
break;
case ERROR_TOO_MANY_OPEN_FILES:
errno = EMFILE;
break;
default:
case ERROR_ACCESS_DENIED:
case ERROR_SHARING_VIOLATION:
case ERROR_LOCK_VIOLATION:
case ERROR_INVALID_NAME:
errno = EACCES;
break;
case ERROR_FILE_EXISTS:
errno = EEXIST;
break;
case ERROR_INVALID_PARAMETER:
errno = EINVAL;
break;
}
FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
}
return DeviceIndex;
}
static int scsi_timeout = SCSI_DEFAULT_TIMEOUT;
void SCSI_Set_Timeout(int secs)
{
if (secs > SCSI_MAX_TIMEOUT)
{
secs = SCSI_MAX_TIMEOUT;
}
scsi_timeout = secs;
}
void SCSI_Default_Timeout(void)
{
scsi_timeout = SCSI_DEFAULT_TIMEOUT;
}
void SCSI_CloseDevice(char *DeviceName, DEVICE_TYPE DeviceFD)
{
if (DeviceFD < nEntries)
{
CloseHandle(HandleTable[DeviceFD].hDevice);
HandleTable[DeviceFD].hDevice = INVALID_HANDLE_VALUE;
}
else
{
errno = EBADF;
FatalError("cannot close SCSI device '%s' - %m\n", DeviceName);
}
}
/* Get the SCSI ID and LUN... */
scsi_id_t *SCSI_GetIDLun(DEVICE_TYPE fd)
{
scsi_id_t * retval;
SCSI_ADDRESS ScsiAddress;
BOOL bResult;
DWORD dwBytesReturned;
if (fd < nEntries)
{
retval = (scsi_id_t *)xmalloc(sizeof(scsi_id_t));
retval->id = HandleTable[fd].TargetId;
retval->lun = HandleTable[fd].Lun;
#ifdef DEBUG
fprintf(stderr,"SCSI:ID=%d LUN=%d\n", retval->id, retval->lun);
#endif
return retval;
}
else
{
errno = EBADF;
FatalError("cannot close SCSI device - %m\n");
}
memset(&ScsiAddress, 0, sizeof(ScsiAddress));
ScsiAddress.Length = sizeof(ScsiAddress);
bResult = DeviceIoControl( HandleTable[fd].hDevice,
IOCTL_SCSI_GET_ADDRESS,
&ScsiAddress, sizeof(ScsiAddress),
&ScsiAddress, sizeof(ScsiAddress),
&dwBytesReturned,
NULL);
if (!bResult)
{
return NULL;
}
retval = (scsi_id_t *)xmalloc(sizeof(scsi_id_t));
retval->id = ScsiAddress.TargetId;
retval->lun = ScsiAddress.Lun;
#ifdef DEBUG
fprintf(stderr,"SCSI:ID=%d LUN=%d\n",retval->id,retval->lun);
#endif
return retval;
}
int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
PSCSI_PASS_THROUGH ScsiPassThrough;
const DWORD dwDataBufferOffset = sizeof(SCSI_PASS_THROUGH) + (sizeof(RequestSense_T) + 3) / 4 * 4;
const DWORD dwBufferSize = dwDataBufferOffset + DataBufferLength;
BOOL bResult;
DWORD dwBytesReturned;
DWORD dwInputLength;
DWORD dwOutputLength;
if (DeviceFD >= nEntries || HandleTable[DeviceFD].hDevice == INVALID_HANDLE_VALUE)
{
errno = EBADF;
return -1;
}
ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize);
memset(ScsiPassThrough, 0, dwDataBufferOffset);
ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH);
ScsiPassThrough->PathId = HandleTable[DeviceFD].PathId;
ScsiPassThrough->TargetId = HandleTable[DeviceFD].TargetId;
ScsiPassThrough->Lun = HandleTable[DeviceFD].Lun;
ScsiPassThrough->CdbLength = (UCHAR)CDB_Length;
ScsiPassThrough->DataIn = Direction == Input;
ScsiPassThrough->DataBufferOffset = dwDataBufferOffset;
ScsiPassThrough->DataTransferLength = DataBufferLength;
ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH);
ScsiPassThrough->SenseInfoLength = sizeof(RequestSense_T);
ScsiPassThrough->TimeOutValue = scsi_timeout;
memcpy(ScsiPassThrough->Cdb, CDB, CDB_Length);
dwBytesReturned = 0;
if (Direction == Output)
{
memcpy((void *)(((char *)ScsiPassThrough) + dwDataBufferOffset), DataBuffer, DataBufferLength);
dwInputLength = dwBufferSize;
dwOutputLength = dwDataBufferOffset;
}
else
{
dwInputLength = sizeof(SCSI_PASS_THROUGH);
dwOutputLength = dwBufferSize;
}
bResult = DeviceIoControl( HandleTable[DeviceFD].hDevice,
IOCTL_SCSI_PASS_THROUGH,
ScsiPassThrough, dwInputLength,
ScsiPassThrough, dwOutputLength,
&dwBytesReturned,
NULL);
if (bResult)
{
if (ScsiPassThrough->ScsiStatus != 0)
{
memcpy(RequestSense, &ScsiPassThrough[1], sizeof(RequestSense_T));
#if DEBUG
fprintf(stderr, "Command failed - ScsiStatus = %d\n", ScsiPassThrough->ScsiStatus);
PrintRequestSense(RequestSense);
#endif
bResult = false;
}
else
{
if (Direction == Input)
{
memcpy( DataBuffer,
(void *)(((char *)ScsiPassThrough) + dwDataBufferOffset),
DataBufferLength);
}
}
}
else
{
#if DEBUG
DWORD dwError = GetLastError();
LPSTR lpszMessage;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, (LPSTR)&lpszMessage, 0, NULL);
fputs(lpszMessage, stderr);
LocalFree(lpszMessage);
#endif
memset(RequestSense, 0, sizeof(RequestSense_T));
}
free(ScsiPassThrough);
return bResult ? 0 : -1;
}

116
mtx-1.3.12/scsieject.1 Normal file
View File

@@ -0,0 +1,116 @@
.\" scsieject.1 Document Copyright 2007-2008 Robert Nelson
.\"
.\" This is free documentation; 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.
.\"
.\" The GNU General Public License's references to "object code"
.\" and "executables" are to be interpreted as the output of any
.\" document formatting or typesetting system, including
.\" intermediate and printed output.
.\"
.\" This manual 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 manual; if not, write to the Free
.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
.\" USA.
.\"
.TH scsieject 1 scsieject1.0
.SH NAME
scsieject \- control SCSI tape devices
.SH SYNOPSIS
scsieject [-f <scsi-generic-device>] commands
.SH DESCRIPTION
The
.B scsieject
command controls SCSI devices in a platform-independent
manner. As long as 'mtx' works on the platform, so does 'scsieject'.
.SH OPTIONS
The first argument, given following
.B -f
, is the SCSI generic device corresponding to your tape drive.
Consult your operating system's documentation for more information (for
example, under Linux these are generally /dev/sg0 through /dev/sg15,
under FreeBSD these are /dev/pass0 through /dev/passX. Under Solaris
this is usually the same as your tape drive (Solaris has a SCSI passthrough
ioctl). You can set the STAPE or TAPE environment variable rather
than use -f.
.P
.SH COMMANDS
.TP 10
.B load
Load the medium into the drive. When this command is issued to a CD/DVD drive
and the tray is extended the tray will be retracted if the drive is capable of it.
.TP 10
.B unload
Unload the medium from the drive (also known as eject). When this command is issued
to a CD/DVD drive or a tape drive the media will be ejected if the device supports it.
.TP 10
.B start
Start the device. Some devices require a start command after a media changer has
loaded new media into the device.
.TP 10
.B stop
Stop the device. Some devices require a stop command prior to unloading the medium
from the device when using a media changer.
.TP 10
.B lock
Lock the device. Locks the device so that the medium cannot be removed manually.
.TP 10
.B unlock
Unlock the device. Unlocks the device so that the medium can be removed manually.
.SH AUTHORS
This program was written by Robert Nelson <robertnelson@users.sourceforge.net>
based on the scsitape program written by Eric Lee Green <eric@badtux.org>.
Major portions of the 'mtxl.c' library used herein were written by
Leonard Zubkoff.
.P
.SH HINTS
Under Linux,
.B cat /proc/scsi/scsi
will tell you what SCSI devices you have.
You can then refer to them as
.B /dev/sga,
.B /dev/sgb,
etc. by the order they
are reported.
.P
Under FreeBSD,
.B camcontrol devlist
will tell you what SCSI devices you
have, along with which
.B pass
device controls them.
.P
Under Solaris 7 and 8,
.B /usr/sbin/devfsadm -C
will clean up your /devices directory. Then
.B find /devices -name 'st@*' -print
will return a list of all tape drives. /dev on Solaris is apparently only
of historical interest.
.SH BUGS AND LIMITATIONS
There are no known bugs or limitations.
.SH AVAILABILITY
This version of
.B scsieject
is currently being maintained by Robert Nelson <robertnelson@users.sourceforge.net>
as part of the 'mtx' suite of programs. The 'mtx' home page is
http://mtx.sourceforge.net and the actual code is currently available there and via
SVN from http://sourceforge.net/projects/mtx.
.SH SEE ALSO
.BR loaderinfo (1), tapeinfo (1), mtx (1)

255
mtx-1.3.12/scsieject.c Normal file
View File

@@ -0,0 +1,255 @@
/* Copyright 2007-2008, Robert Nelson <robertn@the-nelsons.org>
* Released under terms of the GNU General Public License as
* required by the license on 'mtxl.c'.
* $Date: 2007-01-28 19:23:33 -0800 (Sun, 28 Jan 2007) $
* $Revision: 125 $
*/
/* This is a generic SCSI device control program. It operates by
* directly sending commands to the device.
*/
/*
* Commands:
* load -- Load medium
* unload -- Unload medium
* start -- Start device
* stop -- Stop device
* lock -- Lock medium
* unlock -- Unlock medium
*/
#include <stdio.h>
#include <string.h>
#include "mtx.h"
#include "mtxl.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef _MSC_VER
#include <io.h>
#endif
char *argv0;
/* the device handle we're operating upon. */
static char *device; /* the device name. */
static DEVICE_TYPE DeviceFD = (DEVICE_TYPE) -1;
static int S_load(void);
static int S_unload(void);
static int S_start(void);
static int S_stop(void);
static int S_lock(void);
static int S_unlock(void);
struct command_table_struct
{
char *name;
int (*command)(void);
}
command_table[] =
{
{ "load", S_load },
{ "unload", S_unload },
{ "start", S_start },
{ "stop", S_stop },
{ "lock", S_lock },
{ "unlock", S_unlock },
{ NULL, NULL } /* terminate list */
};
void Usage(void)
{
FatalError("Usage: scsieject -f <generic-device> <command> where <command> is:\n load | unload | start | stop | lock | unlock\n");
}
/* open_device() -- set the 'DeviceFD' variable.... */
void open_device(void)
{
if (DeviceFD != -1)
{
SCSI_CloseDevice("Unknown", DeviceFD);
}
DeviceFD = SCSI_OpenDevice(device);
}
/* we see if we've got a file open. If not, we open one :-(. Then
* we execute the actual command. Or not :-(.
*/
int execute_command(struct command_table_struct *command)
{
/*
* If the device is not already open, then open it from the
* environment.
*/
if (DeviceFD == -1)
{
/* try to get it from STAPE or TAPE environment variable... */
if ((device = getenv("STAPE")) == NULL &&
(device = getenv("TAPE")) == NULL)
{
Usage(); /* Doesn't return */
}
open_device();
}
/* okay, now to execute the command... */
return command->command();
}
/* parse_args():
* Basically, we are parsing argv/argc. We can have multiple commands
* on a line, such as "load start" to load a tape and start the device.
* We execute these commands one at a time as we come to them. If we don't
* have a -f at the start and the default device isn't defined in a TAPE or
* STAPE environment variable, we exit.
*/
int parse_args(int argc, char **argv)
{
int index, retval;
struct command_table_struct *command;
argv0 = argv[0];
for (index = 1; index < argc; index++)
{
if (strcmp(argv[index], "-f") == 0)
{
index++;
if (index >= argc)
{
Usage(); /* Doesn't return */
}
device = argv[index];
open_device();
}
else
{
for (command = &command_table[0]; command->name != NULL; command++)
{
if (strcmp(command->name, argv[index]) == 0)
{
break;
}
}
if (command->name == NULL)
{
Usage(); /* Doesn't return */
}
retval = execute_command(command);
if (retval < 0)
{
/* Command failed, we probably shouldn't continue */
return retval;
}
}
}
return 0;
}
int S_load(void)
{
int result = LoadUnload(DeviceFD, 1);
if (result < 0)
{
fputs("scsieject: load failed\n", stderr);
fflush(stderr);
}
return result;
}
int S_unload(void)
{
int result = LoadUnload(DeviceFD, 0);
if (result < 0)
{
fputs("scsieject: unload failed\n", stderr);
fflush(stderr);
}
return result;
}
int S_start(void)
{
int result = StartStop(DeviceFD, 1);
if (result < 0)
{
fputs("scsieject: start failed\n", stderr);
fflush(stderr);
}
return result;
}
int S_stop(void)
{
int result = StartStop(DeviceFD, 0);
if (result < 0)
{
fputs("scsieject: stop failed\n", stderr);
fflush(stderr);
}
return result;
}
int S_lock(void)
{
int result = LockUnlock(DeviceFD, 1);
if (result < 0)
{
fputs("scsieject: lock failed\n", stderr);
fflush(stderr);
}
return result;
}
int S_unlock(void)
{
int result = LockUnlock(DeviceFD, 0);
if (result < 0)
{
fputs("scsieject: unlock failed\n", stderr);
fflush(stderr);
}
return result;
}
/* See parse_args for the scoop. parse_args does all. */
int main(int argc, char **argv)
{
parse_args(argc, argv);
if (device)
{
SCSI_CloseDevice(device, DeviceFD);
}
exit(0);
}

179
mtx-1.3.12/scsitape.1 Normal file
View File

@@ -0,0 +1,179 @@
.\" scsitape.1 Document Copyright 2001 Eric Lee Green
.\" Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
.\"
.\" This is free documentation; 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.
.\"
.\" The GNU General Public License's references to "object code"
.\" and "executables" are to be interpreted as the output of any
.\" document formatting or typesetting system, including
.\" intermediate and printed output.
.\"
.\" This manual 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 manual; if not, write to the Free
.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
.\" USA.
.\"
.TH SCSITAPE 1 SCSITAPE1.0
.SH NAME
scsitape \- control SCSI tape devices
.SH SYNOPSIS
scsitape [-f <scsi-generic-device>] commands
.SH DESCRIPTION
The
.B scsitape
command controls SCSI tape drives in a platform-independent
manner. As long as 'mtx' works on the platform, so does 'scsitape'.
.P
Note that 'scsitape' and your OS's native tape driver may stomp on each
other. In particular, if you use 'setblk' and your OS's native tape
driver has a different notion of the block size, you may get evil results.
It is recommended to use 'scsitape' only for software where you've written
your own low-level READ and WRITE routines that use the SCSI command set
to directly talk to tape drives (i.e., you do not use the OS's native tape
driver at all).
.SH OPTIONS
The first argument, given following
.B -f
, is the SCSI generic device corresponding to your tape drive.
Consult your operating system's documentation for more information (for
example, under Linux these are generally /dev/sg0 through /dev/sg15,
under FreeBSD these are /dev/pass0 through /dev/passX. Under Solaris
this is usually the same as your tape drive (Solaris has a SCSI passthrough
ioctl). You can set the STAPE or TAPE environment variable rather
than use -f.
.P
.SH COMMANDS
.TP 10
.B setblk <n>
Set the tape drive's SCSI block size to <n> bytes. (NOTE: if you are
using your OS's native tape driver, THIS IS EVIL!).
.TP 10
.B fsf <n>
Go forward by <n> tapemarks.
.TP 10
.B bsf <n>
Go to immediately previous the <n>th previous tapemark. (WARNING: This
probably doesn't do what you expect -- e.g. if you are immediately
after a tapemark and type 'bfs 1', it moves to immediately *before*
that tape mark, for a sum total of zero effective movement!).
.TP 10
.B eod
Go to end of data.
.TP 10
.B rewind
Rewind the tape drive.
.TP 10
.B eject
Eject the tape currently in the drive.
.TP 10
.B erase
Does a *short* erase (warning: does NOT work on all drives!).
.TP 10
.B mark <n>
write <n> filemarks ( 'mark 0' flushes the drive's buffers ).
.TP 10
.B seek <n>
Seek to a logical position <n> that was reported by a previous 'tapeinfo'
command.
.TP 10
.B write <blocksize>
write blocks from stdin to the tape. Chunk the data into <blocksize>-sized
chunks. *DOES NOT WRITE OUT A TAPEMARK!* (you will need to use a
subsequent
.B mark 1
command to write out a tape mark).
.TP 10
.B read [<blocksize>] [ <#blocks/#bytes> ]
read blocks from the tape, write them to stdout. If we are in variable
block mode, <blocksize> should be zero (note: The maximum block size
we currently support in variable block mode is 128K, MAX_READ_SIZE will
need to be turned into a settable variable to allow bigger reads). If
<blocksize> is ommitted, we assume that we're in variable block mode, and
that we are going to read from tape until we hit a tapemark or end of
partition or end of tape.
.SH AUTHORS
This program was written by Eric Lee Green <eric@badtux.org>.
Major portions of the 'mtxl.c' library used herein were written by
Leonard Zubkoff.
.P
The SCSI read and write routines are based upon those that Richard
Fish wrote for Enhanced Software Technology's BRU 16.1 product,
substantially modified to work in our particular environment (in
particular, all the variable block stuff is new since BRU only does
fixed block reads and writes, and the BRU code uses bitmasks rather
than bitfields for the various flags and such in return values, as
well as the BRU code having a different SCSI API and having variable
names considerably shorter than the rather sesquipedalian 'mtx'
identifiers). As required by 'mtxl.c', these routines are licensed
under the GNU General Public License.
.SH HINTS
Under Linux,
.B cat /proc/scsi/scsi
will tell you what SCSI devices you have.
You can then refer to them as
.B /dev/sga,
.B /dev/sgb,
etc. by the order they
are reported.
.P
Under FreeBSD,
.B camcontrol devlist
will tell you what SCSI devices you
have, along with which
.B pass
device controls them.
.P
Under Solaris 7 and 8,
.B /usr/sbin/devfsadm -C
will clean up your /devices directory. Then
.B find /devices -name 'st@*' -print
will return a list of all tape drives. /dev on Solaris is apparently only
of historical interest.
.SH BUGS AND LIMITATIONS
for
.B scsitape read 0 <n>
where you are doing variable-block-size reads and wish for <n> bytes,
it instead reads one and exactly one block from tape and prints that
(no matter what its size). Use 'dd' on the output of scsitape if you
want finer control.
.P
.B scsitape read 0
attempts reads of MAX_READ_SIZE, which is currently 128K. If blocks on tape
are larger than 128K, only the first 128K will be read -- the remainder
will be silently dumped in the toilet.
.P
This program does not interact well (or at all :-) with your OS's
native tape driver. You will likely see weird things happen if you
attempt to intermingle scsitape commands with native tape driver
operations. Note that BRU 16.1 for Solaris (and possibly others, but
Solaris I know about) will have a 'scsi' keyword to bypass the
native tape driver and write via direct uscsi commands, so if you use
\'scsitape\' to bypass the flaws of the native Solaris driver, you can use
BRU 16.1 to write your actual tape archives. (Assuming that BRU 16.1
has been released at the time that you read this).
.SH AVAILABILITY
This version of
.B scsitape
is currently being maintained by Robert Nelson <robertnelson@users.sourceforge.net>
as part of the 'mtx' suite of programs. The 'mtx' home page is
http://mtx.sourceforge.net and the actual code is currently available there and via
SVN from http://sourceforge.net/projects/mtx.
.SH SEE ALSO
.BR loaderinfo (1), tapeinfo (1), mtx (1)

941
mtx-1.3.12/scsitape.c Normal file
View File

@@ -0,0 +1,941 @@
/* Copyright 2001 Enhanced Software Technologies Inc.
* Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
* Released under terms of the GNU General Public License as
* required by the license on 'mtxl.c'.
* $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
* $Revision: 193 $
*/
/* This is a generic SCSI tape control program. It operates by
* directly sending commands to the tape drive. If you are going
* through your operating system's SCSI tape driver, do *NOT* use
* this program! If, on the other hand, you are using raw READ and WRITE
* commands through your operating system's generic SCSI interface (or
* through our built-in 'read' and 'write'), this is the place for you.
*/
/*#define DEBUG_PARTITION */
/*#define DEBUG 1 */
/*
Commands:
setblk <n> -- set the block size to <n>
fsf <n> -- go forward by <n> filemarks
bsf <n> -- go backward by <n> filemarks
eod -- go to end of data
rewind -- rewind back to start of data
eject -- rewind, then eject the tape.
erase -- (short) erase the tape (we have no long erase)
mark <n> -- write <n> filemarks.
seek <n> -- seek to position <n>.
write <blksize> <-- write blocks from stdin to the tape
read [<blksize>] [<#blocks/#bytes>] -- read blocks from tape, write to stdout.
See the 'tapeinfo' program for status info about the tape drive.
*/
#include <stdio.h>
#include <string.h>
#include "mtx.h"
#include "mtxl.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#if HAVE_SYS_MTIO_H
#include <sys/mtio.h> /* will try issuing some ioctls for Solaris, sigh. */
#endif
#ifdef _MSC_VER
#include <io.h>
#endif
void Usage(void) {
FatalError("Usage: scsitape -f <generic-device> <command> where <command> is:\n setblk <n> | fsf <n> | bsf <n> | eod | rewind | eject | mark <n> |\n seek <n> | read [<blksize> [<numblocks]] | write [<blocksize>] \n");
}
#define arg1 (arg[0]) /* for backward compatibility, sigh */
static int arg[4]; /* the argument for the command, sigh. */
/* the device handle we're operating upon, sigh. */
static char *device; /* the text of the device thingy. */
static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) -1;
static int S_setblk(void);
static int S_fsf(void);
static int S_bsf(void);
static int S_eod(void);
static int S_rewind(void);
static int S_eject(void);
static int S_mark(void);
static int S_seek(void);
static int S_reten(void);
static int S_erase(void);
static int S_read(void);
static int S_write(void);
struct command_table_struct {
int num_args;
char *name;
int (*command)(void);
} command_table[] = {
{ 1, "setblk", S_setblk },
{ 1, "fsf", S_fsf },
{ 1, "bsf", S_bsf },
{ 0, "eod", S_eod },
{ 0, "rewind", S_rewind },
{ 0, "eject", S_eject },
{ 0, "reten", S_reten },
{ 0, "erase", S_erase },
{ 1, "mark", S_mark },
{ 1, "seek", S_seek },
{ 2, "read", S_read },
{ 2, "write",S_write },
{ 0, NULL, NULL } /* terminate list */
};
char *argv0;
/* open_device() -- set the 'fh' variable.... */
void open_device(void)
{
if (MediumChangerFD != -1)
{
SCSI_CloseDevice("Unknown",MediumChangerFD); /* close it, sigh... new device now! */
}
MediumChangerFD = SCSI_OpenDevice(device);
}
static int get_arg(char *arg)
{
int retval=-1;
if (*arg < '0' || *arg > '9')
{
return -1; /* sorry! */
}
retval=atoi(arg);
return retval;
}
/* we see if we've got a file open. If not, we open one :-(. Then
* we execute the actual command. Or not :-(.
*/
int execute_command(struct command_table_struct *command)
{
/* if the device is not already open, then open it from the
* environment.
*/
if (!MediumChangerFD == -1)
{
/* try to get it from STAPE or TAPE environment variable... */
device = getenv("STAPE");
if (device == NULL)
{
device = getenv("TAPE");
if (device == NULL)
{
Usage();
}
}
open_device();
}
/* okay, now to execute the command... */
return command->command();
}
/* parse_args():
* Basically, we are parsing argv/argc. We can have multiple commands
* on a line now, such as "unload 3 0 load 4 0" to unload one tape and
* load in another tape into drive 0, and we execute these commands one
* at a time as we come to them. If we don't have a -f at the start, we
* barf. If we leave out a drive #, we default to drive 0 (the first drive
* in the cabinet).
*/
int parse_args(int argc, char **argv)
{
int i, cmd_tbl_idx,retval,arg_idx;
struct command_table_struct *command;
i=1;
arg_idx = 0;
while (i < argc)
{
if (strcmp(argv[i],"-f") == 0)
{
i++;
if (i >= argc)
{
Usage();
}
device = argv[i++];
open_device(); /* open the device and do a status scan on it... */
}
else
{
cmd_tbl_idx=0;
command = &command_table[0]; /* default to the first command... */
command = &command_table[cmd_tbl_idx];
while (command->name)
{
if (strcmp(command->name,argv[i]) == 0)
{
/* we have a match... */
break;
}
/* otherwise we don't have a match... */
cmd_tbl_idx++;
command = &command_table[cmd_tbl_idx];
}
/* if it's not a command, exit.... */
if (command->name == NULL)
{
Usage();
}
i++; /* go to the next argument, if possible... */
/* see if we need to gather arguments, though! */
arg1 = -1; /* default it to something */
for (arg_idx=0;arg_idx < command->num_args ; arg_idx++)
{
if (i < argc)
{
arg[arg_idx] = get_arg(argv[i]);
if (arg[arg_idx] != -1)
{
i++; /* increment i over the next cmd. */
}
}
else
{
arg[arg_idx] = 0; /* default to 0 setmarks or whatever */
}
}
retval=execute_command(command); /* execute_command handles 'stuff' */
exit(retval);
}
}
return 0; /* should never get here */
}
/* For Linux, this allows us to do a short erase on a tape (sigh!).
* Note that you'll need to do a 'mt status' on the tape afterwards in
* order to get the tape driver in sync with the tape drive again. Also
* note that on other OS's, this might do other evil things to the tape
* driver. Note that to do an erase, you must first rewind!
*/
static int S_erase(void)
{
int retval;
RequestSense_T *RequestSense;
retval=S_rewind();
if (retval)
{
return retval; /* we have an exit status :-(. */
}
RequestSense=Erase(MediumChangerFD);
if (RequestSense)
{
PrintRequestSense(RequestSense);
exit(1); /* exit with an error status. */
}
return 0;
}
/* This should eject a tape or magazine, depending upon the device sent
* to.
*/
static int S_eject(void)
{
int i;
i = LoadUnload(MediumChangerFD, 0);
if ( i < 0)
{
fprintf(stderr,"scsitape:eject failed\n");
fflush(stderr);
}
return i;
}
/* We write a filemarks of 0 before going to grab position, in order
* to insure that data in the buffer is not a problem.
*/
static int S_mark(void)
{
RequestSense_T RequestSense; /* for result of ReadElementStatus */
CDB_T CDB;
unsigned char buffer[6];
int count = arg1; /* voila! */
CDB[0] = 0x10; /* SET_MARK */
CDB[1] = 0;
CDB[2] = (unsigned char)(count >> 16);
CDB[3] = (unsigned char)(count >> 8);
CDB[4] = (unsigned char)count;
CDB[5] = 0;
/* we really don't care if this command works or not, sigh. */
slow_bzero((char *)&RequestSense, sizeof(RequestSense_T));
if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, buffer, 0, &RequestSense)!= 0)
{
PrintRequestSense(&RequestSense);
return 1;
}
return 0;
}
/* let's rewind to bod!
*/
static int S_rewind(void)
{
RequestSense_T sense;
CDB_T CDB;
unsigned char buffer[6];
CDB[0] = 0x01; /* REWIND */
CDB[1] = 0;
CDB[2] = 0;
CDB[3] = 0;
CDB[4] = 0;
CDB[5] = 0;
/* we really don't care if this command works or not, sigh. */
slow_bzero((char *)&sense,sizeof(RequestSense_T));
if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&sense)!=0)
{
PrintRequestSense(&sense);
return 1;
}
return 0;
}
/* This is used for fsf and bsf. */
static int Space(int count, char code)
{
RequestSense_T sense;
CDB_T CDB;
unsigned char buffer[6];
CDB[0] = 0x11; /* SET_MARK */
CDB[1] = code;
CDB[2] = (unsigned char)(count >> 16);
CDB[3] = (unsigned char)(count >> 8);
CDB[4] = (unsigned char)count;
CDB[5] = 0;
/* we really don't care if this command works or not, sigh. */
slow_bzero((char *)&sense,sizeof(RequestSense_T));
if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, buffer, 0, &sense) != 0)
{
PrintRequestSense(&sense);
return 1;
}
return 0;
}
/* Let's try a fsf: */
/* We write a filemarks of 0 before going to grab position, in order
* to insure that data in the buffer is not a problem.
*/
static int S_fsf(void)
{
return Space(arg1,1); /* go forward! */
}
static int S_bsf(void)
{
return Space(-arg1,1); /* go backward! */
}
static int S_eod(void)
{
return Space(0,3); /* go to eod! */
}
/* sigh, abuse of the LOAD command...
*/
static int S_reten(void)
{
RequestSense_T sense;
CDB_T CDB;
unsigned char buffer[6];
CDB[0] = 0x1B; /* START_STOP */
CDB[1] = 0; /* wait */
CDB[2] = 0;
CDB[3] = 0;
CDB[4] = 3; /* reten. */
CDB[5] = 0;
/* we really don't care if this command works or not, sigh. */
slow_bzero((char *)&sense, sizeof(RequestSense_T));
if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, buffer, 0, &sense) != 0)
{
PrintRequestSense(&sense);
return 1;
}
return 0;
}
/* seek a position on the tape (sigh!) */
static int S_seek(void)
{
RequestSense_T sense;
CDB_T CDB;
unsigned char buffer[6];
int count = arg1;
/* printf("count=%d\n",arg1); */
CDB[0] = 0x2B; /* LOCATE */
CDB[1] = 0; /* Logical */
CDB[2] = 0; /* padding */
CDB[3] = (unsigned char)(count >> 24);
CDB[4] = (unsigned char)(count >> 16);
CDB[5] = (unsigned char)(count >> 8);
CDB[6] = (unsigned char)count;
CDB[7] = 0;
CDB[8] = 0;
CDB[9] = 0;
/* we really don't care if this command works or not, sigh. */
slow_bzero((char *)&sense,sizeof(RequestSense_T));
if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10, buffer, 0, &sense) != 0)
{
PrintRequestSense(&sense);
return 1;
}
return 0;
}
#ifdef MTSRSZ
static int Solaris_setblk(int fh,int count)
{
/* we get here only if we have a MTSRSZ, which means Solaris. */
struct mtop mt_com; /* the struct used for the MTIOCTOP ioctl */
int result;
/* okay, we have fh and count.... */
/* Now to try the ioctl: */
mt_com.mt_op=MTSRSZ;
mt_com.mt_count=count;
/* surround the actual ioctl to enable threading, since fsf/etc. can be
* big time consumers and we want other threads to be able to run too.
*/
result=ioctl(fh, MTIOCTOP, (char *)&mt_com);
if (result < 0)
{
return errno;
}
/* okay, we did okay. Return a value of None... */
return 0;
}
#endif
/* okay, this is a write: we need to set the block size to something: */
static int S_setblk(void)
{
RequestSense_T sense;
CDB_T CDB;
char buffer[12];
unsigned int count = (unsigned int) arg1;
CDB[0] = 0x15; /* MODE SELECT */
CDB[1] = 0x10; /* scsi2 */
CDB[2] = 0;
CDB[3] = 0;
CDB[4] = 12; /* length of data */
CDB[5] = 0;
slow_bzero((char *)&sense, sizeof(RequestSense_T));
slow_bzero(buffer, 12);
/* Now to set the mode page header: */
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0x10; /* we are in buffered mode now, people! */
buffer[3] = 8; /* block descriptor length. */
buffer[4] = 0; /* reset to default density, sigh. */ /* 0 */
buffer[5] = 0; /* 1 */
buffer[6] = 0; /* 2 */
buffer[7] = 0; /* 3 */
buffer[8] = 0; /* 4 */
buffer[9] = (unsigned char)(count >> 16); /* 5 */
buffer[10] = (unsigned char)(count >> 8); /* 6 */
buffer[11] = (unsigned char)count; /* 7 */
if (SCSI_ExecuteCommand(MediumChangerFD,Output,&CDB,6,buffer,12,&sense)!=0)
{
PrintRequestSense(&sense);
return 1;
}
#ifdef MTSRSZ
/* Solaris_setblk(MediumChangerFD,count); */
#endif
return 0;
}
/*************************************************************************/
/* SCSI read/write calls. These are mostly pulled out of BRU 16.1,
* modified to work within the mtxl.h framework rather than the
* scsi_lowlevel.h framework.
*************************************************************************/
#define MAX_READ_SIZE 128*1024 /* max size of a variable-block read */
#define READ_OK 0
#define READ_FILEMARK 1
#define READ_EOD 2
#define READ_EOP 3
#define READ_SHORT 5
#define READ_ERROR 255
#define WRITE_OK 0
#define WRITE_ERROR 1
#define WRITE_EOM 2
#define WRITE_EOV 3
/* These are copied out of BRU 16.1, with all the boolean masks changed
* to our bitmasks.
*/
#define S_NO_SENSE(s) ((s).SenseKey == 0x0)
#define S_RECOVERED_ERROR(s) ((s).SenseKey == 0x1)
#define S_NOT_READY(s) ((s).SenseKey == 0x2)
#define S_MEDIUM_ERROR(s) ((s).SenseKey == 0x3)
#define S_HARDWARE_ERROR(s) ((s).SenseKey == 0x4)
#define S_UNIT_ATTENTION(s) ((s).SenseKey == 0x6)
#define S_BLANK_CHECK(s) ((s).SenseKey == 0x8)
#define S_VOLUME_OVERFLOW(s) ((s).SenseKey == 0xd)
#define DEFAULT_TIMEOUT 3*60 /* 3 minutes here */
#define HIT_FILEMARK(s) (S_NO_SENSE((s)) && (s).Filemark && (s).Valid)
/* Sigh, the T-10 SSC spec says all of the following is needed to
* detect a short read while in variable block mode. We'll see.
*/
#define SHORT_READ(s) (S_NO_SENSE((s)) && (s).ILI && (s).Valid && (s).AdditionalSenseCode==0 && (s).AdditionalSenseCodeQualifier==0)
#define HIT_EOD(s) (S_BLANK_CHECK((s)) && (s).Valid)
#define HIT_EOP(s) (S_MEDIUM_ERROR((s)) && (s).EOM && (s).Valid)
#define HIT_EOM(s) ((s).EOM && (s).Valid)
#define BECOMING_READY(s) (S_UNIT_ATTENTION((s)) && (s).AdditionalSenseCode == 0x28 && (s).AdditionalSenseCodeQualifier == 0)
/* Reading is a problem. We can hit a filemark, hit an EOD, or hit an
* EOP. Our caller may do something about that. Note that we assume that
* our caller has already put us into fixed block mode. If he has not, then
* we are in trouble anyhow.
*/
int SCSI_readt(DEVICE_TYPE fd, char * buf, unsigned int bufsize, unsigned int *len, unsigned int timeout) {
int rtnval;
CDB_T cmd;
int blockCount;
int info;
RequestSense_T RequestSense;
if (bufsize==0)
{
/* we are in variable block mode */
blockCount=MAX_READ_SIZE; /* variable block size. */
}
else
{
blockCount= *len / bufsize;
if ((*len % bufsize) != 0)
{
fprintf(stderr,"Error: Data (%d bytes) not even multiple of block size (%d bytes).\n",*len,bufsize);
exit(1); /* we're finished, sigh. */
}
}
if (timeout == 0)
{
timeout = 1 * 60; /* 1 minutes */
}
memset(&cmd, 0, sizeof(CDB_T));
cmd[0] = 0x08; /* READ */
cmd[1] = (bufsize) ? 1 : 0; /* fixed length or var length blocks */
cmd[2] = (unsigned char)(blockCount >> 16); /* MSB */
cmd[3] = (unsigned char)(blockCount >> 8);
cmd[4] = (unsigned char)blockCount; /* LSB */
/* okay, let's read, look @ the result code: */
rtnval=READ_OK;
if (SCSI_ExecuteCommand(fd,Input,&cmd,6,buf,(bufsize) ? *len : MAX_READ_SIZE,&RequestSense))
{
rtnval=READ_ERROR;
if (HIT_EOP(RequestSense))
{
cmd[0]=0x08;
rtnval=READ_EOP;
}
if (HIT_FILEMARK(RequestSense))
{
rtnval=READ_FILEMARK;
}
if (HIT_EOD(RequestSense))
{
rtnval=READ_EOD;
}
if ( (bufsize==0) && SHORT_READ(RequestSense))
{
rtnval=READ_SHORT; /* we only do short reads for variable block mode */
}
if (rtnval != READ_ERROR)
{
/* info contains number of blocks or bytes *not* read. May be
negative if the block we were trying to read was too big. So
we will have to account for that and set it to zero if so, so that
we return the proper # of blocks read.
*/
info = ((RequestSense.Information[0] << 24) +
(RequestSense.Information[1] << 16) +
(RequestSense.Information[2] << 8) +
RequestSense.Information[3]);
/* on 64-bit platforms, we may need to turn 'info' into a negative # */
if (info > 0x7fffffff)
info = 0;
if (info < 0)
info=0; /* make sure we don't return too big len read. */
/* Now set *len to # of bytes read. */
*len= bufsize ? (blockCount-info) * bufsize : MAX_READ_SIZE-info ;
}
else
{
PrintRequestSense(&RequestSense);
exit(1); /* foo. */
}
}
return rtnval;
}
/* Low level SCSI write. Modified from BRU 16.1, with much BRU smarts
* taken out and with the various types changed to mtx types rather than
* BRU types.
*/
int SCSI_write(DEVICE_TYPE fd, char * buf, unsigned int blocksize,
unsigned int *len)
{
CDB_T cmd;
int blockCount;
int rtnval=0;
RequestSense_T RequestSense;
if (blocksize == 0)
{
/* we are in variable block mode */
blockCount = *len; /* variable block size. */
}
else
{
blockCount= *len / blocksize ;
if ((*len % blocksize) != 0)
{
fprintf(stderr,"Error: Data (%d bytes) not even multiple of block size (%d bytes).\n",*len,blocksize);
exit(1); /* we're finished, sigh. */
}
}
fprintf(stderr,"Writing %d blocks\n",blockCount);
memset(&cmd, 0, sizeof(CDB_T));
cmd[0] = 0x0a; /* WRITE */
cmd[1] = (blocksize) ? 1 : 0; /* fixed length or var length blocks */
cmd[2] = (unsigned char)(blockCount >> 16); /* MSB */
cmd[3] = (unsigned char)(blockCount >> 8);
cmd[4] = (unsigned char)blockCount; /* LSB */
if (SCSI_ExecuteCommand(fd,Output,&cmd,6,buf, *len, &RequestSense))
{
if (HIT_EOM(RequestSense))
{
/* we hit end of media. Return -1. */
if (S_VOLUME_OVERFLOW(RequestSense))
{
exit(WRITE_EOV);
}
exit(WRITE_EOM); /* end of media! */
}
else
{
/* it was plain old write error: */
PrintRequestSense(&RequestSense);
exit(WRITE_ERROR);
}
}
else
{
rtnval = *len; /* worked! */
}
return rtnval;
}
/* S_write is not implemented yet! */
static int S_write(void)
{
char *buffer; /* the buffer we're gonna read/write out of. */
int buffersize;
int len; /* the length of the data in the buffer */
int blocksize = arg[0];
int numblocks = arg[1];
int varsize=0; /* variable size block flag */
int result;
int eof_input;
int infile=fileno(stdin); /* sigh */
if (blocksize == 0)
{
varsize = 1;
buffersize = MAX_READ_SIZE;
len = MAX_READ_SIZE;
}
else
{
varsize = 0; /* fixed block mode */
buffersize = blocksize;
len = blocksize;
}
/* sigh, make it oversized just to have some */
buffer = malloc(buffersize+8);
eof_input = 0;
while (!eof_input)
{
/* size_t could be 64 bit on a 32 bit platform, so do casts. */
len=0;
/* If it is a pipe, we could read 4096 bytes rather than the full
* 128K bytes or whatever, so we must gather multiple reads into
* the buffer.
*/
while (len < buffersize)
{
result=(int)read(infile, buffer + len, (size_t)(buffersize - len));
if (!result)
{
eof_input = 1;
if (!len)
{
/* if we have no deata in our buffer, exit */
return 0; /* we're at end of file! */
}
break; /* otherwise, break and write the data */
}
len += result; /* add the result input to our length. */
}
result = SCSI_write(MediumChangerFD, buffer, blocksize, (unsigned int *)&len);
if (!result)
{
return 1; /* at end of tape! */
}
/* Now see if we have numbytes or numblocks. If so, we may wish to exit
this loop.
*/
if (arg[1])
{
if (varsize)
{
/***BUG***/
return 0; /* we will only write one block in variable size mode :-( */
}
else
{
if (numblocks)
{
numblocks--;
}
else
{
return 0; /* we're done. */
}
}
}
}
/* and done! */
return 0;
}
/* Okay, the read thingy: */
/* We have a device opened (we hope!) by the parser.
* we will have arg[0] and arg[1] being the blocksize and # of blocks
* (respectively).
*/
static int S_read(void)
{
char *buffer; /* the buffer we're going to be reading out of */
int buffersize;
unsigned int len; /* the length of the data in the buffer */
int blocksize = arg[0];
int numblocks = arg[1];
int varsize = 0; /* variable size block flag. */
int result;
int outfile=fileno(stdout); /* sigh. */
if (blocksize == 0)
{
varsize=1;
buffersize=MAX_READ_SIZE;
len=MAX_READ_SIZE;
}
else
{
varsize=0; /* fixed block mode */
buffersize=blocksize;
len=blocksize;
}
/* sigh, make it oversized just to have some */
buffer = malloc(buffersize + 8);
for ( ; ; )
{
if (varsize)
{
/* it could have gotten reset by prior short read... */
len=MAX_READ_SIZE;
}
result=SCSI_readt(MediumChangerFD,buffer,blocksize, &len, DEFAULT_TIMEOUT);
if (result==READ_FILEMARK || result==READ_EOD || result==READ_EOP)
{
/* okay, normal end of file? */
if (len > 0)
{
write(outfile,buffer,len);
}
#ifdef NEED_TO_GO_PAST_FILEMARK
/* Now, let's try to go past the filemark if that's what we hit: */
if (result==READ_FILEMARK)
{
arg1 = 1; /* arg for S_fsf. */
S_fsf(); /* and go forward 1 filemark, we hope! */
}
#endif
return 0; /* hit normal end of file. */
}
else if (result == READ_SHORT)
{
/* short reads are only valid in variable block mode. */
if (varsize)
{
if (len > 0)
{
write(outfile,buffer,len);
}
}
else
{
fprintf(stderr,"scsitape:Short Read encountered on input. Aborting.\n");
fflush(stderr);
exit(1); /* error exit! */
}
}
else if (result == READ_OK)
{
write(outfile,buffer,len);
}
else
{
fprintf(stderr,"scsitape:Read Error\n");
fflush(stderr);
exit(1);
}
/* Now see if we have numbytes or numblocks: if so, we may wish to
* exit this loop.
*/
if (arg[1])
{
if (varsize)
{
/****BUG****/
return 0; /* we're only reading one block in var size mode! */
}
else if (numblocks)
{
numblocks--;
}
else
{
return 0; /* we're done. */
}
}
}
}
/* See parse_args for the scoop. parse_args does all. */
int main(int argc, char **argv)
{
argv0 = argv[0];
parse_args(argc, argv);
if (device)
SCSI_CloseDevice(device,MediumChangerFD);
exit(0);
}

645
mtx-1.3.12/sg_err.c Normal file
View File

@@ -0,0 +1,645 @@
/* This file is a huge cut, paste and hack from linux/drivers/scsi/constant.c
* which I guess was written by:
* Copyright (C) 1993, 1994, 1995 Eric Youngdale
* The rest of this is:
* Copyright (C) 1999 - 2001 D. Gilbert
* Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
*
* 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, or (at your option)
* any later version.
*
* ASCII values for a number of symbolic constants, printing functions, etc.
*
* Some of the tables have been updated for SCSI 2.
*
* Version 0.84 (20010115)
* Change output from stdout to stderr
*/
#define OUTP stderr
static const unsigned char scsi_command_size[8] = { 6, 10, 10, 12,
12, 12, 10, 10 };
#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
static const char unknown[] = "UNKNOWN";
static const char * group_0_commands[] = {
/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
/* 13-16 */ "Verify", "Recover Buffered Data", "Mode Select", "Reserve",
/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic",
/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
};
static const char *group_1_commands[] = {
/* 20-22 */ unknown, unknown, unknown,
/* 23-28 */ unknown, "Define window parameters", "Read Capacity",
unknown, unknown, "Read (10)",
/* 29-2d */ "Read Generation", "Write (10)", "Seek (10)", "Erase",
"Read updated block",
/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position",
/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data",
/* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer",
"Read Buffer",
/* 3d-3f */ "Update Block", "Read Long", "Write Long",
};
static const char *group_2_commands[] = {
/* 40-41 */ "Change Definition", "Write Same",
/* 42-48 */ "Read sub-channel", "Read TOC", "Read header",
"Play audio (10)", unknown, "Play audio msf",
"Play audio track/index",
/* 49-4f */ "Play track relative (10)", unknown, "Pause/resume",
"Log Select", "Log Sense", unknown, unknown,
/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
/* 5c-5f */ unknown, unknown, unknown,
};
/* The following are 12 byte commands in group 5 */
static const char *group_5_commands[] = {
/* a0-a5 */ unknown, unknown, unknown, unknown, unknown,
"Move medium/play audio(12)",
/* a6-a9 */ "Exchange medium", unknown, "Read(12)", "Play track relative(12)",
/* aa-ae */ "Write(12)", unknown, "Erase(12)", unknown,
"Write and verify(12)",
/* af-b1 */ "Verify(12)", "Search data high(12)", "Search data equal(12)",
/* b2-b4 */ "Search data low(12)", "Set limits(12)", unknown,
/* b5-b6 */ "Request volume element address", "Send volume tag",
/* b7-b9 */ "Read defect data(12)", "Read element status", unknown,
/* ba-bf */ unknown, unknown, unknown, unknown, unknown, unknown,
};
#define group(opcode) (((opcode) >> 5) & 7)
#define RESERVED_GROUP 0
#define VENDOR_GROUP 1
static const char **commands[] = {
group_0_commands, group_1_commands, group_2_commands,
(const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP,
group_5_commands, (const char **) VENDOR_GROUP,
(const char **) VENDOR_GROUP
};
static const char reserved[] = "RESERVED";
static const char vendor[] = "VENDOR SPECIFIC";
static void print_opcode(int opcode) {
const char **table = commands[ group(opcode) ];
switch ((unsigned long) table) {
case RESERVED_GROUP:
fprintf(OUTP, "%s(0x%02x) ", reserved, opcode);
break;
case VENDOR_GROUP:
fprintf(OUTP, "%s(0x%02x) ", vendor, opcode);
break;
default:
if (table[opcode & 0x1f] != unknown)
fprintf(OUTP, "%s ",table[opcode & 0x1f]);
else
fprintf(OUTP, "%s(0x%02x) ", unknown, opcode);
break;
}
}
void sg_print_command (const unsigned char * command) {
int i,s;
print_opcode(command[0]);
for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
fprintf(OUTP, "%02x ", command[i]);
fprintf(OUTP, "\n");
}
static const char * statuses[] = {
/* 0-4 */ "Good", "Check Condition", "Condition Met", unknown, "Busy",
/* 5-9 */ unknown, unknown, unknown, "Intermediate", unknown,
/* a-c */ "Intermediate-Condition Met", unknown, "Reservation Conflict",
/* d-10 */ unknown, unknown, unknown, unknown,
/* 11-14 */ "Command Terminated", unknown, unknown, "Queue Full",
/* 15-1a */ unknown, unknown, unknown, unknown, unknown, unknown,
/* 1b-1f */ unknown, unknown, unknown, unknown, unknown,
};
void sg_print_status (int masked_status) {
/* status = (status >> 1) & 0xf; */ /* already done */
fprintf(OUTP, "%s ",statuses[masked_status]);
}
#define D 0x001 /* DIRECT ACCESS DEVICE (disk) */
#define T 0x002 /* SEQUENTIAL ACCESS DEVICE (tape) */
#define L 0x004 /* PRINTER DEVICE */
#define P 0x008 /* PROCESSOR DEVICE */
#define W 0x010 /* WRITE ONCE READ MULTIPLE DEVICE */
#define R 0x020 /* READ ONLY (CD-ROM) DEVICE */
#define S 0x040 /* SCANNER DEVICE */
#define O 0x080 /* OPTICAL MEMORY DEVICE */
#define M 0x100 /* MEDIA CHANGER DEVICE */
#define C 0x200 /* COMMUNICATION DEVICE */
struct error_info{
unsigned char code1, code2;
unsigned short int devices;
const char * text;
};
struct error_info2{
unsigned char code1, code2_min, code2_max;
unsigned short int devices;
const char * text;
};
static struct error_info2 additional2[] =
{
{0x40,0x00,0x7f,D,"Ram failure (%x)"},
{0x40,0x80,0xff,D|T|L|P|W|R|S|O|M|C,"Diagnostic failure on component (%x)"},
{0x41,0x00,0xff,D,"Data path failure (%x)"},
{0x42,0x00,0xff,D,"Power-on or self-test failure (%x)"},
{0, 0, 0, 0, NULL}
};
static struct error_info additional[] =
{
{0x00,0x01,T,"Filemark detected"},
{0x00,0x02,T|S,"End-of-partition/medium detected"},
{0x00,0x03,T,"Setmark detected"},
{0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
{0x00,0x05,T|S,"End-of-data detected"},
{0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
{0x00,0x11,R,"Audio play operation in progress"},
{0x00,0x12,R,"Audio play operation paused"},
{0x00,0x13,R,"Audio play operation successfully completed"},
{0x00,0x14,R,"Audio play operation stopped due to error"},
{0x00,0x15,R,"No current audio status to return"},
{0x01,0x00,D|W|O,"No index/sector signal"},
{0x02,0x00,D|W|R|O|M,"No seek complete"},
{0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
{0x03,0x01,T,"No write current"},
{0x03,0x02,T,"Excessive write errors"},
{0x04,0x00,D|T|L|P|W|R|S|O|M|C,
"Logical unit not ready, cause not reportable"},
{0x04,0x01,D|T|L|P|W|R|S|O|M|C,
"Logical unit is in process of becoming ready"},
{0x04,0x02,D|T|L|P|W|R|S|O|M|C,
"Logical unit not ready, initializing command required"},
{0x04,0x03,D|T|L|P|W|R|S|O|M|C,
"Logical unit not ready, manual intervention required"},
{0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
{0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
{0x06,0x00,D|W|R|O|M,"No reference position found"},
{0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
{0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
{0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
{0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
{0x09,0x00,D|T|W|R|O,"Track following error"},
{0x09,0x01,W|R|O,"Tracking servo failure"},
{0x09,0x02,W|R|O,"Focus servo failure"},
{0x09,0x03,W|R|O,"Spindle servo failure"},
{0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
{0x0C,0x00,T|S,"Write error"},
{0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
{0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
{0x10,0x00,D|W|O,"Id crc or ecc error"},
{0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
{0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
{0x11,0x02,D|T|W|S|O,"Error too long to correct"},
{0x11,0x03,D|T|W|S|O,"Multiple read errors"},
{0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
{0x11,0x05,W|R|O,"L-ec uncorrectable error"},
{0x11,0x06,W|R|O,"Circ unrecovered error"},
{0x11,0x07,W|O,"Data resynchronization error"},
{0x11,0x08,T,"Incomplete block read"},
{0x11,0x09,T,"No gap found"},
{0x11,0x0A,D|T|O,"Miscorrected error"},
{0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
{0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
{0x12,0x00,D|W|O,"Address mark not found for id field"},
{0x13,0x00,D|W|O,"Address mark not found for data field"},
{0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
{0x14,0x01,D|T|W|R|O,"Record not found"},
{0x14,0x02,T,"Filemark or setmark not found"},
{0x14,0x03,T,"End-of-data not found"},
{0x14,0x04,T,"Block sequence error"},
{0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
{0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
{0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
{0x16,0x00,D|W|O,"Data synchronization mark error"},
{0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
{0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
{0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
{0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
{0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
{0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
{0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
{0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
{0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
{0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
{0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
{0x18,0x03,R,"Recovered data with circ"},
{0x18,0x04,R,"Recovered data with lec"},
{0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
{0x19,0x00,D|O,"Defect list error"},
{0x19,0x01,D|O,"Defect list not available"},
{0x19,0x02,D|O,"Defect list error in primary list"},
{0x19,0x03,D|O,"Defect list error in grown list"},
{0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
{0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
{0x1C,0x00,D|O,"Defect list not found"},
{0x1C,0x01,D|O,"Primary defect list not found"},
{0x1C,0x02,D|O,"Grown defect list not found"},
{0x1D,0x00,D|W|O,"Miscompare during verify operation"},
{0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
{0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
{0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
{0x21,0x01,M,"Invalid element address"},
{0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
{0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
{0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
{0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
{0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
{0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
{0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
{0x27,0x00,D|T|W|O,"Write protected"},
{0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
{0x28,0x01,M,"Import or export element accessed"},
{0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
{0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
{0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
{0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
{0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
{0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
{0x2C,0x01,S,"Too many windows specified"},
{0x2C,0x02,S,"Invalid combination of windows specified"},
{0x2D,0x00,T,"Overwrite error on update in place"},
{0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
{0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
{0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
{0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
{0x30,0x03,D|T,"Cleaning cartridge installed"},
{0x31,0x00,D|T|W|O,"Medium format corrupted"},
{0x31,0x01,D|L|O,"Format command failed"},
{0x32,0x00,D|W|O,"No defect spare location available"},
{0x32,0x01,D|W|O,"Defect list update failure"},
{0x33,0x00,T,"Tape length error"},
{0x36,0x00,L,"Ribbon, ink, or toner failure"},
{0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
{0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
{0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
{0x3B,0x00,T|L,"Sequential positioning error"},
{0x3B,0x01,T,"Tape position error at beginning-of-medium"},
{0x3B,0x02,T,"Tape position error at end-of-medium"},
{0x3B,0x03,L,"Tape or electronic vertical forms unit not ready"},
{0x3B,0x04,L,"Slew failure"},
{0x3B,0x05,L,"Paper jam"},
{0x3B,0x06,L,"Failed to sense top-of-form"},
{0x3B,0x07,L,"Failed to sense bottom-of-form"},
{0x3B,0x08,T,"Reposition error"},
{0x3B,0x09,S,"Read past end of medium"},
{0x3B,0x0A,S,"Read past beginning of medium"},
{0x3B,0x0B,S,"Position past end of medium"},
{0x3B,0x0C,S,"Position past beginning of medium"},
{0x3B,0x0D,M,"Medium destination element full"},
{0x3B,0x0E,M,"Medium source element empty"},
{0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
{0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
{0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
{0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
{0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
{0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
{0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
{0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
{0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
{0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
{0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
{0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
{0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
{0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
{0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
{0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
{0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
{0x50,0x00,T,"Write append error"},
{0x50,0x01,T,"Write append position error"},
{0x50,0x02,T,"Position error related to timing"},
{0x51,0x00,T|O,"Erase failure"},
{0x52,0x00,T,"Cartridge fault"},
{0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
{0x53,0x01,T,"Unload tape failure"},
{0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
{0x54,0x00,P,"Scsi to host system interface failure"},
{0x55,0x00,P,"System resource failure"},
{0x57,0x00,R,"Unable to recover table-of-contents"},
{0x58,0x00,O,"Generation does not exist"},
{0x59,0x00,O,"Updated block read"},
{0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
{0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
{0x5A,0x02,D|T|W|O,"Operator selected write protect"},
{0x5A,0x03,D|T|W|O,"Operator selected write permit"},
{0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
{0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
{0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
{0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
{0x5C,0x00,D|O,"Rpl status change"},
{0x5C,0x01,D|O,"Spindles synchronized"},
{0x5C,0x02,D|O,"Spindles not synchronized"},
{0x60,0x00,S,"Lamp failure"},
{0x61,0x00,S,"Video acquisition error"},
{0x61,0x01,S,"Unable to acquire video"},
{0x61,0x02,S,"Out of focus"},
{0x62,0x00,S,"Scan head positioning error"},
{0x63,0x00,R,"End of user area encountered on this track"},
{0x64,0x00,R,"Illegal mode for this track"},
{0, 0, 0, NULL}
};
static const char *snstext[] = {
"None", /* There is no sense information */
"Recovered Error", /* The last command completed successfully
but used error correction */
"Not Ready", /* The addressed target is not ready */
"Medium Error", /* Data error detected on the medium */
"Hardware Error", /* Controller or device failure */
"Illegal Request",
"Unit Attention", /* Removable medium was changed, or
the target has been reset */
"Data Protect", /* Access to the data is blocked */
"Blank Check", /* Reached unexpected written or unwritten
region of the medium */
"Key=9", /* Vendor specific */
"Copy Aborted", /* COPY or COMPARE was aborted */
"Aborted Command", /* The target aborted the command */
"Equal", /* A SEARCH DATA command found data equal */
"Volume Overflow", /* Medium full with still data to be written */
"Miscompare", /* Source data and data on the medium
do not agree */
"Key=15" /* Reserved */
};
/* Print sense information */
void sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
int sb_len)
{
int i, s;
int sense_class, valid, code;
const char * error = NULL;
sense_class = (sense_buffer[0] >> 4) & 0x07;
code = sense_buffer[0] & 0xf;
valid = sense_buffer[0] & 0x80;
if (sense_class == 7) { /* extended sense data */
s = sense_buffer[7] + 8;
if(s > sb_len)
s = sb_len;
if (!valid)
fprintf(OUTP, "[valid=0] ");
fprintf(OUTP, "Info fld=0x%x, ", (int)((sense_buffer[3] << 24) |
(sense_buffer[4] << 16) | (sense_buffer[5] << 8) |
sense_buffer[6]));
if (sense_buffer[2] & 0x80)
fprintf(OUTP, "FMK "); /* current command has read a filemark */
if (sense_buffer[2] & 0x40)
fprintf(OUTP, "EOM "); /* end-of-medium condition exists */
if (sense_buffer[2] & 0x20)
fprintf(OUTP, "ILI "); /* incorrect block length requested */
switch (code) {
case 0x0:
error = "Current"; /* error concerns current command */
break;
case 0x1:
error = "Deferred"; /* error concerns some earlier command */
/* e.g., an earlier write to disk cache succeeded, but
now the disk discovers that it cannot write the data */
break;
default:
error = "Invalid";
}
fprintf(OUTP, "%s ", error);
if (leadin)
fprintf(OUTP, "%s: ", leadin);
fprintf(OUTP, "sense key: %s\n", snstext[sense_buffer[2] & 0x0f]);
/* Check to see if additional sense information is available */
if(sense_buffer[7] + 7 < 13 ||
(sense_buffer[12] == 0 && sense_buffer[13] == 0)) goto done;
for(i=0; additional[i].text; i++)
if(additional[i].code1 == sense_buffer[12] &&
additional[i].code2 == sense_buffer[13])
fprintf(OUTP, "Additional sense indicates: %s\n",
additional[i].text);
for(i=0; additional2[i].text; i++)
if(additional2[i].code1 == sense_buffer[12] &&
additional2[i].code2_min >= sense_buffer[13] &&
additional2[i].code2_max <= sense_buffer[13]) {
fprintf(OUTP, "Additional sense indicates: ");
fprintf(OUTP, additional2[i].text, sense_buffer[13]);
fprintf(OUTP, "\n");
};
} else { /* non-extended sense data */
/*
* Standard says:
* sense_buffer[0] & 0200 : address valid
* sense_buffer[0] & 0177 : vendor-specific error code
* sense_buffer[1] & 0340 : vendor-specific
* sense_buffer[1..3] : 21-bit logical block address
*/
if (leadin)
fprintf(OUTP, "%s: ", leadin);
if (sense_buffer[0] < 15)
fprintf(OUTP,
"old sense: key %s\n", snstext[sense_buffer[0] & 0x0f]);
else
fprintf(OUTP, "sns = %2x %2x\n", sense_buffer[0], sense_buffer[2]);
fprintf(OUTP, "Non-extended sense class %d code 0x%0x ",
sense_class, code);
s = 4;
}
done:
fprintf(OUTP, "Raw sense data (in hex):\n ");
for (i = 0; i < s; ++i) {
if ((i > 0) && (0 == (i % 24)))
fprintf(OUTP, "\n ");
fprintf(OUTP, "%02x ", sense_buffer[i]);
}
fprintf(OUTP, "\n");
return;
}
static const char * hostbyte_table[]={
"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET",
"DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR",
"DID_PASSTHROUGH", "DID_SOFT_ERROR", NULL};
void sg_print_host_status(int host_status)
{ static int maxcode=0;
int i;
if(! maxcode) {
for(i = 0; hostbyte_table[i]; i++) ;
maxcode = i-1;
}
fprintf(OUTP, "Host_status=0x%02x", host_status);
if(host_status > maxcode) {
fprintf(OUTP, "is invalid ");
return;
}
fprintf(OUTP, "(%s) ",hostbyte_table[host_status]);
}
static const char * driverbyte_table[]={
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR",
"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE", NULL};
static const char * driversuggest_table[]={"SUGGEST_OK",
"SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE",
unknown,unknown,unknown, "SUGGEST_SENSE",NULL};
void sg_print_driver_status(int driver_status)
{
static int driver_max =0 , suggest_max=0;
int i;
int dr = driver_status & SG_ERR_DRIVER_MASK;
int su = (driver_status & SG_ERR_SUGGEST_MASK) >> 4;
if(! driver_max) {
for(i = 0; driverbyte_table[i]; i++) ;
driver_max = i;
for(i = 0; driversuggest_table[i]; i++) ;
suggest_max = i;
}
fprintf(OUTP, "Driver_status=0x%02x",driver_status);
fprintf(OUTP, " (%s,%s) ",
dr < driver_max ? driverbyte_table[dr]:"invalid",
su < suggest_max ? driversuggest_table[su]:"invalid");
}
#ifdef SG_IO
int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp)
{
return sg_chk_n_print(leadin, hp->masked_status, hp->host_status,
hp->driver_status, hp->sbp, hp->sb_len_wr);
}
#endif
int sg_chk_n_print(const char * leadin, int masked_status,
int host_status, int driver_status,
const unsigned char * sense_buffer, int sb_len)
{
int done_leadin = 0;
int done_sense = 0;
if ((0 == masked_status) && (0 == host_status) &&
(0 == driver_status))
return 1; /* No problems */
if (0 != masked_status) {
if (leadin)
fprintf(OUTP, "%s: ", leadin);
done_leadin = 1;
sg_print_status(masked_status);
fprintf(OUTP, "\n");
if (sense_buffer && ((masked_status == CHECK_CONDITION) ||
(masked_status == COMMAND_TERMINATED))) {
sg_print_sense(0, sense_buffer, sb_len);
done_sense = 1;
}
}
if (0 != host_status) {
if (leadin && (! done_leadin))
fprintf(OUTP, "%s: ", leadin);
if (done_leadin)
fprintf(OUTP, "plus...: ");
else
done_leadin = 1;
sg_print_host_status(host_status);
fprintf(OUTP, "\n");
}
if (0 != driver_status) {
if (leadin && (! done_leadin))
fprintf(OUTP, "%s: ", leadin);
if (done_leadin)
fprintf(OUTP, "plus...: ");
else
done_leadin = 1;
sg_print_driver_status(driver_status);
fprintf(OUTP, "\n");
if (sense_buffer && (! done_sense) &&
(SG_ERR_DRIVER_SENSE & driver_status))
sg_print_sense(0, sense_buffer, sb_len);
}
return 0;
}
#ifdef SG_IO
int sg_err_category3(struct sg_io_hdr * hp)
{
return sg_err_category(hp->masked_status, hp->host_status,
hp->driver_status, hp->sbp, hp->sb_len_wr);
}
#endif
int sg_err_category(int masked_status, int host_status,
int driver_status, const unsigned char * sense_buffer,
int sb_len)
{
if ((0 == masked_status) && (0 == host_status) &&
(0 == driver_status))
return SG_ERR_CAT_CLEAN;
if ((CHECK_CONDITION == masked_status) ||
(COMMAND_TERMINATED == masked_status) ||
(SG_ERR_DRIVER_SENSE & driver_status)) {
if (sense_buffer && (sb_len > 2)) {
if(RECOVERED_ERROR == sense_buffer[2])
return SG_ERR_CAT_RECOVERED;
else if ((UNIT_ATTENTION == (0x0f & sense_buffer[2])) &&
(sb_len > 12)) {
if (0x28 == sense_buffer[12])
return SG_ERR_CAT_MEDIA_CHANGED;
if (0x29 == sense_buffer[12])
return SG_ERR_CAT_RESET;
}
}
return SG_ERR_CAT_SENSE;
}
if (0 != host_status) {
if ((SG_ERR_DID_NO_CONNECT == host_status) ||
(SG_ERR_DID_BUS_BUSY == host_status) ||
(SG_ERR_DID_TIME_OUT == host_status))
return SG_ERR_CAT_TIMEOUT;
}
if (0 != driver_status) {
if (SG_ERR_DRIVER_TIMEOUT == driver_status)
return SG_ERR_CAT_TIMEOUT;
}
return SG_ERR_CAT_OTHER;
}
int sg_get_command_size(unsigned char opcode)
{
return COMMAND_SIZE(opcode);
}

140
mtx-1.3.12/sg_err.h Normal file
View File

@@ -0,0 +1,140 @@
#ifndef SG_ERR_H
#define SG_ERR_H
/* Feel free to copy and modify this GPL-ed code into your applications. */
/* Version 0.84 (20010115)
- all output now sent to stderr rather thatn stdout
- remove header files included in this file
*/
/* Some of the following error/status codes are exchanged between the
various layers of the SCSI sub-system in Linux and should never
reach the user. They are placed here for completeness. What appears
here is copied from drivers/scsi/scsi.h which is not visible in
the user space. */
/* The following are 'host_status' codes */
#ifndef DID_OK
#define DID_OK 0x00
#endif
#ifndef DID_NO_CONNECT
#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 DID_BAD_TARGET 0x04 /* Bad target (id?) */
#define DID_ABORT 0x05 /* Told to abort for some other reason */
#define DID_PARITY 0x06 /* Parity error (on SCSI bus) */
#define DID_ERROR 0x07 /* Internal error */
#define DID_RESET 0x08 /* Reset by somebody */
#define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */
#define DID_PASSTHROUGH 0x0a /* Force command past mid-level */
#define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */
#endif
/* These defines are to isolate applictaions from kernel define changes */
#define SG_ERR_DID_OK DID_OK
#define SG_ERR_DID_NO_CONNECT DID_NO_CONNECT
#define SG_ERR_DID_BUS_BUSY DID_BUS_BUSY
#define SG_ERR_DID_TIME_OUT DID_TIME_OUT
#define SG_ERR_DID_BAD_TARGET DID_BAD_TARGET
#define SG_ERR_DID_ABORT DID_ABORT
#define SG_ERR_DID_PARITY DID_PARITY
#define SG_ERR_DID_ERROR DID_ERROR
#define SG_ERR_DID_RESET DID_RESET
#define SG_ERR_DID_BAD_INTR DID_BAD_INTR
#define SG_ERR_DID_PASSTHROUGH DID_PASSTHROUGH
#define SG_ERR_DID_SOFT_ERROR DID_SOFT_ERROR
/* The following are 'driver_status' codes */
#ifndef DRIVER_OK
#define DRIVER_OK 0x00
#endif
#ifndef DRIVER_BUSY
#define DRIVER_BUSY 0x01
#define DRIVER_SOFT 0x02
#define DRIVER_MEDIA 0x03
#define DRIVER_ERROR 0x04
#define DRIVER_INVALID 0x05
#define DRIVER_TIMEOUT 0x06
#define DRIVER_HARD 0x07
#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
/* Following "suggests" are "or-ed" with one of previous 8 entries */
#define SUGGEST_RETRY 0x10
#define SUGGEST_ABORT 0x20
#define SUGGEST_REMAP 0x30
#define SUGGEST_DIE 0x40
#define SUGGEST_SENSE 0x80
#define SUGGEST_IS_OK 0xff
#endif
#ifndef DRIVER_MASK
#define DRIVER_MASK 0x0f
#endif
#ifndef SUGGEST_MASK
#define SUGGEST_MASK 0xf0
#endif
/* These defines are to isolate applictaions from kernel define changes */
#define SG_ERR_DRIVER_OK DRIVER_OK
#define SG_ERR_DRIVER_BUSY DRIVER_BUSY
#define SG_ERR_DRIVER_SOFT DRIVER_SOFT
#define SG_ERR_DRIVER_MEDIA DRIVER_MEDIA
#define SG_ERR_DRIVER_ERROR DRIVER_ERROR
#define SG_ERR_DRIVER_INVALID DRIVER_INVALID
#define SG_ERR_DRIVER_TIMEOUT DRIVER_TIMEOUT
#define SG_ERR_DRIVER_HARD DRIVER_HARD
#define SG_ERR_DRIVER_SENSE DRIVER_SENSE
#define SG_ERR_SUGGEST_RETRY SUGGEST_RETRY
#define SG_ERR_SUGGEST_ABORT SUGGEST_ABORT
#define SG_ERR_SUGGEST_REMAP SUGGEST_REMAP
#define SG_ERR_SUGGEST_DIE SUGGEST_DIE
#define SG_ERR_SUGGEST_SENSE SUGGEST_SENSE
#define SG_ERR_SUGGEST_IS_OK SUGGEST_IS_OK
#define SG_ERR_DRIVER_MASK DRIVER_MASK
#define SG_ERR_SUGGEST_MASK SUGGEST_MASK
/* The following "print" functions send ACSII to stdout */
extern void sg_print_command(const unsigned char * command);
extern void sg_print_sense(const char * leadin,
const unsigned char * sense_buffer, int sb_len);
extern void sg_print_status(int masked_status);
extern void sg_print_host_status(int host_status);
extern void sg_print_driver_status(int driver_status);
/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings
else it prints to standard output and returns 0. */
extern int sg_chk_n_print(const char * leadin, int masked_status,
int host_status, int driver_status,
const unsigned char * sense_buffer, int sb_len);
/* The following function declaration is for the sg version 3 driver.
Only version 3 sg_err.c defines it. */
struct sg_io_hdr;
extern int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp);
/* 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 is in the sense buffer */
#define SG_ERR_CAT_OTHER 99 /* Some other error/warning has occurred */
extern int sg_err_category(int masked_status, int host_status,
int driver_status, const unsigned char * sense_buffer,
int sb_len);
/* The following function declaration is for the sg version 3 driver.
Only version 3 sg_err.c defines it. */
extern int sg_err_category3(struct sg_io_hdr * hp);
/* Returns length of SCSI command given the opcode (first byte) */
int sg_get_command_size(unsigned char opcode);
#endif

72
mtx-1.3.12/tapeinfo.1 Normal file
View File

@@ -0,0 +1,72 @@
.\" tapeinfo.1 Document copyright 2000 Eric Lee Green
.\" Program Copyright 2000 Eric Lee Green <eric@badtux.org>
.\" Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
.\"
.\" This is free documentation; 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.
.\"
.\" The GNU General Public License's references to "object code"
.\" and "executables" are to be interpreted as the output of any
.\" document formatting or typesetting system, including
.\" intermediate and printed output.
.\"
.\" This manual 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 manual; if not, write to the Free
.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
.\" USA.
.\"
.TH TAPEINFO 1 TAPEINFO1.0
.SH NAME
tapeinfo \- report SCSI tape device info
.SH SYNOPSIS
tapeinfo -f <scsi-generic-device>
.SH DESCRIPTION
The
.B tapeinfo
command reads various information from SCSI tape drives that is not
generally available via most vendors' tape drivers. It issues raw
commands directly to the tape drive, using either the operating system's
SCSI generic device ( e.g. /dev/sg0 on Linux, /dev/pass0 on FreeBSD) or
the raw SCSI I/O ioctl on a tape device on some operating systems.
.P
One good time to use 'tapeinfo' is immediately after a tape i/o operation has
failed. On tape drives that support HP's 'tapealert' API, 'tapeinfo' will
report a more exact description of what went wrong.
.P
Do be aware that 'tapeinfo' is not a substitute for your operating system's
own 'mt' or similar tape driver control program. It is intended to supplement,
not replace, programs like 'mt' that access your operating system's tape
driver in order to report or set information.
.SH OPTIONS
The first argument, given following
.B -f
, is the SCSI generic device corresponding to your tape drive.
Consult your operating system's documentation for more information (for
example, under Linux these are generally start at /dev/sg0
under FreeBSD these start at /dev/pass0).
.P
Under FreeBSD, 'camcontrol devlist' will tell you what SCSI devices you
have, along with which 'pass' device controls them. Under Linux,
"cat /proc/scsi/scsi" will tell you what SCSI devices you have.
.SH BUGS AND LIMITATIONS
.P
This program has only been tested on Linux with a limited number of
tape drives (HP DDS4, Seagate AIT).
.P
.SH AVAILABILITY
.B tapeinfo
is currently being maintained by Robert Nelson <robertnelson@users.sourceforge.net>
as part of the 'mtx' suite of programs. The 'mtx' home page is
http://mtx.sourceforge.net and the actual code is currently available there and via
SVN from http://sourceforge.net/projects/mtx.
.SH SEE ALSO
.BR mt (1), mtx (1), scsitape (1), scsieject (1), loaderinfo (1)

969
mtx-1.3.12/tapeinfo.c Normal file
View File

@@ -0,0 +1,969 @@
/* Copyright 2000 Enhanced Software Technologies Inc.
* Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
* Released under terms of the GNU General Public License as
* required by the license on 'mtxl.c'.
* $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
* $Revision: 193 $
*/
/*#define DEBUG_PARTITION */
/*#define DEBUG 1 */
/* What this does: This basically dumps out the contents of the following
* pages:
*
* Inquiry -- prints full inquiry info. If it's not a tape drive, this is
* the end of things.
* DeviceType:
* Manufacturer:
* ProdID:
* ProdRevision:
*
* Log Sense: TapeAlert Page (if supported):
* TapeAlert:[message#]<Message> e.g. "TapeAlert:[22]Cleaning Cartridge Worn Out"
*
* Mode Sense:
* Data Compression Page:
* DataCompEnabled:<yes|no>
* DataCompCapable:<yes|no>
* DataDeCompEnabled:<yes|no>
* CompType:<number>
* DeCompType:<number>
*
* Device Configuration Page:
* ActivePartition:<#>
* DevConfigComp:<#> -- the compression byte in device config page.
* EarlyWarningSize:<#> -- size of early warning buffer?
*
* Medium Partition Page:
* NumPartitions:<#>
* MaxPartitions:<#>
* Partition[0]:<size>
* Partition[1]:<size>...
*
* Read Block Limits command:
* MinBlock:<#> -- Minimum block size.
* MaxBlock:<#> -- Maximum block size.
*/
#include <stdio.h>
#include <string.h>
#include "mtx.h"
#include "mtxl.h"
char *argv0;
void usage(void)
{
FatalError("Usage: tapeinfo -f <generic-device>\n");
}
/* A table for printing out the peripheral device type as ASCII. */
static char *PeripheralDeviceType[32] =
{
"Disk Drive",
"Tape Drive",
"Printer",
"Processor",
"Write-once",
"CD-ROM",
"Scanner",
"Optical",
"Medium Changer",
"Communications",
"ASC IT8",
"ASC IT8",
"RAID Array",
"Enclosure Services",
"OCR/W",
"Bridging Expander", /* 0x10 */
"Reserved", /* 0x11 */
"Reserved", /* 0x12 */
"Reserved", /* 0x13 */
"Reserved", /* 0x14 */
"Reserved", /* 0x15 */
"Reserved", /* 0x16 */
"Reserved", /* 0x17 */
"Reserved", /* 0x18 */
"Reserved", /* 0x19 */
"Reserved", /* 0x1a */
"Reserved", /* 0x1b */
"Reserved", /* 0x1c */
"Reserved", /* 0x1d */
"Reserved", /* 0x1e */
"Unknown" /* 0x1f */
};
/* we call it MediumChangerFD for history reasons, sigh. */
/* now to print inquiry information: Copied from other one.... */
static void ReportInquiry(DEVICE_TYPE MediumChangerFD)
{
RequestSense_T RequestSense;
Inquiry_T *Inquiry;
int i;
Inquiry = RequestInquiry(MediumChangerFD, &RequestSense);
if (Inquiry == NULL)
{
PrintRequestSense(&RequestSense);
FatalError("INQUIRY Command Failed\n");
}
printf("Product Type: %s\n", PeripheralDeviceType[Inquiry->PeripheralDeviceType]);
printf("Vendor ID: '");
for (i = 0; i < sizeof(Inquiry->VendorIdentification); i++)
printf("%c", Inquiry->VendorIdentification[i]);
printf("'\nProduct ID: '");
for (i = 0; i < sizeof(Inquiry->ProductIdentification); i++)
printf("%c", Inquiry->ProductIdentification[i]);
printf("'\nRevision: '");
for (i = 0; i < sizeof(Inquiry->ProductRevisionLevel); i++)
printf("%c", Inquiry->ProductRevisionLevel[i]);
printf("'\n");
if (Inquiry->MChngr)
{
/* check the attached-media-changer bit... */
printf("Attached Changer API: Yes\n");
}
else
{
printf("Attached Changer API: No\n");
}
free(Inquiry); /* well, we're about to exit, but ... */
}
/* Okay, now for the Log Sense Tape Alert Page (if supported): */
#define TAPEALERT_SIZE 2048 /* max size of tapealert buffer. */
#define MAX_TAPE_ALERT 0x41
static char *tapealert_messages[] =
{
"Undefined", /* 0 */
" Read: Having problems reading (slowing down)", /* 1 */
" Write: Having problems writing (losing capacity)", /* 2 */
" Hard Error: Uncorrectable read/write error", /* 3 */
" Media: Media Performance Degraded, Data Is At Risk", /* 4 */
" Read Failure: Tape faulty or tape drive broken", /* 5 */
"Write Failure: Tape faulty or tape drive broken", /* 6 */
" Media Life: The tape has reached the end of its useful life", /* 7 */
"Not Data Grade:Replace cartridge with one containing data grade tape",/*8*/
"Write Protect: Attempted to write to a write-protected cartridge",/*9 */
" No Removal: Cannot unload, initiator is preventing media removal", /*a*/
"Cleaning Media:Cannot back up or restore to a cleaning cartridge", /* b */
" Bad Format: The loaded tape contains data in an unsupported format", /*c */
" Snapped Tape: The data cartridge contains a broken tape", /* d */
"Undefined", /* e */
"Undefined", /* f */
"Undefined", /* 10 */
"Undefined", /* 11 */
"Undefined", /* 12 */
"Undefined", /* 13 */
" Clean Now: The tape drive neads cleaning NOW", /* 0x14 */
"Clean Periodic:The tape drive needs to be cleaned at next opportunity", /* 0x15 */
"Cleaning Media:Cannot clean because cleaning cartridge used up, insert new cleaning cartridge to clean the drive", /* 0x16 */
"Undefined", /* 0x17 */
"Undefined", /* 0x18 */
"Undefined", /* 0x19 */
"Undefined", /* 0x1a */
"Undefined", /* 0x1b */
"Undefined", /* 0x1c */
"Undefined", /* 0x1d */
" Hardware A: Tape drive has a problem not read/write related", /* 0x1e */
" Hardware B: Tape drive has a problem not read/write related", /* 0x1f */
" Interface: Problem with SCSI interface between tape drive and initiator", /* 0x20 */
" Eject Media: The current operation has failed. Eject and reload media", /* 0x21 */
"Download Fail: Attempt to download new firmware failed", /* 0x22 */
"Undefined", /* 0x23 */
"Undefined", /* 0x24 */
"Undefined", /* 0x25 */
"Undefined", /* 0x26 */
"Undefined", /* 0x27 */
"Loader Hardware A: Changer having problems communicating with tape drive", /* 0x28 40 */
"Loader Stray Tape: Stray tape left in drive from prior error", /* 0x29 41 */
"Loader Hardware B: Autoloader mechanism has a fault", /* 0x2a 42 */
" Loader Door: Loader door is open, please close it", /* 0x2b 43 */
"Undefined", /* 0x2c */
"Undefined", /* 0x2d */
"Undefined", /* 0x2e */
"Undefined", /* 0x2f */
"Undefined", /* 0x30 */
"Undefined", /* 0x31 */
"Undefined", /* 0x32 */
"Undefined", /* 0x33 */
"Undefined", /* 0x34 */
"Undefined", /* 0x35 */
"Undefined", /* 0x36 */
"Undefined", /* 0x37 */
"Undefined", /* 0x38 */
"Undefined", /* 0x39 */
"Undefined", /* 0x3a */
"Undefined", /* 0x3b */
"Undefined", /* 0x3c */
"Undefined", /* 0x3d */
"Undefined", /* 0x3e */
"Undefined", /* 0x3f */
"Undefined" /* 0x40 */
};
typedef struct TapeCapacityStruct
{
unsigned int partition0_remaining;
unsigned int partition1_remaining;
unsigned int partition0_size;
unsigned int partition1_size;
} TapeCapacity;
#if defined(DEBUG)
/* DEBUG */
static void dump_data(unsigned char *data, int len)
{
if (len != 0)
{
fprintf(stderr,"DATA:");
PrintHex(1, data, len);
}
else
{
fprintf(stderr, "**NO DATA**\n");
}
}
#endif
/* Request the tape capacity page defined by some DAT autoloaders. */
static TapeCapacity *RequestTapeCapacity(DEVICE_TYPE fd, RequestSense_T *sense)
{
CDB_T CDB;
TapeCapacity *result;
int result_len;
unsigned char buffer[TAPEALERT_SIZE]; /* Overkill, but ... */
slow_bzero((char *)buffer,TAPEALERT_SIZE); /*zero it... */
/* now to create the CDB block: */
CDB[0] = 0x4d; /* Log Sense */
CDB[1] = 0;
CDB[2] = 0x31; /* Tape Capacity Page. */
CDB[3] = 0;
CDB[4] = 0;
CDB[5] = 0;
CDB[6] = 0;
CDB[7] = TAPEALERT_SIZE >> 8 & 0xff; /* hi byte, allocation size */
CDB[8] = TAPEALERT_SIZE & 0xff; /* lo byte, allocation size */
CDB[9] = 0; /* reserved */
if (SCSI_ExecuteCommand(fd, Input, &CDB, 10, buffer, TAPEALERT_SIZE, sense) != 0)
{
/* fprintf(stderr,"RequestTapeCapacity: Command failed: Log Sense\n"); */
return NULL;
}
/* dump_data(buffer,64); */
/* okay, we have stuff in the result buffer: the first 4 bytes are a header:
* byte 0 should be 0x31, byte 1 == 0, bytes 2,3 tell how long the
* log page is.
*/
if ((buffer[0]&0x3f) != 0x31)
{
/* fprintf(stderr,"RequestTapeCapacity: Invalid header for page (not 0x31).\n"); */
return NULL;
}
result_len = ((int)buffer[2] << 8) + buffer[3];
if (result_len != 32)
{
/* fprintf(stderr,"RequestTapeCapacity: Page was %d bytes long, not 32 bytes\n",result_len); */
return NULL; /* This Is Not The Page You're Looking For */
}
result = xmalloc(sizeof(TapeCapacity));
/* okay, now allocate data and move the buffer over there: */
/* 0 1 2 3 4 5 6 7 8 9
DATA: 31 00 00 20 00 01 4c 04 01 3a
10 11 12 13 14 15 16 17 18 19
DATA: 81 0c 00 02 4c 04 00 00 00 00
20 21 22 23 24 25 26 27 28 29
DATA: 00 03 4c 04 01 3f 4b 1f 00 04
30 31 32 33 34 35
DATA: 4c 04 00 00 00 00 00 00 00 00
DATA: 00 00 00 00 00 00 00 00 00 00
DATA: 00 00 00 00 00 00 00 00 00 00
DATA: 00 00 00 00
*/
result->partition0_remaining =
((unsigned int)buffer[8] << 24) +
((unsigned int)buffer[9] << 16) +
((unsigned int)buffer[10] << 8) +
buffer[11];
result->partition1_remaining =
((unsigned int)buffer[16] << 24) +
((unsigned int)buffer[17] << 16) +
((unsigned int)buffer[18] << 8) +
buffer[19];
result->partition0_size =
((unsigned int)buffer[24] << 24) +
((unsigned int)buffer[25] << 16) +
((unsigned int)buffer[26] << 8) +
buffer[27];
result->partition1_size =
((unsigned int)buffer[32] << 24) +
((unsigned int)buffer[33] << 16) +
((unsigned int)buffer[34] << 8) +
buffer[35];
return result;
}
struct tapealert_struct
{
int length;
unsigned char *data;
};
static struct tapealert_struct *RequestTapeAlert(DEVICE_TYPE fd, RequestSense_T *sense)
{
CDB_T CDB;
struct tapealert_struct *result;
int i, tapealert_len, result_idx;
unsigned char buffer[TAPEALERT_SIZE];
unsigned char *walkptr;
slow_bzero((char *)buffer, TAPEALERT_SIZE); /*zero it... */
/* now to create the CDB block: */
CDB[0] = 0x4d; /* Log Sense */
CDB[1] = 0;
CDB[2] = 0x2e; /* Tape Alert Page. */
CDB[3] = 0;
CDB[4] = 0;
CDB[5] = 0;
CDB[6] = 0;
CDB[7] = TAPEALERT_SIZE >> 8 & 0xff; /* hi byte, allocation size */
CDB[8] = TAPEALERT_SIZE & 0xff; /* lo byte, allocation size */
CDB[9] = 0; /* reserved */
if (SCSI_ExecuteCommand(fd,Input,&CDB,10,buffer,TAPEALERT_SIZE,sense)!=0)
{
return NULL;
}
result = xmalloc(sizeof(struct tapealert_struct));
/* okay, we have stuff in the result buffer: the first 4 bytes are a header:
* byte 0 should be 0x2e, byte 1 == 0, bytes 2,3 tell how long the
* tapealert page is.
*/
if ((buffer[0]&0x3f) != 0x2e)
{
result->data = NULL;
result->length = 0;
return result;
}
tapealert_len = ((int)buffer[2] << 8) + buffer[3];
if (!tapealert_len)
{
result->length = 0;
result->data = NULL;
return result;
}
/* okay, now allocate data and move the buffer over there: */
result->length = MAX_TAPE_ALERT;
result->data = xzmalloc(MAX_TAPE_ALERT); /* alloc & zero. */
walkptr = &buffer[4];
i = 0;
while (i < tapealert_len)
{
result_idx=(((int)walkptr[0])<<8) + walkptr[1]; /* the parameter #. */
if (result_idx > 0 && result_idx < MAX_TAPE_ALERT)
{
if (walkptr[4])
{
result->data[result_idx] = 1;
}
else
{
result->data[result_idx] = 0;
}
#ifdef DEBUGOLD1
fprintf(stderr,"Alert[0x%x]=%d\n",result_idx,result->data[result_idx]);
fflush(stderr);
#endif
}
else
{
FatalError("Invalid tapealert page: %d\n",result_idx);
}
i = i + 4 + walkptr[3]; /* length byte! */
walkptr = walkptr + 4 + walkptr[3]; /* next! */
}
return result;
}
static void ReportTapeCapacity(DEVICE_TYPE fd)
{
/* we actually ignore a bad sense reading, like might happen if the
* tape drive does not support the tape capacity page.
*/
RequestSense_T RequestSense;
TapeCapacity *result;
result=RequestTapeCapacity(fd,&RequestSense);
if (!result)
return;
printf("Partition 0 Remaining Kbytes: %d\n", result->partition0_remaining);
printf("Partition 0 Size in Kbytes: %d\n", result->partition0_size);
if (result->partition1_size)
{
printf("Partition 1 Remaining Kbytes: %d\n", result->partition1_remaining);
printf("Partition 1 Size in Kbytes: %d\n", result->partition1_size);
}
free(result);
}
static void ReportTapeAlert(DEVICE_TYPE fd)
{
/* we actually ignore a bad sense reading, like might happen if the
* tape drive does not support the tapealert page.
*/
RequestSense_T RequestSense;
struct tapealert_struct *result;
int i;
result=RequestTapeAlert(fd,&RequestSense);
if (!result)
return; /* sorry. Don't print sense here. */
if (!result->length)
return; /* sorry, no alerts valid. */
for (i = 0; i < result->length; i++)
{
if (result->data[i])
{
printf("TapeAlert[%d]: %s.\n", i, tapealert_messages[i]);
}
}
free(result->data);
free(result);
}
static unsigned char
*mode_sense(DEVICE_TYPE fd, char pagenum, int alloc_len, RequestSense_T *RequestSense)
{
CDB_T CDB;
unsigned char *input_buffer;
unsigned char *tmp;
unsigned char *retval;
int i, pagelen;
if (alloc_len > 255)
{
FatalError("mode_sense(6) can only read up to 255 characters!\n");
}
input_buffer = (unsigned char *)xzmalloc(256); /* overdo it, eh? */
/* clear the sense buffer: */
slow_bzero((char *)RequestSense, sizeof(RequestSense_T));
/* returns an array of bytes in the page, or, if not possible, NULL. */
CDB[0] = 0x1a; /* Mode Sense(6) */
CDB[1] = 0;
CDB[2] = pagenum; /* the page to read. */
CDB[3] = 0;
CDB[4] = 255; /* allocation length. This does max of 256 bytes! */
CDB[5] = 0;
if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, input_buffer, 255, RequestSense) != 0)
{
#ifdef DEBUG_MODE_SENSE
fprintf(stderr,"Could not execute mode sense...\n");
fflush(stderr);
#endif
return NULL; /* sorry, couldn't do it. */
}
/* Oh hell, write protect is the only thing I have: always print
* it if our mode page was 0x0fh, before skipping past buffer:
* if the media is *NOT* write protected, just skip, sigh.
*
* Oh poops, the blocksize is reported in the block descriptor header
* < * too. Again, just print if our mode page was 0x0f...
*/
if (pagenum == 0x0f)
{
int blocklen;
if (input_buffer[2] & 0x80)
{
printf("WriteProtect: yes\n");
}
if (input_buffer[2] & 0x70)
{
printf("BufferedMode: yes\n");
}
if (input_buffer[1] )
{
printf("Medium Type: 0x%x\n", input_buffer[1]);
}
else
{
printf("Medium Type: Not Loaded\n");
}
printf("Density Code: 0x%x\n", input_buffer[4]);
/* Put out the block size: */
blocklen = ((int)input_buffer[9] << 16)+
((int)input_buffer[10] << 8)+
input_buffer[11];
printf("BlockSize: %d\n", blocklen);
}
/* First skip past any header.... */
tmp = input_buffer + 4 + input_buffer[3];
/* now find out real length of page... */
pagelen = tmp[1] + 2;
retval = xmalloc(pagelen);
/* and copy our data to the new page. */
for (i=0;i<pagelen;i++)
{
retval[i]=tmp[i];
}
/* okay, free our input buffer: */
free(input_buffer);
return retval;
}
#define DCE_MASK 0x80
#define DCC_MASK 0x40
#define DDE_MASK 0x80
static void ReportCompressionPage(DEVICE_TYPE fd)
{
/* actually ignore a bad sense reading, like might happen if the tape
* drive does not support the mode sense compression page.
*/
RequestSense_T RequestSense;
unsigned char *compression_page;
compression_page=mode_sense(fd,0x0f,16,&RequestSense);
if (!compression_page)
{
return; /* sorry! */
}
/* Okay, we now have the compression page. Now print stuff from it: */
printf("DataCompEnabled: %s\n", (compression_page[2] & DCE_MASK)? "yes" : "no");
printf("DataCompCapable: %s\n", (compression_page[2] & DCC_MASK)? "yes" : "no");
printf("DataDeCompEnabled: %s\n", (compression_page[3] & DDE_MASK)? "yes" : "no");
printf("CompType: 0x%x\n",
(compression_page[4] << 24) +
(compression_page[5] << 16) +
(compression_page[6] << 8) +
compression_page[7]);
printf("DeCompType: 0x%x\n",
(compression_page[8] << 24) +
(compression_page[9] << 16) +
(compression_page[10] << 8) +
compression_page[11]);
free(compression_page);
}
/* Now for the device configuration mode page: */
static void ReportConfigPage(DEVICE_TYPE fd)
{
RequestSense_T RequestSense;
unsigned char *config_page;
config_page = mode_sense(fd, 0x10, 16, &RequestSense);
if (!config_page)
return;
/* Now to print the stuff: */
printf("ActivePartition: %d\n", config_page[3]);
/* The following does NOT work accurately on any tape drive I know of... */
/* printf("DevConfigComp: %s\n", config_page[14] ? "yes" : "no"); */
printf("EarlyWarningSize: %d\n",
(config_page[11] << 16) +
(config_page[12] << 8) +
config_page[13]);
}
/* ***************************************
* Medium Partition Page:
*
* The problem here, as we oh so graphically demonstrated during debugging
* of the Linux 'st' driver :-), is that there are actually *TWO* formats for
* the Medium Partition Page: There is the "long" format, where there is a
* partition size word for each partition on the page, and there is a "short"
* format, beloved of DAT drives, which only has a partition size word for
* partition #1 (and no partition size word for partition #0, and no
* provisions for any more partitions). So we must look at the size and
* # of partitions defined to know what to report as what.
*
********************************************/
static void ReportPartitionPage(DEVICE_TYPE fd)
{
RequestSense_T RequestSense;
unsigned char *partition_page;
int num_parts,max_parts;
int i;
partition_page=mode_sense(fd,0x11,255,&RequestSense);
if (!partition_page)
return;
/* Okay, now we have either old format or new format: */
num_parts = partition_page[3];
max_parts = partition_page[2];
printf("NumPartitions: %d\n", num_parts);
printf("MaxPartitions: %d\n", max_parts);
if (!num_parts)
{
/* if no additional partitions, then ... */
free(partition_page);
return;
}
/* we know we have at least one partition if we got here. Check the
* page size field. If it is 8 or below, then we are the old format....
*/
#ifdef DEBUG_PARTITION
fprintf(stderr,"partition_page[1]=%d\n",partition_page[1]);
fflush(stderr);
#endif
if (partition_page[1]==8)
{
/* old-style! */
printf("Partition1: %d\n",(partition_page[8]<<8)+partition_page[9]);
}
else
{
/* new-style! */
for (i=0;i<=max_parts;i++)
{
#ifdef DEBUG_PARTITION
fprintf(stderr,"partition%d:[%d]%d [%d]%d\n", i, 8 + i * 2,
partition_page[8+i*2]<<8, 9+i*2,partition_page[9 + i * 2]);
fflush(stderr);
#endif
printf("Partition%d: %d\n", i,
(partition_page[8 + i * 2] << 8) + partition_page[9 + i * 2]);
}
}
free(partition_page);
}
static void ReportSerialNumber(DEVICE_TYPE fd)
{
/* Actually ignore a bad sense reading, like might happen if the
tape drive does not support the inquiry page 0x80.
*/
RequestSense_T sense;
CDB_T CDB;
#define WILD_SER_SIZE 30
unsigned char buffer[WILD_SER_SIZE]; /* just wildly overestimate serial# length! */
int i, lim;
char *bufptr;
CDB[0] = 0x12; /* INQUIRY */
CDB[1] = 1; /* EVPD = 1 */
CDB[2] = 0x80; /* The serial # page, hopefully. */
CDB[3] = 0; /* reserved */
CDB[4] = WILD_SER_SIZE;
CDB[5] = 0;
if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, &buffer, sizeof(buffer), &sense) != 0)
{
/* PrintRequestSense(&sense); */ /* zap debugging output :-) */
/* printf("No Serial Number: None\n"); */
return;
}
/* okay, we have something in our buffer. Byte 3 should be the length of
the sernum field, and bytes 4 onward are the serial #. */
lim = (int)buffer[3];
bufptr = (char *)&(buffer[4]);
printf("SerialNumber: '");
for (i=0;i<lim;i++)
{
putchar(*bufptr++);
}
printf("'\n");
}
/* Read Block Limits! */
void ReportBlockLimits(DEVICE_TYPE fd)
{
RequestSense_T sense;
CDB_T CDB;
unsigned char buffer[6];
CDB[0] = 0x05; /* READ_BLOCK_LIMITS */
CDB[1] = 0;
CDB[2] = 0;
CDB[3] = 0; /* 1-5 all unused. */
CDB[4] = 0;
CDB[5] = 0;
slow_bzero((char *)&sense,sizeof(RequestSense_T));
if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, buffer, 6, &sense) != 0)
{
return;
}
/* okay, but if we did get a result, print it: */
printf("MinBlock: %d\n", (buffer[4] << 8) + buffer[5]);
printf("MaxBlock: %d\n", (buffer[1] << 16) + (buffer[2]<<8) + buffer[3]);
}
/* Do a READ_POSITION. This may not be always valid, but (shrug). */
void ReadPosition(DEVICE_TYPE fd)
{
RequestSense_T sense;
CDB_T CDB;
unsigned char buffer[20];
unsigned int position;
CDB[0] = 0x34; /* READ_POSITION */
CDB[1] = 0;
CDB[2] = 0;
CDB[3] = 0; /* 1-9 all unused. */
CDB[4] = 0;
CDB[5] = 0;
CDB[6] = 0;
CDB[7] = 0;
CDB[8] = 0;
CDB[9] = 0;
slow_bzero((char *)&sense, sizeof(RequestSense_T));
SCSI_Set_Timeout(2); /* set timeout to 2 seconds! */
/* if we don't get a result (e.g. we issue this to a disk drive), punt. */
if (SCSI_ExecuteCommand(fd, Input, &CDB, 10, buffer, 20, &sense) != 0)
{
return;
}
SCSI_Default_Timeout(); /* reset it to 5 minutes, sigh! */
/* okay, but if we did get a result, print it: */
#define RBL_BOP 0x80
#define RBL_EOP 0x40
#define RBL_BCU 0x20
#define RBL_BYCU 0x10
#define RBL_R1 0x08
#define RBL_BPU 0x04
#define RBL_PERR 0x02
/* If we have BOP, go ahead and print that. */
if (buffer[0]&RBL_BOP)
{
printf("BOP: yes\n");
}
/* if we have valid data, print it: */
if (buffer[0]&RBL_BPU)
{
printf("Block Position: -1");
}
else
{
position = (unsigned int)(((unsigned int)buffer[4] << 24) +
((unsigned int)buffer[5] << 16) +
((unsigned int)buffer[6] << 8) +
buffer[7]);
printf("Block Position: %d\n",position);
}
}
/* Test unit ready: This will tell us whether the tape drive
* is currently ready to read or write.
*/
int TestUnitReady(DEVICE_TYPE fd)
{
RequestSense_T sense;
CDB_T CDB;
unsigned char buffer[6];
CDB[0] = 0x00; /* TEST_UNIT_READY */
CDB[1] = 0;
CDB[2] = 0;
CDB[3] = 0; /* 1-5 all unused. */
CDB[4] = 0;
CDB[5] = 0;
slow_bzero((char *)&sense,sizeof(RequestSense_T));
if (SCSI_ExecuteCommand(fd,Input,&CDB,6,buffer,0,&sense)!=0)
{
printf("Ready: no\n");
return 0;
}
printf("Ready: yes\n");
return 1;
}
/* We write a filemarks of 0 before going to grab position, in order
* to insure that data in the buffer is not a problem.
*/
int WriteFileMarks(DEVICE_TYPE fd,int count)
{
RequestSense_T sense;
CDB_T CDB;
unsigned char buffer[6];
CDB[0] = 0x10; /* WRITE_FILE_MARKS */
CDB[1] = 0;
CDB[2] = (unsigned char)(count >> 16);
CDB[3] = (unsigned char)(count >> 8);
CDB[4] = (unsigned char)count;
CDB[5] = 0;
/* we really don't care if this command works or not, sigh. */
slow_bzero((char *)&sense, sizeof(RequestSense_T));
if (SCSI_ExecuteCommand(fd, Input, &CDB, 6, buffer, 0, &sense) != 0)
{
return 1;
}
return 0;
}
/* This will get the SCSI ID and LUN of the target device, if such
* is available from the OS. Currently only Linux supports this,
* but other drivers could, if someone wants to write a
* SCSI_GetIDLun function for them.
*/
#ifdef HAVE_GET_ID_LUN
static void ReportIDLun(DEVICE_TYPE fd)
{
scsi_id_t *scsi_id;
scsi_id = SCSI_GetIDLun(fd);
printf("SCSI ID: %d\nSCSI LUN: %d\n", scsi_id->id, scsi_id->lun);
}
#endif
/* we only have one argument: "-f <device>". */
int main(int argc, char **argv)
{
DEVICE_TYPE fd;
char *filename;
argv0=argv[0];
if (argc != 3)
{
fprintf(stderr,"argc=%d",argc);
usage();
}
if (strcmp(argv[1],"-f")!=0)
{
usage();
}
filename=argv[2];
fd=SCSI_OpenDevice(filename);
/* Now to call the various routines: */
ReportInquiry(fd);
ReportSerialNumber(fd);
ReportTapeAlert(fd);
ReportBlockLimits(fd);
#ifdef HAVE_GET_ID_LUN
ReportIDLun(fd);
#endif
/* okay, we should only report position if the unit is ready :-(. */
if (TestUnitReady(fd))
{
ReportCompressionPage(fd);
ReadPosition(fd);
ReportTapeCapacity(fd); /* only if we have it */
ReportConfigPage(fd); /* only valid if unit is ready. */
ReportPartitionPage(fd);
}
exit(0);
}

175
mtx-1.3.12/vms/000readme Normal file
View File

@@ -0,0 +1,175 @@
MTX -- SCSI Tape Attached Medium Changer Control Program
Copyright 1997 by Leonard N. Zubkoff <lnz@dandelion.com>
VMS port, April 1998, by TECSys Development, Inc.
SECTION I - Disclaimer
These programs / ports are distributed in the hopes that they
will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
While a significant amount of testing has been performed, and while
TDI believes that this utility is safe, ONLY YOU can make that
determination with respect to your environment. There are NO
GUARANTEES WHATSOEVER that use of this program will not CRASH
YOUR SYSTEM or worse. TDI disclaims any responsibility for any
losses or damages from the use of this program.
SECTION II - MTX program
***CAUTIONS***
* Attempting to check the status of the drive with 1 tape manually
inserted in the drive, and no magazine results in an unexpected
SCSI status message. During porting of this program on a VS4000/90a,
it is believed that the handling of this status message caused
some sort of scsi state problem wherein subsequent accesses to
the changer from normal MKDRIVER activity resulted in SCSI bus
phase errors and an ultimate system failure. Power cycles of both
the drive and the host were required to rectify the problem.
Instructions:
Read instructions (particularly section III below on the LDRSET utility).
Compile (DEC C) and link image, copy to an appropriate install location
Define a foreign symbol to access the program
Use UNIX syntax to operate program per documentation (MTX.DOC)
A descrip.mms file is provided if you have MMS or MMK installed.
You can build everything from scratch by doing a MMK/FROM_SOURCE. This
includes the LDRSET utility described below. You should be in the mtx
root directory before you do this, NOT [.vms].
If you do not have or use MMS or MMK, I can highly recommend MMK as
a great tool. It is available from www.madgoat.com. If you still do
not have mmk or mms, a build.com is provided. Again, you should be in
the mtx root directory before you do this, NOT [.vms].
Note: If you are on an alpha, the mms[k]-built exe files will be called
.alpha_exe, not .exe. Adjust following instructions accordingly.
Example:
$ MMS/DESCRIP=[.VMS] !Or MMK, or @[.VMS]BUILD
$ copy mtx.exe DISK$USERDISK:[USERS.FRED.UTILS]MTX
$ MTX:==$DISK$USERDISK:[USERS.FRED.UTILS]MTX.EXE
$ MTX -f MKA500 status
--or--
$ DEFINE TAPE MKA500
$ MTX status
Notes:
* This code does NOT compile under VAX C... the only issue is VAX C's
incorrect assertion that a 'boolean' is not an acceptable "expression"
on either side of a || operator. If you are on VAX C - upgrade. If you
really must... then use a ((int)...) around the left-hand-side
of the lines where the compiler complains.... like so:
if (((int)StorageElementFull[FirstStorageElementNumber]) ||
* The following symbols result from 'status' or element movement commands:
MTX_DTE (Data Transfer Element)
FULL:n (Full, with element 'n'... if n=0, then unknown ele)
EMPTY
MTX_MSZ (Magazine SiZe)
n (Number of magazine slots)
MTX_STE01 thru MTX_STExx (STorage Element, Max is MTX_MSZ)
FULL
EMPTY
These will allow a DCL program to process the results
of the status command
* PHY_IO and DIAGNOSE are required to run the program as it stands
today. Yes, the GKdriver dox say PHY_IO or LOG_IO.... MKdriver
thinks otherwise. It wins.
* The program IS equipped to handle being installed with DIAGNOSE
and PHY_IO. A certain level of checking is done to see that the
right type of device is being targeted for the autoloader manipulation
SCSI commands. [see next item]
* There is an indicator bit in DEVCHAR2 that indicates the presence of
a loader on a tape. Unfortunately, it appears that the "knowledge" of
whether a loader is present or not is hard-coded into mkdriver. This
means that if you don't have the magic DEC rom's in your Archive
loader, then the LDR bit is not set. This makes the LDR bit effectively
useless unless you want to write a kernel dinker program you could
run from startup to "fix" the DEVCHAR2 longword. (Incidentally,
if you ship the autoloader commands at a TZ30 (a non-loader drive),
the tz30 blows it off w/o any adverse side-effects - I would hope
that other drives are as tolerant.)
Because of this misfeature, the code to check the LDR flag in DEVCHAR2
is commented out.
*See section below on LDRSET utility if you want to use this
feature anyway (recommended).
* This program has been tested under:
VAX C V2.2, VAX/VMS 6.1, VS 4000/90A **with code modifications
DEC C V5.2, VAX/VMS 6.1, VS 4000/90A
DEC C V5.3, AXP/VMS 6.2, DEC3000-300X
SECTION III - LDRSET Utility
Description:
This is a small KERNEL MODE utility program to go set or reset
the DEV$M_LDR flag in UCB$L_DEVCHAR2 for a specified device. While
a certain amount of checking is performed, and while the author
believes that this utility is safe, ONLY YOU can make that
determination with respect to your environment. There are NO
GUARANTEES WHATSOEVER that use of this program will not CRASH
YOUR SYSTEM or worse. TDI disclaims any responsibility for any
losses or damages from the use of this program.
With all that said... this utility can be used [example: system
startup] to set the LDR flag for the autoloader tape device.
With the loader flag properly set, you can re-enable the LDR
check section in MTX and be fairly well certain that the MTX
utility will not be used against a drive that is not a SCSI
MAGTAPE with a LOADER attached.
Instructions:
Compile (DEC C ***ONLY***) the LDRSET C program
Alpha:
MACRO/MIGRATE the LDRUTIL.MAR program
LINK LDRSET,LDRUTIL/SYSEXE to generate the image
Vax
MACRO the LDRUTIL.MAR program
LINK LDRSET,LDRUTIL to generate the image
[NOTE!!! ---> DO NOT USE RESULTING IMAGE IF THERE ARE ANY COMPILE OR LINK
ERRORS!!!]
Copy to an appropriate install location.
Edit provided CLD file to reflect the installed location
Use the following syntax:
$ SET COMMAND LDRSET
$ LDRSET MKA500: /SET !Sets the loader present flag on MKA500
$ LDRSET MKA500: /RESET !Clears the loader present flag on MKA500
Alpha example:
$ CC/DECC/DEBUG/NOOPT LDRSET
$ MACRO/MIGRATE LDRUTIL
$ LINK LDRSET,LDRUTIL/SYSEXE
Vax example:
$ CC/DECC/DEBUG/NOOPT LDRSET
$ MACRO LDRUTIL
$ LINK LDRSET,LDRUTIL
Common:
$ copy LDRSET.EXE DISK$USERDISK:[USERS.FRED.UTILS]LDRSET.EXE
$ copy LDRSET.CLD DISK$USERDISK:[USERS.FRED.UTILS]LDRSET.CLD
$ EDIT DISK$USERDISK:[USERS.FRED.UTILS]LDRSET.CLD
...change image sys$disk:[]ldrset line to :
image DISK$USERDISK:[USERS.FRED.UTILS]LDRSET.EXE
$ LDRSET MKA500 /SET
* This program has been tested under:
DEC C V5.2, VAX/VMS 6.1, VS 4000/90A
DEC C V5.6, AXP/VMS 7.1, AS 1000A

34
mtx-1.3.12/vms/build.com Normal file
View File

@@ -0,0 +1,34 @@
$!x='f$ver(0)
$ if f$parse("[.VMS]A.A").eqs.""
$ then
$ write sys$output "?Error: Use $ @[.VMS]BUILD from the mtx directory"
$ exit 44
$ endif
$ alpha = f$getsyi("hw_model").ge.1024
$ vax = .not.alpha
$ exe = "EXE"
$ obj = "OBJ"
$ sysexe=""
$ migrate=""
$ if alpha then exe="ALPHA_EXE"
$ if alpha then obj="ALPHA_OBJ"
$ if alpha then sysexe="/SYSEXE"
$ if alpha then migrate="/MIGRATION/NOOPT"
$ set verify
$ if "''p1'".eqs."LINK" then goto do_link
$ CC /DECC/DEB/NOOP MTX.C/DEB/NOOP/OBJECT=MTX.'obj'
$ if f$search("MTX.''obj';-1").nes."" then -
purge/log MTX.'obj'
$ CC /DECC/DEB/NOOP [.VMS]LDRSET.C/DEB/NOOP/OBJECT=[.VMS]LDRSET.'obj'
$ if f$search("[.VMS]LDRSET.''obj';-1").nes."" then -
purge/log [.VMS]LDRSET.'obj'
$ MACRO'migrate' /DEB [.VMS]LDRUTIL.MAR -
/OBJECT=[.VMS]LDRUTIL.'obj'
$ if f$search("[.VMS]LDRUTIL.''obj';-1").nes."" then -
purge/log [.VMS]LDRUTIL.'obj'
$!
$ do_link:
$ link/notrace mtx.'obj'/exe=mtx.'exe'
$ link [.vms]ldrset.'obj',[.vms]ldrutil.'obj' -
/exe=ldrset.'exe' 'sysexe'
$ exit

42
mtx-1.3.12/vms/defs.h Normal file
View File

@@ -0,0 +1,42 @@
#ifdef __DECC
#pragma module MTX "V01-00"
#else
#module MTX "V01-00"
#endif
typedef int DEVICE_TYPE;
#include <ssdef.h>
#include <lib$routines.h>
#include <ots$routines.h>
#include <stdio.h>
#include <ctype.h>
#include <iodef.h>
#include <descrip.h>
#include <dcdef.h>
#include <devdef.h>
#include <dvidef.h>
#include <starlet.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <prvdef.h>
#if defined(__DECC)
#pragma member_alignment save
#pragma nomember_alignment
#endif
/*
Define either of the following symbols as 1 to enable checking of
the LDR flag for specified devices. DO NOT set these bits if you
do not 1) have a DEC-recognized autoloader, or 2) use the LDRSET
utility to set the LDR flag for the target devices.
*/
#define USING_DEC_DRIVE 0
#define USING_LDRSET 0
static unsigned long VMS_ExitCode = SS$_ABORT;

View File

@@ -0,0 +1,77 @@
!
! MMS System build for MTX and LDRSET utility
!
!Global build flag macros
!
CDEBUG = /DEB/NOOP
MDEBUG = /DEB
CFLAGS = /DECC$(CDEBUG)
MFLAGS = $(MDEBUG)
.IFDEF __AXP__
.SUFFIXES .ALPHA_OBJ
MFLAGS = /MIGRATE$(MFLAGS)/NOOP
DBG = .ALPHA_DBG
EXE = .ALPHA_EXE
OBJ = .ALPHA_OBJ
OPT = .ALPHA_OPT
SYSEXE=/SYSEXE
.ELSE
DBG = .DBG
EXE = .EXE
OPT = .OPT
OBJ = .OBJ
SYSEXE=
.ENDIF
PURGEOBJ = if f$search("$(MMS$TARGET_NAME)$(OBJ);-1").nes."" then purge/log $(MMS$TARGET_NAME)$(OBJ)
!
!Bend the default build rules for C, MACRO, and MESSAGE
!
.C$(OBJ) :
$(CC) $(CFLAGS) $(MMS$SOURCE)$(CDEBUG)/OBJECT=$(MMS$TARGET_NAME)$(OBJ)
$(PURGEOBJ)
.MAR$(OBJ) :
$(MACRO) $(MFLAGS) $(MMS$SOURCE)$(MDEBUG)/OBJECT=$(MMS$TARGET_NAME)$(OBJ)
$(PURGEOBJ)
.CLD$(OBJ) :
SET COMMAND/OBJECT=$(MMS$TARGET_NAME)$(OBJ) $(MMS$SOURCE)
$(PURGEOBJ)
.MSG$(OBJ) :
MESSAGE $(MMS$SOURCE)/OBJECT=$(MMS$TARGET_NAME)$(OBJ)
$(PURGEOBJ)
DEFAULT : ERROR,-
MTX,-
LDRSET
@ !
ERROR :
@ if f$parse("[.VMS]A.A").eqs."" then write sys$output "?Error: Use $ MMS/DESCRIP=[.VMS] from the mtx directory"
MTX : mtx$(EXE)
@ !
mtx$(EXE) : mtx$(OBJ)
$ link/notrace mtx$(OBJ)/exe=mtx$(EXE)
mtx$(OBJ) : mtx.c,[.vms]scsi.c,[.vms]defs.h
LDRSET : ldrset$(EXE),ldrset.cld
@ !
ldrset.cld : [.vms]ldrset.cld
$ copy [.vms]ldrset.cld []/log
ldrset$(EXE) : [.vms]ldrset$(OBJ),[.vms]ldrutil$(OBJ)
$ link [.vms]ldrset$(OBJ),[.vms]ldrutil$(OBJ)/exe=ldrset$(EXE)$(SYSEXE)
[.vms]ldrset$(OBJ) : [.vms]ldrset.c
[.vms]ldrutil$(OBJ) : [.vms]ldrutil.mar

183
mtx-1.3.12/vms/ldrset.c Normal file
View File

@@ -0,0 +1,183 @@
/* LDRSET - Set the state of the LDR flag in UCB$L_DEVCHAR2 for a
** SCSI magtape. REQUIRES CMKRNL privilege.
**
** Copyright 1999 by TECSys Development, Inc. http://www.tditx.com
**
** This program is free software; you may redistribute and/or modify it under
** the terms of the GNU General Public License Version 2 as published by the
** Free Software Foundation.
**
** 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 complete details.
**
** Description:
** This is a small KERNEL MODE utility program to go set or reset
** the DEV$M_LDR flag in UCB$L_DEVCHAR2 for a specified device. While
** a certain amount of checking is performed, and while the author
** believes that this utility is safe, ONLY YOU can make that
** determination with respect to your environment. There are NO
** GUARANTEES WHATSOEVER that use of this program will not CRASH
** YOUR SYSTEM or worse. TDI disclaims any responsibility for any
** losses or damages from the use of this program.
**
** With all that said... this utility can be used [example: system
** startup] to set the LDR flag for the autoloader tape device.
** With the loader flag properly set, you can re-enable the LDR
** check section in MTX and be fairly well certain that the MTX
** utility will not be used against a drive that is not a SCSI
** MAGTAPE with a LOADER attached.
**
**
** LDRSET.CLD:
** define verb LDRSET
** image sys$disk:[]ldrset.exe
** parameter p1, label=device,
** prompt="Device",
** value(required,type=$FILE)
** qualifier SET, nonnegatable
** qualifier RESET, nonnegatable
** disallow any2 (SET, RESET)
** disallow NOT SET AND NOT RESET
*/
#ifdef __DECC
#pragma module LDRSET "V01-00"
#else
#module LDRSET "V01-00"
#endif
#include <ssdef.h>
#include <dcdef.h>
#include <devdef.h>
#include <dvidef.h>
#include <descrip.h>
#include <stdio.h>
#include <string.h>
#include <lib$routines.h>
#include <starlet.h>
#ifndef DESCR_CNT
#define DESCR_CNT 16
/* MUST BE of the form 2^N!, big enough for max concurrent usage */
#endif
static struct dsc$descriptor_s *
descr(
char *str)
{
static struct dsc$descriptor_s d_descrtbl[DESCR_CNT]; /* MAX usage! */
static unsigned short int descridx=0;
struct dsc$descriptor_s *d_ret = &d_descrtbl[descridx];
descridx = (descridx+1)&(DESCR_CNT-1);
d_ret->dsc$w_length = strlen((const char *)str);
d_ret->dsc$a_pointer = (char *)str;
d_ret->dsc$b_class =
d_ret->dsc$b_dtype = 0;
return(d_ret);
}
extern unsigned long int finducb();
extern unsigned long int _setldr();
extern unsigned long int _clrldr();
unsigned long int
set_ldrstate(
int devch,
int setstate)
{
unsigned long int ret;
struct ucbdef *ucb;
if (~(ret=finducb(devch,&ucb))&1)
return(ret);
if (setstate)
return(_setldr(ucb));
else
return(_clrldr(ucb));
}
extern unsigned long int CLI$PRESENT();
extern unsigned long int CLI$GET_VALUE();
static unsigned long int
cld_special(
char *kwd_name)
{
lib$establish(lib$sig_to_ret);
return(CLI$PRESENT(descr(kwd_name)));
}
int
main(){
unsigned long int ret;
unsigned long int ismnt = 0;
unsigned long int dvcls = 0;
unsigned long int dchr2 = 0;
struct itmlst_3 {
unsigned short int ilen;
unsigned short int code;
unsigned long int *returnP;
unsigned long int ignored;
} dvi_itmlst[] = {
{4, DVI$_MNT, 0/*&ismnt*/, 0},
{4, DVI$_DEVCLASS, 0/*&dvcls*/, 0},
{4, DVI$_DEVCHAR2, 0/*&dchr2*/, 0},
{0,0,0,0} };
unsigned long int iosb[2];
struct dsc$descriptor_s *dp_tmp;
struct dsc$descriptor_d dy_devn = { 0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0 };
unsigned long int devch=0;
dvi_itmlst[0].returnP = &ismnt;
dvi_itmlst[1].returnP = &dvcls;
dvi_itmlst[2].returnP = &dchr2;
if (~(ret=CLI$PRESENT(dp_tmp=descr("DEVICE")))&1) {
printf("?Error obtaining CLD DEVICE parameter\n");
return(ret); }
if (~(ret=CLI$GET_VALUE(dp_tmp,&dy_devn,0))&1) {
printf("?Error obtaining CLD DEVICE value\n");
return(ret); }
if (~(ret=sys$alloc(&dy_devn,0,0,0,0))&1) {
printf("?Error allocating specified device\n");
return(ret); }
if (~(ret=sys$assign(&dy_devn,&devch,0,0))&1) {
printf("?Error assigning a channel to specified device\n");
return(ret); }
if (~(ret=sys$getdviw(0,devch,0,&dvi_itmlst,&iosb,0,0,0))&1) {
printf("?Error obtaining device information(1)\n");
return(ret); }
if (~(ret=iosb[0])&1) {
printf("?Error obtaining device information(2)\n");
return(ret); }
if (dvcls != DC$_TAPE) {
printf("?Device is not a tape drive\n");
return(SS$_IVDEVNAM); }
if (~dchr2 & DEV$M_SCSI) {
printf("?Device is not a SCSI device\n");
return(SS$_IVDEVNAM); }
if (ismnt) {
printf("?Device is mounted\n");
return(SS$_DEVMOUNT); }
if (cld_special("SET")&1)
return(set_ldrstate(devch,1));
if (cld_special("RESET")&1)
return(set_ldrstate(devch,0));
/* Either SET or RESET above must be present to win */
printf("?CLD structural error - see source\n");
return(SS$_BADPARAM);
}

View File

@@ -0,0 +1,9 @@
define verb LDRSET
image sys$disk:[]ldrset.exe
parameter p1, label=device,
prompt="Device",
value(required,type=$FILE)
qualifier SET, nonnegatable
qualifier RESET, nonnegatable
disallow any2 (SET, RESET)
disallow NOT SET AND NOT RESET

104
mtx-1.3.12/vms/ldrutil.mar Normal file
View File

@@ -0,0 +1,104 @@
.title LDRUTIL - Obtain ucb for assigned channel
.ident /LDRUTIL V1.0/
; LDRUTIL - VMS UCB LDR bit utility library
;
; TECSys Development, Inc., April 1998
;
; This file may be copied under the terms and conditions of version 2
; of the GNU General Public License, as published by the Free
; Software Foundation (Cambridge, Massachusetts).
;
; 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
;
;
.link "sys$system:sys.stb"/selective_search
.library /sys$share:lib/
.NTYPE ...IS_IT_ALPHA,R22 ;Get the type of R22
...IS_IT_ALPHA = <...IS_IT_ALPHA@-4&^XF>-5
.IIF EQ,...IS_IT_ALPHA, ALPHA=1
$ssdef
$ucbdef
$ccbdef
$chfdef
$dcdef
$devdef
$pcbdef
.psect $$code,exe,rd,nowrt,shr
.IF NDF,ALPHA
.entry finducb,^m<r2,r3,r4,r5,r6,r7,r8,r9> ;Find UCB address from channel
.IFF
.call_entry, 2,home_args=TRUE,-
preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
output=<r0,r1>,-
label=finducb
.endc
movzwl 4(AP),r0 ;prep to find UCB
jsb g^IOC$VERIFYCHAN ;callable from user mode!
blbc r0,20$
movl CCB$L_UCB(r1),@8(AP) ;save UCB address
movzbl #1,r0
20$: ret
.IF NDF,ALPHA
.entry __setldr,^m<r2,r3,r4,r5,r6,r7,r8,r9> ;Find UCB address from channel
.IFF
.call_entry, 2,home_args=TRUE,-
preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
output=<r0,r1>,-
label=__setldr
.endc
movl 4(AP),r1
bisl #DEV$M_LDR,UCB$L_DEVCHAR2(r1)
movzbl #1,r0
ret
.IF NDF,ALPHA
.entry _setldr,^m<r2,r3,r4,r5,r6,r7,r8,r9> ;Find UCB address from channel
.IFF
.call_entry, 2,home_args=TRUE,-
preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
output=<r0,r1>,-
label=_setldr
.endc
$cmkrnl_s -
routin = __setldr,-
arglst = (AP)
ret
.IF NDF,ALPHA
.entry __clrldr,^m<r2,r3,r4,r5,r6,r7,r8,r9> ;Find UCB address from channel
.IFF
.call_entry, 2,home_args=TRUE,-
preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
output=<r0,r1>,-
label=__clrldr
.endc
movl 4(AP),r1
bicl #DEV$M_LDR,UCB$L_DEVCHAR2(r1)
movzbl #1,r0
ret
.IF NDF,ALPHA
.entry _clrldr,^m<r2,r3,r4,r5,r6,r7,r8,r9> ;Find UCB address from channel
.IFF
.call_entry, 2,home_args=TRUE,-
preserve=<r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>,-
output=<r0,r1>,-
label=_clrldr
.endc
$cmkrnl_s -
routin = __clrldr,-
arglst = (AP)
ret
.end

400
mtx-1.3.12/vms/scsi.c Normal file
View File

@@ -0,0 +1,400 @@
/* SCSI.C - VMS-specific SCSI routines.
**
** TECSys Development, Inc., April 1998
**
** This module began life as a program called CDWRITE20, a CD-R control
** program for VMS. No real functionality from the original CDWRITE20
** is present in this module, but in the interest of making certain that
** proper credit is given where it may be due, the copyrights and inclusions
** from the CDWRITE20 program are included below.
**
** The portions of coding in this module ascribable to TECSys Development
** are hereby also released under the terms and conditions of version 2
** of the GNU General Public License as described below....
*/
/* The remainder of these credits are included directly from the CDWRITE20
** sources. */
/* Copyright 1994 Yggdrasil Computing, Inc. */
/* Written by Adam J. Richter (adam@yggdrasil.com) */
/* Rewritten February 1997 by Eberhard Heuser-Hofmann*/
/* using the OpenVMS generic scsi-interface */
/* see the README-file, how to setup your machine */
/*
** Modified March 1997 by John Vottero to use overlapped, async I/O
** and lots of buffers to help prevent buffer underruns.
** Also improved error reporting.
*/
/* This file may be copied under the terms and conditions of version 2
of the GNU General Public License, as published by the Free
Software Foundation (Cambridge, Massachusetts).
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* The second notice comes from sys$examples:gktest.c (OpenVMS 7.0)*/
/*
** COPYRIGHT (c) 1993 BY
** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.
** ALL RIGHTS RESERVED.
**
** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
** ONLY IN ACCORDANCE OF THE TERMS OF SUCH LICENSE AND WITH THE
** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER
** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
** OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY
** TRANSFERRED.
**
** THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE
** AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT
** CORPORATION.
**
** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS
** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
*/
/*
Define the Generic SCSI Command Descriptor.
*/
typedef struct scsi$desc
{
unsigned int SCSI$L_OPCODE; /* SCSI Operation Code */
unsigned int SCSI$L_FLAGS; /* SCSI Flags Bit Map */
unsigned char *SCSI$A_CMD_ADDR; /* ->SCSI Command Buffer */
unsigned int SCSI$L_CMD_LEN; /* SCSI Command Length (bytes) */
unsigned char *SCSI$A_DATA_ADDR; /* ->SCSI Data Buffer */
unsigned int SCSI$L_DATA_LEN; /* SCSI Data Length (bytes) */
unsigned int SCSI$L_PAD_LEN; /* SCSI Pad Length (bytes) */
unsigned int SCSI$L_PH_CH_TMOUT; /* SCSI Phase Change Timeout (seconds) */
unsigned int SCSI$L_DISCON_TMOUT; /* SCSI Disconnect Timeout (seconds) */
unsigned int SCSI$L_RES_1; /* Reserved */
unsigned int SCSI$L_RES_2; /* Reserved */
unsigned int SCSI$L_RES_3; /* Reserved */
unsigned int SCSI$L_RES_4; /* Reserved */
unsigned int SCSI$L_RES_5; /* Reserved */
unsigned int SCSI$L_RES_6; /* Reserved */
}
SCSI$DESC;
/*
Define the SCSI Input/Output Status Block.
*/
typedef struct scsi$iosb
{
unsigned short SCSI$W_VMS_STAT; /* VMS Status Code */
unsigned long SCSI$L_IOSB_TFR_CNT; /* Actual Byte Count Transferred */
unsigned char SCSI$B_IOSB_FILL_1; /* Unused */
unsigned char SCSI$B_IOSB_STS; /* SCSI Device Status */
}
SCSI$IOSB;
/*
Define the VMS symbolic representation for a successful SCSI command.
*/
#define SCSI$K_GOOD 0
/*
Define the SCSI Flag Field Constants.
*/
#define SCSI$K_WRITE 0x0 /* Data Transfer Direction: Write */
#define SCSI$K_READ 0x1 /* Data Transfer Direction: Read */
#define SCSI$K_FL_ENAB_DIS 0x2 /* Enable Disconnect/Reconnect */
/*
Define DESCR_CNT. It must be a power of two and large enough
for the maximum concurrent usage of descriptors.
*/
#define DESCR_CNT 16
#define MK_EFN 0 /* Event Flag Number */
#define FailureStatusP(Status) (~(Status) & 1)
static struct dsc$descriptor_s *descr(char *String)
{
static struct dsc$descriptor_s d_descrtbl[DESCR_CNT];
static unsigned short descridx = 0;
struct dsc$descriptor_s *d_ret = &d_descrtbl[descridx];
descridx = (descridx + 1) & (DESCR_CNT - 1);
d_ret->dsc$w_length = strlen((const char *) String);
d_ret->dsc$a_pointer = String;
d_ret->dsc$b_class = 0;
d_ret->dsc$b_dtype = 0;
return d_ret;
}
static int SCSI_OpenDevice(char *DeviceName)
{
unsigned long d_dev[2], iosb[2], Status;
union prvdef setprivs, newprivs;
unsigned long ismnt = 0;
unsigned long dvcls = 0;
unsigned long dchr2 = 0;
int DeviceFD = 0;
struct itmlst_3
{
unsigned short ilen;
unsigned short code;
unsigned long *returnP;
unsigned long ignored;
}
dvi_itmlst[] = {
{ 4, DVI$_MNT, 0 /*&ismnt*/, 0 },
{ 4, DVI$_DEVCLASS, 0 /*&dvcls*/, 0 },
{ 4, DVI$_DEVCHAR2, 0 /*&dchr2*/, 0 },
{ 0, 0, 0, 0 }
};
dvi_itmlst[0].returnP = &ismnt;
dvi_itmlst[1].returnP = &dvcls;
dvi_itmlst[2].returnP = &dchr2;
Status = sys$alloc(descr(DeviceName), 0, 0, 0, 0);
if (FailureStatusP(Status))
{
VMS_ExitCode = Status;
FatalError("cannot allocate device '%s' - %X\n", DeviceName, Status);
}
Status = sys$assign(descr(DeviceName), &DeviceFD, 0, 0);
if (FailureStatusP(Status))
{
VMS_ExitCode = Status;
FatalError("cannot open device '%s' - %X\n", DeviceName, Status);
}
Status = sys$getdviw(0, DeviceFD, 0, &dvi_itmlst, &iosb, 0, 0, 0);
if (FailureStatusP(Status))
{
VMS_ExitCode = Status;
FatalError("cannot $getdvi(1) on device '%s' - %X\n", DeviceName, Status);
}
if (FailureStatusP(Status = iosb[0]))
{
VMS_ExitCode = Status;
FatalError("cannot $getdvi(2) on device '%s' - %X\n", DeviceName, Status);
}
if (dvcls != DC$_TAPE)
{
VMS_ExitCode = SS$_IVDEVNAM;
FatalError("specified device is NOT a magtape: operation denied\n");
}
#ifndef __DECC
#ifndef DEV$M_SCSI
#define DEV$M_SCSI 0x1000000
#endif
#endif
if (~dchr2 & DEV$M_SCSI)
{
VMS_ExitCode = SS$_IVDEVNAM;
FatalError("specified magtape is NOT a SCSI device: operation denied\n");
}
#if USING_DEC_DRIVE | USING_LDRSET
#ifndef __DECC
#ifndef DEV$M_LDR
#define DEV$M_LDR 0x100000
#endif
#endif
if (~dchr2 & DEV$M_LDR)
{
VMS_ExitCode = SS$_IVDEVNAM;
FatalError("specified SCSI magtape does not have a loader: operation denied\n");
}
#endif
if (ismnt)
{
VMS_ExitCode = SS$_DEVMOUNT;
FatalError("specified device is mounted: operation denied\n");
}
ots$move5(0, 0, 0, sizeof(newprivs), &newprivs);
newprivs.prv$v_diagnose = 1;
newprivs.prv$v_log_io = 1;
newprivs.prv$v_phy_io = 1;
Status = sys$setprv(1, &newprivs, 0, 0);
if (FailureStatusP(Status))
{
VMS_ExitCode = Status;
FatalError("error enabling privs (diagnose,log_io,phy_io): operation denied\n");
}
Status = sys$setprv(1, 0, 0, &setprivs);
if (FailureStatusP(Status))
{
VMS_ExitCode = Status;
FatalError("error retrieving current privs: operation denied\n");
}
if (!setprivs.prv$v_diagnose)
{
VMS_ExitCode = SS$_NODIAGNOSE;
FatalError("DIAGNOSE privilege is required: operation denied\n");
}
if (!setprivs.prv$v_phy_io && !setprivs.prv$v_log_io)
{
VMS_ExitCode = SS$_NOPHY_IO;
FatalError("PHY_IO or LOG_IO privilege is required: operation denied\n");
}
return DeviceFD;
}
static void SCSI_CloseDevice(char *DeviceName, int DeviceFD)
{
unsigned long Status;
Status = sys$dassgn(DeviceFD);
if (FailureStatusP(Status))
FatalError("cannot close SCSI device '%s' - %X\n", DeviceName, Status);
}
static int SCSI_ExecuteCommand( int DeviceFD,
Direction_T Direction,
CDB_T *CDB,
int CDB_Length,
void *DataBuffer,
int DataBufferLength,
RequestSense_T *RequestSense)
{
SCSI$DESC cmd_desc;
SCSI$IOSB cmd_iosb;
unsigned long Status;
int Result;
memset(RequestSense, 0, sizeof(RequestSense_T));
/* Issue the QIO to send the SCSI Command. */
ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc);
cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */
cmd_desc.SCSI$A_CMD_ADDR = CDB;
cmd_desc.SCSI$L_CMD_LEN = CDB_Length;
cmd_desc.SCSI$A_DATA_ADDR = DataBuffer;
cmd_desc.SCSI$L_DATA_LEN = DataBufferLength;
cmd_desc.SCSI$L_PAD_LEN = 0;
cmd_desc.SCSI$L_PH_CH_TMOUT = 180; /* SCSI Phase Change Timeout (seconds) */
cmd_desc.SCSI$L_DISCON_TMOUT = 180; /* SCSI Disconnect Timeout (seconds) */
switch (Direction)
{
/*
NOTE: Do NOT include flag SCSI$K_FL_ENAB_SYNC.
It does NOT work for this case.
*/
case Input:
cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
break;
case Output:
cmd_desc.SCSI$L_FLAGS = SCSI$K_WRITE | SCSI$K_FL_ENAB_DIS;
break;
}
/* Issue the SCSI Command. */
Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0,
&cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0);
Result = SCSI$K_GOOD;
if (Status & 1)
Status = cmd_iosb.SCSI$W_VMS_STAT;
if (Status & 1)
Result = cmd_iosb.SCSI$B_IOSB_STS;
if (Result != SCSI$K_GOOD)
{
unsigned char RequestSenseCDB[6] =
{ 0x03 /* REQUEST_SENSE */, 0, 0, 0, sizeof(RequestSense_T), 0 };
printf("SCSI command error: %d - requesting sense data\n", Result);
/* Execute Request Sense to determine the failure reason. */
ots$move5(0, 0, 0, sizeof(cmd_desc), &cmd_desc);
cmd_desc.SCSI$L_OPCODE = 1; /* Only defined SCSI opcode... a VMS thing */
cmd_desc.SCSI$L_FLAGS = SCSI$K_READ | SCSI$K_FL_ENAB_DIS;
cmd_desc.SCSI$A_CMD_ADDR = RequestSenseCDB;
cmd_desc.SCSI$L_CMD_LEN = 6;
cmd_desc.SCSI$A_DATA_ADDR = RequestSense;
cmd_desc.SCSI$L_DATA_LEN = sizeof(RequestSense_T);
cmd_desc.SCSI$L_PH_CH_TMOUT = 180;
cmd_desc.SCSI$L_DISCON_TMOUT = 180;
/* Issue the QIO to send the Request Sense Command. */
Status = sys$qiow(MK_EFN, DeviceFD, IO$_DIAGNOSE, &cmd_iosb, 0, 0,
&cmd_desc, sizeof(cmd_desc), 0, 0, 0, 0);
if (FailureStatusP(Status))
{
printf("?Error returned from REQUEST_SENSE(1): %%X%08lX\n", Status);
sys$exit(Status);
}
/* Check the VMS Status from QIO. */
Status = cmd_iosb.SCSI$W_VMS_STAT;
if (FailureStatusP(Status))
{
printf("?Error returned from REQUEST_SENSE(2): %%X%08lX\n", Status);
sys$exit(Status);
}
}
return Result;
}
static void VMS_DefineStatusSymbols(void)
{
char SymbolName[32], SymbolValue[32];
int StorageElementNumber;
if (DataTransferElementFull)
{
/* Define MTX_DTE Symbol (environment variable) as 'FULL:n'. */
sprintf(SymbolValue, "FULL:%d",
DataTransferElementSourceStorageElementNumber);
lib$set_symbol(descr("MTX_DTE"), descr(SymbolValue), &2);
}
else
{
/* Define MTX_DTE Symbol (environment variable) as 'EMPTY'. */
lib$set_symbol(descr("MTX_DTE"), descr("EMPTY"), &2);
}
/* Define MTX_MSZ Symbol (environment variable) "Magazine SiZe" as 'n'. */
sprintf(SymbolValue, "%d", StorageElementCount);
lib$set_symbol(descr("MTX_MSZ"), descr(SymbolValue), &2);
for (StorageElementNumber = 1;
StorageElementNumber <= StorageElementCount;
StorageElementNumber++)
{
sprintf(SymbolName, "MTX_STE%02d", StorageElementNumber);
strcpy(SymbolValue,
(StorageElementFull[StorageElementNumber] ? "FULL" : "EMPTY"));
lib$set_symbol(descr(SymbolName), descr(SymbolValue), &2);
}
}