--- /dev/null
+CHANGES, mtx 1.2.16:
+- Fix a very special case that ADIC loaders presented to element status
+ parser
+- Fix a buffer overflow w/some weird hardware, courtesy of Lars
+ Fredricksen
+- Make version number include a "rel" so that the rpm will overrride
+ a "pre" version's rpm, i.e., --version will say "mtx 1.2.16rel".
+- Added TapeChanger Perl package for use with Amanda to the contrib
+ directory (see http://www.killfile.org/~tskirvin/software/tapechanger.html )
+ courtesy of Tim Skirvin.
+- Fixed spec file that was broken by 1.2.15 :-(, courtesy of Kenneth
+ Porter
+
+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.
+
+
--- /dev/null
+PLEASE CONTRIBUTE YOUR HARDWARE!
+--------------------------------
+If you are currently using mtx on hardware not listed below, please
+forward me the result of 'mtx inquiry' on your hardware, plus the first
+few lines of 'mtx status', so that I can add it to the list.
+That EMAIL address is:
+
+ eric@badtux.org
+
+Compilers:
+----------
+mtx requires the GNU 'make' command in order to build. On FreeBSD or other
+Unixes you must thus install 'gmake' and use that command for building.
+In addition, it has only been verified to compile correctly using the
+GNU "C" compiler. It may not compile with vendor "C" compilers, which
+typically tend to implement an earlier version of "C" than the version
+implemented by "gcc".
+
+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).
+
+Vendors:
+--------
+
+'mtx' tries to stay with generic SCSI media changer commands, thus
+works with most vendors' products (see list of tested media changers
+that follows). The following vendors have been singled out for special
+mention because they provide easily-accessible SCSI documentation via
+their web site, thus making it easier for people world-wide to
+contribute to 'mtx' development:
+
+ Tandberg Data http://www.tandberg.com
+ Exabyte/Ecrix http://www.exabyte.com
+ SpectraLogic http://www.spectralogic.com
+
+If contacting other companies asking them to provide easily-accessible
+SCSI documentation for their loaders, please be polite. Remember, several
+'mtx' contributors work for those "other" companies.
+
+----------------
+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'
+
+* DISC/NSM 3000-series DVD optical library (4 drives, 240 slot, 1 mail slot)
+ Limitations: Will export to mail slot, but won't import from it (yet).
+ 'transfer' command does not work because of mechanical limitations of
+ the hardware. Initial mtx status takes a while.
+ Vendor ID: 'NSM '
+ Product ID: 'NSM3000 '
+ Revision: '1140'
+
+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
+
--- /dev/null
+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. Please
+ read the man page for directions.
+
+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.
+
--- /dev/null
+ 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.
+\f
+ 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.)
+\f
+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.
+\f
+ 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.
+\f
+ 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
--- /dev/null
+<!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 & GNU inquiries & 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>
--- /dev/null
+# 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.2.16rel
+BINS = mtx tapeinfo loaderinfo scsitape
+
+TARGET = @TARGET@
+CPU = @CPU@
+CC = @CC@
+INSTALL = @INSTALL@
+
+CFLAGS = @CFLAGS@
+CPPFLAGS = @CPPFLAGS@ -DVERSION="\"$(VERSION)\""
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+
+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@
+
+#
+# Linux on x86...
+#
+ifeq ($(TARGET),linux)
+CFLAGS += -Wall
+CPPFLAGS += -I/usr/src/linux/include -DLONG_PRINT_REQUEST_SENSE=1
+endif
+
+#
+# FreeBSD on x86...
+#
+ifeq ($(TARGET),freebsd86)
+CFLAGS += -m486
+CPPFLAGS += -I/usr/src/linux/include -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
+
+all: $(BINS)
+
+install: $(BINS)
+ for file in $(BINS); do \
+ strip $$file; \
+ done
+ $(INSTALL_DIR) $(sbindir)
+ $(INSTALL_BIN) $(BINS) $(sbindir)
+ $(INSTALL_DIR) $(mandir) $(mandir)/man1
+ $(INSTALL_DOC) mtx.1 tapeinfo.1 scsitape.1 loaderinfo.1 $(mandir)/man1
+
+clean:
+ rm -f *.o *~
+ rm -f $(BINS)
+ rm -f mam2debug mam2debug2
+
+distclean: clean
+ rm -f Makefile config.log config.cache config.status
+
+dist: distclean
+ ./makedist $(VERSION)
+
+loaderinfo: loaderinfo.o mtxl.o mtxl.h mtx.h $(EXTRA)
+ $(CC) $(LDFLAGS) -o loaderinfo loaderinfo.o mtxl.o $(EXTRA) $(LIBS)
+
+
+mtx: mtx.o mtxl.o mtxl.h mtx.h $(EXTRA)
+ $(CC) $(LDFLAGS) -o mtx mtx.o mtxl.o $(EXTRA) $(LIBS)
+
+mam2debug: mtxl.o mam2debug.o mtx.h $(EXTRA)
+ $(CC) $(LDFLAGS) -o mam2debug mtxl.o mam2debug.o $(EXTRA) $(LIBS)
+
+tapeinfo: tapeinfo.o mtxl.o mtx.h mtxl.h $(EXTRA)
+ $(CC) $(LDFLAGS) -o tapeinfo tapeinfo.o mtxl.o $(EXTRA) $(LIBS)
+
+mam2debug2: mtxl.o mam2debug2.o mtx.h $(EXTRA)
+ $(CC) $(LDFLAGS) -o mam2debug2 mtxl.o mam2debug2.o $(EXTRA) $(LIBS)
+
+scsitape: scsitape.o mtxl.o mtxl.h mtx.h $(EXTRA)
+ $(CC) $(LDFLAGS) -o scsitape scsitape.o mtxl.o $(EXTRA) $(LIBS)
+
+scsitape.o: scsitape.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 mtxl.h scsi_linux.c
--- /dev/null
+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@estinc.com>. 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@estinc.com>
--- /dev/null
+1 Add a 'timeout' command to adjust the timeout (timeout will be in seconds!).
+1 Adjust timeout lenghts for various operations now that the low level
+ SCSI routines now support timeouts.
+1 Add IES command (sets CDB[5] to something in routine Inventory() ).
+1 Increase the timeout for Inventory() commands to something *BIG*.
+1 Fix 'scsitape' READ and WRITE so that block counts work.
+2 Fix ports to other Unixes/VMS.
+2 Port to AIX (no longer care about SCO Unix).
+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?
+3 Create a TCL/TK GUI front end (otherwise managing big libraries is HARD!).
+
--- /dev/null
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+#
+# This file 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.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# The master version of this file is at the FSF in /home/gd/gnu/lib.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit system type (host/target name).
+#
+# Only a few systems have been added to this list; please add others
+# (but try to keep the structure clean).
+#
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 8/24/94.)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy; exit 1' 1 2 15
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ cat <<EOF >$dummy.s
+ .globl main
+ .ent main
+main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ ${CC-cc} $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr [[A-Z]] [[a-z]]`
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-cbm-sysv4
+ exit 0;;
+ amiga:NetBSD:*:*)
+ echo m68k-cbm-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ arc64:OpenBSD:*:*)
+ echo mips64el-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hkmips:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mips-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ arm32:NetBSD:*:*)
+ echo arm-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ SR2?01:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:*|MIS*:OSx*:*:*|MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:NetBSD:*:*)
+ echo m68k-atari-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:NetBSD:*:*)
+ echo m68k-sun-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3*:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:NetBSD:*:*)
+ echo m68k-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:NetBSD:*:*)
+ echo powerpc-apple-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ sed 's/^ //' << EOF >$dummy.c
+ int main (argc, argv) int argc; char **argv; {
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ ${CC-cc} $dummy.c -o $dummy \
+ && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \
+ -o ${TARGET_BINARY_INTERFACE}x = x ] ; then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i?86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ ${CC-cc} $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:4)
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -EHl ${IBM_CPU_ID} | grep POWER >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=4.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/6?? | 9000/7?? | 9000/80[24] | 9000/8?[13679] | 9000/892 )
+ sed 's/^ //' << EOF >$dummy.c
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (${CC-cc} $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+ rm -f $dummy.c $dummy
+ esac
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ ${CC-cc} $dummy.c -o $dummy && ./$dummy && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i?86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*X-MP:*:*:*)
+ echo xmp-cray-unicos
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo t3e-cray-unicosmk${UNAME_RELEASE}
+ exit 0 ;;
+ CRAY-2:*:*:*)
+ echo cray2-cray-unicos
+ exit 0 ;;
+ F300:UNIX_System_V:*:*)
+ FUJITSU_SYS=`uname -p | tr [A-Z] [a-z] | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ F301:UNIX_System_V:*:*)
+ echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'`
+ exit 0 ;;
+ hp3[0-9][05]:NetBSD:*:*)
+ echo m68k-hp-netbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ i?86:BSD/386:*:* | i?86:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:NetBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:Linux:*:*)
+ # uname on the ARM produces all sorts of strangeness, and we need to
+ # filter it out.
+ case "$UNAME_MACHINE" in
+ arm* | sa110*) UNAME_MACHINE="arm" ;;
+ esac
+
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us.
+ ld_help_string=`ld --help 2>&1`
+ ld_supported_emulations=`echo $ld_help_string \
+ | sed -ne '/supported emulations:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported emulations: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_emulations" in
+ i?86linux) echo "${UNAME_MACHINE}-pc-linux-gnuaout" ; exit 0 ;;
+ i?86coff) echo "${UNAME_MACHINE}-pc-linux-gnucoff" ; exit 0 ;;
+ sparclinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+ armlinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+ m68klinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;;
+ elf32ppc) echo "powerpc-unknown-linux-gnu" ; exit 0 ;;
+ esac
+
+ if test "${UNAME_MACHINE}" = "alpha" ; then
+ sed 's/^ //' <<EOF >$dummy.s
+ .globl main
+ .ent main
+ main:
+ .frame \$30,0,\$26,0
+ .prologue 0
+ .long 0x47e03d80 # implver $0
+ lda \$2,259
+ .long 0x47e20c21 # amask $2,$1
+ srl \$1,8,\$2
+ sll \$2,2,\$2
+ sll \$0,3,\$0
+ addl \$1,\$0,\$0
+ addl \$2,\$0,\$0
+ ret \$31,(\$26),1
+ .end main
+EOF
+ LIBC=""
+ ${CC-cc} $dummy.s -o $dummy 2>/dev/null
+ if test "$?" = 0 ; then
+ ./$dummy
+ case "$?" in
+ 7)
+ UNAME_MACHINE="alpha"
+ ;;
+ 15)
+ UNAME_MACHINE="alphaev5"
+ ;;
+ 14)
+ UNAME_MACHINE="alphaev56"
+ ;;
+ 10)
+ UNAME_MACHINE="alphapca56"
+ ;;
+ 16)
+ UNAME_MACHINE="alphaev6"
+ ;;
+ esac
+
+ objdump --private-headers $dummy | \
+ grep ld.so.1 > /dev/null
+ if test "$?" = 0 ; then
+ LIBC="libc1"
+ fi
+ fi
+ rm -f $dummy.s $dummy
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0
+ elif test "${UNAME_MACHINE}" = "mips" ; then
+ cat >$dummy.c <<EOF
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef __MIPSEB__
+ printf ("%s-unknown-linux-gnu\n", argv[1]);
+#endif
+#ifdef __MIPSEL__
+ printf ("%sel-unknown-linux-gnu\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ ${CC-cc} $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ else
+ # Either a pre-BFD a.out linker (linux-gnuoldld)
+ # or one that does not give us useful --help.
+ # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout.
+ # If ld does not provide *any* "supported emulations:"
+ # that means it is gnuoldld.
+ echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:"
+ test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0
+
+ case "${UNAME_MACHINE}" in
+ i?86)
+ VENDOR=pc;
+ ;;
+ *)
+ VENDOR=unknown;
+ ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ cat >$dummy.c <<EOF
+#include <features.h>
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef __ELF__
+# ifdef __GLIBC__
+# if __GLIBC__ >= 2
+ printf ("%s-${VENDOR}-linux-gnu\n", argv[1]);
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+# else
+ printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+ printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]);
+#endif
+ return 0;
+}
+EOF
+ ${CC-cc} $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm $dummy.c $dummy && exit 0
+ rm -f $dummy.c $dummy
+ fi ;;
+# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions
+# are messed up and put the nodename in both sysname and nodename.
+ i?86:DYNIX/ptx:4*:*)
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i?86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*)
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ i?86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ i?86:UnixWare:*:*)
+ if /bin/uname -X 2>/dev/null >/dev/null ; then
+ (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ fi
+ echo ${UNAME_MACHINE}-unixware-${UNAME_RELEASE}-${UNAME_VERSION}
+ exit 0 ;;
+ pc:*:*:*)
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ i?86:LynxOS:2.*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:*:6*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R3000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R4000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+ printf ("vax-dec-bsd\n"); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+${CC-cc} $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+#echo '(Unable to guess system type)' 1>&2
+
+exit 1
--- /dev/null
+/* config.h. Generated automatically 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: 2001/06/05 17:10:18 $
+ * $Revision: 1.1.1.1 $
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H 1
+
+/* autoconf changes these. */
+#define HAVE_STRING_H 1
+#define HAVE_UNISTD_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STDARG_H 1
+#define HAVE_SCSI_SCSI_H 1
+#define HAVE_SCSI_SCSI_IOCTL_H 1
+#define HAVE_SCSI_SG_H 1
+#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 1
+
+#define WORDS_BIGENDIAN 0
+
+#endif
+
--- /dev/null
+/* 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: 2001/06/05 17:10:18 $
+ * $Revision: 1.1.1.1 $
+ */
+
+#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_SCSI_SCSI_H 0
+#define HAVE_SCSI_SCSI_IOCTL_H 0
+#define HAVE_SCSI_SG_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 WORDS_BIGENDIAN 0
+
+#endif
+
--- /dev/null
+#! /bin/sh
+# Configuration validation subroutine script, version 1.1.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+# Free Software Foundation, Inc.
+#
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file 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.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+if [ x$1 = x ]
+then
+ echo Configuration name missing. 1>&2
+ echo "Usage: $0 CPU-MFR-OPSYS" 1>&2
+ echo "or $0 ALIAS" 1>&2
+ echo where ALIAS is a recognized configuration type. 1>&2
+ exit 1
+fi
+
+# First pass through any local machine types.
+case $1 in
+ *local*)
+ echo $1
+ exit 0
+ ;;
+ *)
+ ;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \
+ | arme[lb] | pyramid | mn10200 | mn10300 | tron | a29k \
+ | 580 | i960 | h8300 \
+ | x86 | ppcbe | mipsbe | mipsle | shbe | shle | armbe | armle \
+ | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \
+ | alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \
+ | alphaev6[78] \
+ | we32k | ns16k | clipper | i370 | sh | powerpc | powerpcle \
+ | 1750a | dsp16xx | pdp11 | mips16 | mips64 | mipsel | mips64el \
+ | mips64orion | mips64orionel | mipstx39 | mipstx39el \
+ | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
+ | mips64vr5000 | miprs64vr5000el | mcore \
+ | sparc | sparclet | sparclite | sparc64 | sparcv9 | v850 | c4x \
+ | powerpc64 | sparcv8 | supersparc | microsparc | ultrasparc \
+ | thumb | d10v | fr30 | avr)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | h8500 | w65 | pj | pjl)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i[34567]86 | pentium[23] | k[56] | k6[23] | athlon)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ vax-* | tahoe-* | i[34567]86-* | pentium[23]-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \
+ | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \
+ | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
+ | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \
+ | xmp-* | ymp-* \
+ | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* | armbe-* | armle-* \
+ | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* | hppa2.0n-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \
+ | alphaev6[78]-* \
+ | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
+ | clipper-* | orion-* \
+ | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \
+ | sparc64-* | sparcv9-* | sparc86x-* | mips16-* | mips64-* | mipsel-* \
+ | mips64el-* | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
+ | mipstx39-* | mipstx39el-* | mcore-* \
+ | f301-* | armv*-* | s390-* | sv1-* | t3e-* \
+ | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \
+ | k[56]-* | k6[23]-* | athlon-* | powerpc64-* \
+ | sparcv8-* | supersparc-* | microsparc-* | ultrasparc-* \
+ | thumb-* | v850-* | d30v-* | tic30-* | c30-* | fr30-* )
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-cbm
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-cbm
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-cbm
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [ctj]90-cray)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ basic_machine=hppa2.0-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ basic_machine=hppa2.0-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i[34567]86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i[34567]86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i[34567]86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i[34567]86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ i386-go32 | go32)
+ basic_machine=i386-unknown
+ os=-go32
+ ;;
+ i386-mingw32 | mingw32)
+ basic_machine=i386-unknown
+ os=-mingw32
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ macppc*)
+ basic_machine=powerpc-apple
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ msdos)
+ basic_machine=i386-unknown
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentiummmx | p55)
+ basic_machine=pentiummmx-pc
+ ;;
+ pentium | p5 | i586)
+ basic_machine=pentium-pc
+ ;;
+ pentiumpro | p6)
+ basic_machine=pentiumpro-pc
+ ;;
+ pentiummmx-* | p55-*)
+ basic_machine=pentiummmx-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium-* | p5-* | i586-*)
+ basic_machine=pentium-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-*)
+ basic_machine=pentiumpro-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ nexen)
+ # We don't have specific support for Nexgen yet, so just call it a Pentium
+ basic_machine=i586-nexgen
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=rs6000-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*)
+ basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sparclite-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=t3e-cray
+ os=-unicos
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sparc | sparcv9)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ c4x*)
+ basic_machine=c4x-none
+ os=-coff
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i[34567]86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto*)
+ os=-nto-qnx
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -*MiNT)
+ os=-mint
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f301-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -*MiNT)
+ vendor=atari
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
--- /dev/null
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_default_prefix=/usr/local
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=mtx.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+# Do some error checking and defaulting for the host and target type.
+# The inputs are:
+# configure --host=HOST --target=TARGET --build=BUILD NONOPT
+#
+# The rules are:
+# 1. You are not allowed to specify --host, --target, and nonopt at the
+# same time.
+# 2. Host defaults to nonopt.
+# 3. If nonopt is not specified, then host defaults to the current host,
+# as determined by config.guess.
+# 4. Target and build default to nonopt.
+# 5. If nonopt is not specified, then target and build default to host.
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+case $host---$target---$nonopt in
+NONE---*---* | *---NONE---* | *---*---NONE) ;;
+*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;;
+esac
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:575: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+ case $nonopt in
+ NONE)
+ if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+ else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+ fi ;;
+ *) host_alias=$nonopt ;;
+ esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+echo $ac_n "checking target system type""... $ac_c" 1>&6
+echo "configure:596: checking target system type" >&5
+
+target_alias=$target
+case "$target_alias" in
+NONE)
+ case $nonopt in
+ NONE) target_alias=$host_alias ;;
+ *) target_alias=$nonopt ;;
+ esac ;;
+esac
+
+target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias`
+target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$target" 1>&6
+
+echo $ac_n "checking build system type""... $ac_c" 1>&6
+echo "configure:614: checking build system type" >&5
+
+build_alias=$build
+case "$build_alias" in
+NONE)
+ case $nonopt in
+ NONE) build_alias=$host_alias ;;
+ *) build_alias=$nonopt ;;
+ esac ;;
+esac
+
+build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias`
+build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$build" 1>&6
+
+test "$host_alias" != "$target_alias" &&
+ test "$program_prefix$program_suffix$program_transform_name" = \
+ NONENONEs,x,x, &&
+ program_prefix=${target_alias}-
+
+
+
+case "$host_os" in
+ *linux*) cat >> confdefs.h <<\EOF
+#define LINUX 1
+EOF
+
+ TARGET=linux
+ ;;
+ *solaris*) cat >> confdefs.h <<\EOF
+#define SOLARIS 1
+EOF
+
+ TARGET=solarissparc
+ ;;
+ *sunos*) TARGET=solarissparc
+ ;;
+ *freebsd*) TARGET=freebsd86
+ ;;
+ *irix*) TARGET=sgi
+ ;;
+ *hp*) TARGET=hpux
+ ;;
+ *HP*) TARGET=hpux
+ ;;
+ *sequent*) cat >> confdefs.h <<\EOF
+#define SEQUENT 1
+EOF
+
+ ;;
+ *) TARGET=$host_os
+ ;;
+esac
+
+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
+
+
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:685: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:715: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:766: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:798: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 809 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:814: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:840: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:845: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:854: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:873: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:916: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:970: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 985 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:991: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 1002 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1008: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 1019 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1025: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1050: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1055 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1063: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1080 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1098 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ :
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1119 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1130: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+for ac_hdr in \
+ unistd.h \
+ stdlib.h \
+ errno.h \
+ fcntl.h \
+ stdarg.h \
+ string.h \
+ scsi/scsi.h \
+ scsi/scsi_ioctl.h \
+ scsi/sg.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 \
+ dslib.h \
+ du/defs.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1182: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1187 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1192: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:1220: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1225 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this. */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this. */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this. */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in an arm
+ of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:1274: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_c_const=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+ cat >> confdefs.h <<\EOF
+#define const
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:1295: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1300 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_size_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+ cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+echo $ac_n "checking for pid_t""... $ac_c" 1>&6
+echo "configure:1328: checking for pid_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1333 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_pid_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_pid_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_pid_t" 1>&6
+if test $ac_cv_type_pid_t = no; then
+ cat >> confdefs.h <<\EOF
+#define pid_t int
+EOF
+
+fi
+
+echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
+echo "configure:1361: checking whether time.h and sys/time.h may both be included" >&5
+if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1366 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+int main() {
+struct tm *tp;
+; return 0; }
+EOF
+if { (eval echo configure:1375: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_header_time=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_time=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_time" 1>&6
+if test $ac_cv_header_time = yes; then
+ cat >> confdefs.h <<\EOF
+#define TIME_WITH_SYS_TIME 1
+EOF
+
+fi
+
+echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6
+echo "configure:1396: checking whether struct tm is in sys/time.h or time.h" >&5
+if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1401 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <time.h>
+int main() {
+struct tm *tp; tp->tm_sec;
+; return 0; }
+EOF
+if { (eval echo configure:1409: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_struct_tm=time.h
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_struct_tm=sys/time.h
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_struct_tm" 1>&6
+if test $ac_cv_struct_tm = sys/time.h; then
+ cat >> confdefs.h <<\EOF
+#define TM_IN_SYS_TIME 1
+EOF
+
+fi
+
+echo $ac_n "checking size of int""... $ac_c" 1>&6
+echo "configure:1430: checking size of int" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1438 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(int));
+ exit(0);
+}
+EOF
+if { (eval echo configure:1449: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_int=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_int=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_int" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+EOF
+
+
+echo $ac_n "checking size of long""... $ac_c" 1>&6
+echo "configure:1469: checking size of long" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1477 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(long));
+ exit(0);
+}
+EOF
+if { (eval echo configure:1488: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_long=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_long=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+EOF
+
+
+echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
+echo "configure:1508: checking whether byte ordering is bigendian" >&5
+if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_cv_c_bigendian=unknown
+# See if sys/param.h defines the BYTE_ORDER macro.
+cat > conftest.$ac_ext <<EOF
+#line 1515 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1526: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+cat > conftest.$ac_ext <<EOF
+#line 1530 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1541: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_c_bigendian=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_c_bigendian=no
+fi
+rm -f conftest*
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+if test $ac_cv_c_bigendian = unknown; then
+if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1561 "configure"
+#include "confdefs.h"
+main () {
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+if { (eval echo configure:1574: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_c_bigendian=no
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_c_bigendian=yes
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_c_bigendian" 1>&6
+if test $ac_cv_c_bigendian = yes; then
+ cat >> confdefs.h <<\EOF
+#define WORDS_BIGENDIAN 1
+EOF
+
+fi
+
+
+
+
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:1602: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1607 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:1624: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_type_signal=void
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+echo $ac_n "checking for vprintf""... $ac_c" 1>&6
+echo "configure:1643: checking for vprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1648 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char vprintf(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char vprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_vprintf) || defined (__stub___vprintf)
+choke me
+#else
+vprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1671: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_VPRINTF 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test "$ac_cv_func_vprintf" != yes; then
+echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
+echo "configure:1695: checking for _doprnt" >&5
+if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1700 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char _doprnt(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char _doprnt();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+_doprnt();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1723: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_DOPRNT 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@target@%$target%g
+s%@target_alias@%$target_alias%g
+s%@target_cpu@%$target_cpu%g
+s%@target_vendor@%$target_vendor%g
+s%@target_os@%$target_os%g
+s%@build@%$build%g
+s%@build_alias@%$build_alias%g
+s%@build_cpu@%$build_cpu%g
+s%@build_vendor@%$build_vendor%g
+s%@build_os@%$build_os%g
+s%@TARGET@%$TARGET%g
+s%@CPU@%$CPU%g
+s%@CC@%$CC%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@CPP@%$CPP%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
--- /dev/null
+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
+ ;;
+ *irix*) TARGET=sgi
+ ;;
+ *hp*) TARGET=hpux
+ ;;
+ *HP*) TARGET=hpux
+ ;;
+ *sequent*) AC_DEFINE(SEQUENT)
+ ;;
+ *) 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
+
+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 \
+ 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 \
+ dslib.h \
+ du/defs.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)
--- /dev/null
+<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->loadedtape;
+ print "Currently loaded: $loaded\n" if ($loaded);</PRE>
+<PRE>
+ TapeChanger::MTX->loadtape('next');
+ my $nowloaded = TapeChanger::MTX->loadedtape;
+ print "Currently loaded: $nowloaded\n" 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<loadedtape()>
+ prev C<loadprevtape()>
+ next C<loadnexttape()>
+ first C<loadfirsttape()>
+ last C<loadlasttape()>
+ 0 C<_ejectdrive()>
+ 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 <<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>></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 <<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>>.</P>
+
+</BODY>
+
+</HTML>
--- /dev/null
+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.
--- /dev/null
+#!/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. [NOTE FROM ERIC: Note that
+# this is now unmaintained, unless someone wishes to volunteer. In other
+# words, if you have a problem with this script, please fix it and forward
+# a new version of the script with your EMAIL address as the one to contact
+# about it :-) ]
+#
+# 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 $?
--- /dev/null
+#! /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
--- /dev/null
+# 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: mtx.py,v $
+# Revision 1.1.1.1 2001/06/05 17:10:51 elgreen
+# Initial import into SourceForge
+#
+# 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
+# =->==
+#
--- /dev/null
+<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->loadedtape;
+ print "Currently loaded: $loaded\n" if ($loaded);</PRE>
+<PRE>
+ TapeChanger::MTX->loadtape('next');
+ my $nowloaded = TapeChanger::MTX->loadedtape;
+ print "Currently loaded: $nowloaded\n" 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<loadedtape()>
+ prev C<loadprevtape()>
+ next C<loadnexttape()>
+ first C<loadfirsttape()>
+ last C<loadlasttape()>
+ 0 C<_ejectdrive()>
+ 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 <<A HREF="mailto:tskirvin@uiuc.edu">tskirvin@uiuc.edu</A>></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 <<A HREF="mailto:tskirvin@ks.uiuc.edu">tskirvin@ks.uiuc.edu</A>>.</P>
+
+</BODY>
+
+</HTML>
--- /dev/null
+# 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
+
--- /dev/null
+!/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;
+}
--- /dev/null
+#!/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;
+}
--- /dev/null
+#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
--- /dev/null
+/* 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;
+}
--- /dev/null
+#!/bin/sh
+# $Date: 2001/06/05 17:10:21 $
+# $Revision: 1.1.1.1 $
+#
+# 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
--- /dev/null
+.\" tapeinfo.1 Document copyright 2000 Eric Lee Green
+.\" Program Copyright 2000 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 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 Eric Lee Green <eric@badtux.org> formerly of
+Enhanced Software Technologies Inc. The 'mtx' home page is
+http://mtx.sourceforge.net and the actual code
+is currently available there and via CVS from
+http://sourceforge.net/projects/mtx/ .
+
+.SH SEE ALSO
+.BR mt (1), tapeinfo (1), mtx (1)
--- /dev/null
+/* Copyright 2000 Enhanced Software Technologies Inc.
+ * Released under terms of the GNU General Public License as
+ * required by the license on 'mtxl.c'.
+ */
+
+/*
+* $Date: 2001/06/05 17:10:21 $
+* $Revision: 1.1.1.1 $
+*/
+
+/* 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 Reserved;
+ unsigned char MT_Xfer; /* bits about whether mt->xx xfers work. */
+ unsigned char ST_Xfer; /* bits about whether st->xx xfers work. */
+ unsigned char IE_Xfer; /* bits about whether id->xx xfers work. */
+ unsigned char DT_Xfer; /* bits about whether DT->xx xfers work. */
+ unsigned char Reserved2[12]; /* 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 ... */
+ return; /* done! */
+}
+
+/*********** 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, int 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]=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. */
+ }
+
+ /* 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 EAAP_Type *ReportEAAP(DEVICE_TYPE MediumChangerFD) {
+ EAAP_Type *EAAP;
+ RequestSense_T RequestSense;
+
+ EAAP=(EAAP_Type *)mode_sense(MediumChangerFD,0x1d,sizeof(EAAP_Type),&RequestSense);
+
+ if (EAAP==NULL) {
+ printf("EAAP: No\n");
+ return NULL;
+ }
+
+ /* 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 Element 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]));
+
+ return 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");
+ }
+
+ return; /* done. */
+}
+
+/* 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).
+ */
+
+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");
+
+ /* okay, now see if we can do xfers: */
+ if (result->ST_Xfer & ST_BIT) {
+ printf("Can Transfer: Yes\n");
+ } else {
+ printf("Can Transfer: No\n");
+ }
+ /* We don't care about anything else at the moment, eventually we
+ * do want to add the inport/export stuff here too...
+ */
+
+ return;
+}
+
+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);
+}
--- /dev/null
+#!/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 -czvhf mtx-${1}.tar.gz mtx-${1}
+
--- /dev/null
+/* Mammoth 2 Debug Buffer Dumper
+ Copyright 2000 Enhanced Software Technologies Inc.
+
+$Date: 2001/06/05 17:10:21 $
+$Revision: 1.1.1.1 $
+
+ Written by Eric Lee Green <eric@estinc.com>
+ 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);
+}
+
--- /dev/null
+/* Mammoth 2 Debug Buffer Dumper
+ Copyright 2000 Enhanced Software Technologies Inc.
+
+$Date: 2001/06/05 17:10:21 $
+$Revision: 1.1.1.1 $
+
+ Written by Eric Lee Green <eric@estinc.com>
+ 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);
+}
+
--- /dev/null
+.\" mtx.1 Document copyright 2000 Eric Lee Green
+.\" Program Copyright 1996, 1997 Leonard Zubkoff
+.\" Extensive changes 2000 by Eric Lee Green <eric@estinc.com>
+.\"
+.\" 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.2
+.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 first [<drivenum>]
+Loads drive <drivenum> from the first slot in the media changer. Unloads
+the drive if there is already media in it. 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.
+.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.
+
+.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>, to whom all problems should
+be reported for this revision. 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.
+Report problems to Eric Lee Green <eric@badtux.org>.
+.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 \*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 EST
+web site at http://www.estinc.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 Eric Lee Green <eric@badtux.org> formerly of
+Enhanced Software Technologies Inc. The 'mtx' home page is
+http://mtx.sourceforge.net and the actual code
+is currently available there and via CVS from
+http://sourceforge.net/projects/mtx/ .
+
+.SH SEE ALSO
+.BR mt (1), tapeinfo (1), scsitape(1), loaderinfo(1)
--- /dev/null
+/*
+
+ MTX -- SCSI Tape Attached Medium Changer Control Program
+ $Date: 2001/11/06 21:20:40 $
+ $Revision: 1.2.2.1 $
+
+ Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+ Now maintained by Eric Lee Green <eric@estinc.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.
+
+ The author respectfully requests that any modifications to this software be
+ sent directly to him for evaluation and testing.
+
+ Thanks to Philip A. Prindeville <philipp@enteka.com> of Enteka Enterprise
+ Technology Service for porting MTX to Solaris/SPARC.
+
+ Thanks to Carsten Koch <Carsten.Koch@icem.de> for porting MTX to SGI IRIX.
+
+ Thanks to TECSys Development, Inc. for porting MTX to Digital Unix and
+ OpenVMS.
+
+ Changes Feb 2000 Eric Lee Green <eric@estinc.com> to add support for
+ multi-drive tape changers, extract out library stuff into mtxl.c,
+ and otherwise bring things up to date for dealing with LARGE tape jukeboxes
+ and other such enterprise-class storage subsystems.
+
+*/
+
+char *argv0;
+
+#include "mtx.h" /* various defines for bit order etc. */
+#include "mtxl.h"
+
+/* A table for printing out the peripheral device type as ASCII. */
+static char *PeripheralDeviceType[32] = {
+ "Disk Drive", /* 0 */
+ "Tape Drive", /* 1 */
+ "Printer", /* 2 */
+ "Processor", /* 3 */
+ "Write-once", /* 4 */
+ "CD-ROM", /* 5 */
+ "Scanner", /* 6 */
+ "Optical", /* 7 */
+ "Medium Changer", /* 8 */
+ "Communications", /* 9 */
+ "ASC IT8", /* a */
+ "ASC IT8", /* b */
+ "RAID Array", /* c */
+ "Enclosure Services", /* d */
+ "RBC Simplified Disk", /* e */
+ "OCR/W", /* f */
+ "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 */
+};
+
+static int argc;
+static char **argv;
+
+
+
+
+static char *device=NULL; /* the device name passed as argument */
+
+/* Unfortunately this must be true for SGI, because SGI does not
+ use an int :-(.
+*/
+
+static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) 0;
+static int device_opened = 0; /* okay, replace check here. */
+
+/* was: static int MediumChangerFD=-1; *//* open filehandle to that device */
+static int arg1=-1; /* first arg to command */
+static int arg2=-1; /* second arg to command */
+
+static SCSI_Flags_T SCSI_Flags = { 0, 0, 0,0 };
+
+/* static int invert_bit=0;*/ /* we by default do not invert... */
+/* static int eepos=0; */ /* the extend thingy for import/export. */
+static Inquiry_T *inquiry_info; /* needed by MoveMedium etc... */
+static ElementStatus_T *ElementStatus = NULL;
+
+
+/* pre-defined commands: */
+static void ReportInquiry(void);
+static void Status(void);
+static void Load(void);
+static void Unload(void);
+static void First(void);
+static void Last(void);
+static void Next(void);
+/* static void Previous(void); */
+static void InvertCommand(void);
+static void Transfer(void);
+static void Eepos(void);
+static void NoAttach(void);
+static void Version(void);
+static void do_Inventory(void);
+static void do_Unload(void);
+static void do_Erase(void);
+static void NoBarCode(void);
+
+struct command_table_struct {
+ int num_args;
+ char *name;
+ void (*command)(void);
+ int need_device;
+ int need_status;
+} command_table[] = {
+ { 0, "inquiry",ReportInquiry, 1,0},
+ { 0, "status", Status, 1,1 },
+ { 0, "invert", InvertCommand, 0,0},
+ { 0, "noattach",NoAttach,0,0},
+ { 1, "eepos", Eepos, 0,0},
+ { 2, "load", Load, 1,1 },
+ { 2, "unload", Unload, 1,1 },
+ { 2, "transfer", Transfer, 1,1 },
+ { 1, "first", First, 1,1 },
+ { 1, "last", Last, 1,1 },
+ { 1, "next", Next, 1,1 },
+ { 0, "--version", Version, 0,0 },
+ { 0, "inventory", do_Inventory, 1,0},
+ { 0, "eject", do_Unload, 1, 0},
+ { 0, "erase", do_Erase, 1, 0},
+ { 0, "nobarcode", NoBarCode, 0,0},
+ { 0, NULL, NULL }
+};
+
+static void Usage()
+{
+ fprintf(stderr, "Usage:\n\
+ mtx --version\n\
+ mtx [ -f <loader-dev> ] noattach <more commands>\n\
+ mtx [ -f <loader-dev> ] inquiry | inventory \n\
+ mtx [ -f <loader-dev> ] [nobarcode] status\n\
+ mtx [ -f <loader-dev> ] first [<drive#>]\n\
+ mtx [ -f <loader-dev> ] last [<drive#>]\n\
+ mtx [ -f <loader-dev> ] next [<drive#>]\n\
+ mtx [ -f <loader-dev> ] previous [<drive#>]\n\
+ mtx [ -f <loader-dev> ] [invert] load <storage-element-number> [<drive#>]\n\
+ mtx [ -f <loader-dev> ] [invert] unload [<storage-element-number>][<drive#>]\n\
+ mtx [ -f <loader-dev> ] [eepos eepos-number] transfer <storage-element-number> <storage-element-number>\n\
+ mtx [ -f <device> ] eject\n");
+
+#ifndef VMS
+ exit(1);
+#else
+ sys$exit(VMS_ExitCode);
+#endif
+}
+
+
+
+static void Version(void) {
+ fprintf(stderr,"mtx version %s\n\n",VERSION);
+ Usage();
+}
+
+static void NoAttach(void) {
+ SCSI_Flags.no_attached=1;
+}
+
+static void InvertCommand(void) {
+ SCSI_Flags.invert=1;
+ /* invert_bit=1;*/
+}
+
+static void NoBarCode(void) {
+ SCSI_Flags.no_barcodes=1; /* don't request barcodes, sigh! */
+}
+
+/* First and Last are easy. Next is the bitch. */
+static void First(void){
+ int driveno;
+ /* okay, first see if we have a drive#: */
+ if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
+ driveno=arg1;
+ } else {
+ driveno = 0;
+ }
+ /* now see if there's anything *IN* that drive: */
+ if (ElementStatus->DataTransferElementFull[driveno]) {
+ /* if so, then unload it... */
+ arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
+ if (arg1==1) {
+ printf("loading...done.\n"); /* it already has tape #1 in it! */
+ return;
+ }
+ arg2=driveno;
+ Unload();
+ }
+ /* and now to actually do the Load(): */
+ arg2=driveno;
+ arg1=1; /* first! */
+ Load(); /* and voila! */
+
+}
+
+static void Last(void) {
+ int driveno;
+ /* okay, first see if we have a drive#: */
+ if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
+ driveno=arg1;
+ } else {
+ driveno = 0;
+ }
+
+ /* now see if there's anything *IN* that drive: */
+ if (ElementStatus->DataTransferElementFull[driveno]) {
+ /* if so, then unload it... */
+ arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
+ if (arg1==ElementStatus->StorageElementCount) {
+ printf("loading...done.\n"); /* it already has last tape in it! */
+ return;
+ }
+ arg2=driveno;
+ Unload();
+ }
+
+ arg1 = ElementStatus->StorageElementCount; /* the last slot... */
+ arg2=driveno;
+ Load(); /* and load it, sigh! */
+
+}
+
+
+static void Next(void) {
+ int driveno;
+ int current=0;
+ /* okay, first see if we have a drive#: */
+ if (arg1 >= 0 && arg1 < ElementStatus->DataTransferElementCount) {
+ driveno=arg1;
+ } else {
+ driveno = 0;
+ }
+
+ /* Now to see if there's anything in that drive! */
+ if (ElementStatus->DataTransferElementFull[driveno]) {
+ /* if so, unload it! */
+ arg1=ElementStatus->DataTransferElementSourceStorageElementNumber[driveno]+1;
+ current=arg1;
+ arg2=driveno;
+ Unload();
+ }
+
+ /* okay, now to load, if we can... */
+ current++;
+ if (current > ElementStatus->StorageElementCount) { /* the last slot... */
+ FatalError("No More Tapes\n");
+ }
+ arg1=current;
+ arg2=driveno;
+ Load();
+}
+
+static void do_Inventory(void)
+{
+ int i;
+ i=Inventory(MediumChangerFD);
+ if (i < 0) {
+ fprintf(stderr,"mtx:inventory failed\n");
+ fflush(stderr);
+ exit(1); /* exit with an error status. */
+ }
+ return; /* if it failed, well, sigh.... */
+}
+
+/* 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 using the OS's
+ * native tools!
+ */
+static void do_Erase(void) {
+ RequestSense_T *RequestSense;
+ RequestSense=Erase(MediumChangerFD);
+ if (RequestSense) {
+ PrintRequestSense(RequestSense);
+ exit(1); /* exit with an error status. */
+ }
+ return;
+}
+
+
+/* This should eject a tape or magazine, depending upon the device sent
+ * to.
+ */
+static void do_Unload(void)
+{
+ int i;
+ i=Eject(MediumChangerFD);
+ if (i<0) {
+ fprintf(stderr,"mtx:eject failed\n");
+ fflush(stderr);
+ }
+ return; /* if it failed, well, sigh.... */
+}
+
+static void ReportInquiry(void)
+{
+ 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");
+ }
+ free(Inquiry); /* well, we're about to exit, but ... */
+}
+
+static void Status(void)
+{
+ int StorageElementNumber;
+ int TransferElementNumber;
+ printf(" Storage Changer %s:%d Drives, %d Slots ( %d Import/Export )\n",
+ device,
+ ElementStatus->DataTransferElementCount,
+ ElementStatus->StorageElementCount,
+ ElementStatus->ImportExportCount);
+
+
+ for (TransferElementNumber=0;TransferElementNumber <
+ ElementStatus->DataTransferElementCount;TransferElementNumber++) {
+
+ printf("Data Transfer Element %d:",TransferElementNumber);
+ if (ElementStatus->DataTransferElementFull[TransferElementNumber])
+ {
+ if (ElementStatus->DataTransferElementSourceStorageElementNumber[TransferElementNumber] > -1)
+ printf("Full (Storage Element %d Loaded)",
+ ElementStatus->DataTransferElementSourceStorageElementNumber[TransferElementNumber]+1);
+ else printf("Full (Unknown Storage Element Loaded)");
+ if
+ (ElementStatus->DataTransferPrimaryVolumeTag[TransferElementNumber][0])
+ printf(":VolumeTag = %s",ElementStatus->DataTransferPrimaryVolumeTag[TransferElementNumber]);
+ if
+ (ElementStatus->DataTransferAlternateVolumeTag[TransferElementNumber][0])
+
+ printf(":AlternateVolumeTag = %s",ElementStatus->DataTransferAlternateVolumeTag[TransferElementNumber]);
+ putchar('\n');
+ }
+ else printf("Empty\n");
+ }
+
+ for (StorageElementNumber = 0;
+ StorageElementNumber < ElementStatus->StorageElementCount;
+ StorageElementNumber++) {
+ printf(" Storage Element %d%s:%s", StorageElementNumber+1,
+ (ElementStatus->StorageElementIsImportExport[StorageElementNumber]) ? " IMPORT/EXPORT" : "",
+ (ElementStatus->StorageElementFull[StorageElementNumber] ? "Full " : "Empty"));
+ if (ElementStatus->PrimaryVolumeTag[StorageElementNumber][0]) {
+ printf(":VolumeTag=%s",ElementStatus->PrimaryVolumeTag[StorageElementNumber]);
+ }
+ if (ElementStatus->AlternateVolumeTag[StorageElementNumber][0]) {
+ printf(":AlternateVolumeTag=%s",ElementStatus->AlternateVolumeTag[StorageElementNumber]);
+ }
+ putchar('\n');
+ }
+
+#ifdef VMS
+ VMS_DefineStatusSymbols();
+#endif
+}
+
+void Move(int src, int dest) {
+ RequestSense_T *result; /* from MoveMedium */
+
+ result=MoveMedium(MediumChangerFD,src,dest,ElementStatus,inquiry_info,&SCSI_Flags);
+ if (result) /* we have an error! */
+ {
+ if (result->AdditionalSenseCode == 0x3B &&
+ result->AdditionalSenseCodeQualifier == 0x0E)
+ FatalError("source Element Address %d is Empty\n", src);
+ if (result->AdditionalSenseCode == 0x3B &&
+ result->AdditionalSenseCodeQualifier == 0x0D)
+ FatalError("destination Element Address %d is Already Full\n",
+ dest);
+ if (result->AdditionalSenseCode == 0x30 &&
+ result->AdditionalSenseCodeQualifier == 0x03)
+ FatalError("Cleaning Cartridge Installed and Ejected\n");
+ PrintRequestSense(result);
+ FatalError("MOVE MEDIUM from Element Address %d to %d Failed\n",
+ src,dest);
+ }
+}
+
+/* okay, now for the Load, Unload, etc. logic: */
+
+static void Load(void) {
+ int src, dest;
+ /* okay, check src, dest: arg1=src, arg2=dest */
+ if (arg1 < 1) {
+ FatalError("No source specified\n");
+ }
+ if (arg2 < 0) {
+ arg2 = 0; /* default to 1st drive :-( */
+ }
+ arg1--; /* we use zero-based arrays, sigh, not 1 base like some lusers */
+ /* check for filehandle: */
+ if (!device_opened) {
+ FatalError("No Media Changer Device Specified\n");
+ }
+ /* okay, we should be there: */
+ if (arg1 < 0 || arg1 >= ElementStatus->StorageElementCount) {
+ FatalError(
+ "illegal <storage-element-number> argument '%s' to 'load' command\n",
+ arg1+1);
+ }
+ if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) {
+ FatalError(
+ "illegal <drive-number> argument '%s' to 'load' command\n",
+ arg2);
+ }
+ if (ElementStatus->DataTransferElementFull[arg2]) {
+ FatalError("Drive %d Full (Storage Element %d loaded)\n",arg2,
+ ElementStatus->DataTransferElementSourceStorageElementNumber[arg2]+1);
+ }
+ /* Now look up the actual devices: */
+ dest=ElementStatus->DataTransferElementAddress[arg2];
+ src=ElementStatus->StorageElementAddress[arg1];
+ Move(src,dest); /* load it into the particular slot, if possible! */
+ /* now set the status for further command son this line... */
+ ElementStatus->StorageElementFull[arg1] = false;
+ ElementStatus->DataTransferElementFull[arg2] = true;
+
+ return; /* and done! */
+}
+
+static void Transfer(void) {
+ int src,dest;
+ if (arg1 < 1 ) {
+ FatalError("No source specified\n");
+ }
+ if (arg2 < 1) {
+ FatalError("No destination specified\n");
+ }
+ if (arg1 > ElementStatus->StorageElementCount) {
+ FatalError("Invalid source\n");
+ }
+ if (arg2 > ElementStatus->StorageElementCount) {
+ FatalError("Invalid destination\n");
+ }
+ /* okay, that's done... */
+ src=ElementStatus->StorageElementAddress[arg1-1];
+ dest=ElementStatus->StorageElementAddress[arg2-1];
+ Move(src,dest);
+}
+
+static void Eepos(void) {
+ if (arg1 < 0 || arg1 > 3) {
+ FatalError("eepos equires argument between 0 and 3.\n");
+ }
+ SCSI_Flags.eepos=arg1;
+}
+
+
+static void Unload(void) {
+ int src,dest; /* the actual SCSI-level numbers */
+ if (arg2 < 0) {
+ arg2 = 0; /* default to 1st drive :-( */
+ }
+ /* check for filehandle: */
+ if (!device_opened) {
+ FatalError("No Media Changer Device Specified\n");
+ }
+ /* okay, we should be there: */
+ if (arg1 < 0) {
+ arg1 = ElementStatus->DataTransferElementSourceStorageElementNumber[arg2];
+ if (arg1 < 0) {
+ FatalError("No Source for tape in drive %d!\n");
+ }
+ } else {
+ arg1--; /* go from bogus 1-base to zero-base */
+ }
+
+ if (arg1 >= ElementStatus->StorageElementCount) {
+ FatalError(
+ "illegal <storage-element-number> argument '%s' to 'unload' command\n",
+ arg1+1);
+ }
+
+ if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) {
+ FatalError(
+ "illegal <drive-number> argument '%s' to 'unload' command\n",
+ arg2);
+ }
+ if (ElementStatus->DataTransferElementFull[arg2] < 0 ) {
+ FatalError("Data Transfer Element %d is Empty\n", arg2);
+ }
+ /* Now see if something already lives where we wanna go... */
+ if (ElementStatus->StorageElementFull[arg1]) {
+ FatalError("Storage Element %d is Already Full\n", arg1+1);
+ }
+ /* okay, now to get src, dest: */
+ src=ElementStatus->DataTransferElementAddress[arg2];
+ if (arg1 >=0) {
+ dest=ElementStatus->StorageElementAddress[arg1];
+ } else {
+ dest=ElementStatus->DataTransferElementSourceStorageElementNumber[arg2];
+ }
+ if (dest < 0) { /* we STILL don't know, sigh... */
+ FatalError("Do not know which slot to unload tape into!\n");
+ }
+ fprintf(stderr, "Unloading Data Transfer Element into Storage Element %d...", arg1+1);
+ fflush(stderr); /* make it real-time :-( */
+ Move(src,dest);
+ fprintf(stderr, "done\n");
+ ElementStatus->StorageElementFull[arg1] = true;
+ ElementStatus->DataTransferElementFull[arg2] = false;
+}
+
+/*****************************************************************
+ ** ARGUMENT PARSING SUBROUTINES: Parse arguments, dispatch.
+ *****************************************************************/
+
+/* ***
+ * int get_arg(idx):
+ *
+ * If we have an actual argument at the index position indicated (i.e. we
+ * have not gone off the edge of the world), we return
+ * its number. If we don't, or it's not a numeric argument,
+ * we return -1. Note that 'get_arg' is kind of misleading, we only accept
+ * numeric arguments, not any other kind.
+ */
+int get_arg(int idx) {
+ char *arg;
+ int retval=-1;
+
+ if (idx >= argc) return -1; /* sorry! */
+ arg=argv[idx];
+ if (*arg < '0' || *arg > '9') {
+ return -1; /* sorry! */
+ }
+
+ retval=atoi(arg);
+ return retval;
+}
+
+/* open_device() -- set the 'fh' variable.... */
+void open_device(void) {
+
+
+ if (device_opened) {
+ SCSI_CloseDevice("Unknown",MediumChangerFD); /* close it, sigh... new device now! */
+ }
+
+ MediumChangerFD = SCSI_OpenDevice(device);
+ device_opened=1; /* SCSI_OpenDevice does an exit() if not. */
+}
+
+
+/* we see if we've got a file open. If not, we open one :-(. Then
+ * we execute the actual command. Or not :-(.
+ */
+void execute_command(struct command_table_struct *command) {
+ RequestSense_T RequestSense;
+
+
+ if ((device==NULL) && command->need_device) {
+ /* try to get it from TAPE environment variable... */
+ device=getenv("CHANGER");
+ if (device==NULL) {
+ device=getenv("TAPE");
+ if (device==NULL) {
+ device="/dev/changer"; /* Usage(); */
+ }
+ }
+ open_device();
+ }
+ if (!ElementStatus && command->need_status) {
+ 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, now to execute the command... */
+ 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(void) {
+ int i,cmd_tbl_idx;
+ struct command_table_struct *command;
+
+ i=1;
+ 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! */
+ if (command->num_args == 0) {
+ execute_command(command); /* execute_command handles 'stuff' */
+ }
+ else {
+ arg1=get_arg(i); /* checks i... */
+ if (arg1 != -1) {
+ i++; /* next! */
+ }
+ if (command->num_args==2 && arg1 != -1) {
+ arg2=get_arg(i);
+ if (arg2 != -1) {
+ i++;
+ }
+ }
+ execute_command(command);
+ }
+ arg1=-1;
+ arg2=-1;
+ }
+ }
+ return 0; /* should never get here. */
+}
+
+
+
+int main(int ArgCount,
+ char *ArgVector[],
+ char *Environment[])
+{
+
+
+#ifdef VMS
+ RequestSense_T RequestSense;
+#endif
+
+ /* save these where we can get at them elsewhere... */
+ argc=ArgCount;
+ argv=ArgVector;
+
+ argv0=argv[0];
+
+
+
+
+ parse_args(); /* also executes them as it sees them, sigh. */
+
+#ifndef VMS
+ if (device)
+ SCSI_CloseDevice(device, MediumChangerFD);
+ return 0;
+#else
+ if (device) {
+ ElementStatus = ReadElementStatus(MediumChangerFD,&RequestSense);
+ if (!ElementStatus) {
+ PrintRequestSense(&RequestSense);
+ FatalError("READ ELEMENT STATUS Command Failed\n");
+ }
+ VMS_DefineStatusSymbols();
+ SCSI_CloseDevice(device, MediumChangerFD);
+ }
+ return SS$_NORMAL;
+#endif
+
+
+}
+/*
+ *$Log: mtx.c,v $
+ *Revision 1.2.2.1 2001/11/06 21:20:40 elgreen
+ *Hopefully a fix to the problem with the 0 return for open in crontabs
+ *
+ *Revision 1.2 2001/06/09 17:26:26 elgreen
+ *Added 'nobarcode' command to mtx (to skip the initial request asking for
+ *barcodes for mtx status purposes).
+ *
+ *Revision 1.1.1.1 2001/06/05 17:10:22 elgreen
+ *Initial import into SourceForge
+ *
+ *Revision 1.15 2001/04/18 16:32:59 eric
+ *Cleaned up all -Wall messages.
+ *
+ *Revision 1.14 2001/04/18 16:08:08 eric
+ *after re-arranging & fixing bugs
+ *
+ *Revision 1.13 2001/03/06 01:37:05 eric
+ *Solaris changes
+ *
+ *Revision 1.12 2001/01/24 22:04:08 eric
+ *added /dev/changer as default file name.
+ *
+ *Revision 1.11 2001/01/15 22:21:06 eric
+ *added autoconf stuff.
+ *
+ *Revision 1.10 2000/11/30 20:37:17 eric
+ *Added quicky 'erase' command because I needed blank tapes and Linux
+ *only does long erases. (Not documented on purpose, because of rather
+ *bizarre interactions with the native tape driver if you don't know
+ *what you are doing).
+ *
+ *Revision 1.9 2000/11/28 01:10:50 eric
+ *Fixed syntax error in mtx.c...
+ *
+ *Revision 1.8 2000/10/28 00:10:35 eric
+ *Fixed stupid typo
+ *
+ *Revision 1.7 2000/10/27 23:36:57 eric
+ *make mtx inventory return an error code if the inventory fails, so that
+ *we can wait for inventory to be completed at system startup
+ *
+ *
+ */
--- /dev/null
+[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.
--- /dev/null
+/* 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.
+
+ $Date: 2001/06/19 21:51:32 $
+ $Revision: 1.3 $
+ 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: */
+
+#include "config.h" /* all the autoconf stuff. */
+
+/* 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
+
+/* Now greately 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
+
+/* 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 'tm_buf' interface, as used on AIX. */
+#ifdef HAVE_SYS_SCSI_H
+#include <sys/scsi.h>
+#include <sys/scsi_buf.h>
+#include <sys/devinfo.h> /* devinfo. */
+typedef struct tm_device_type {
+ int filenum;
+ int id;
+ int lun;
+ char *DeviceName;
+} *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... */
+
+/* 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;
+} SCSI_Flags_T;
+
+typedef enum { false, true } boolean;
+
+
+typedef enum { Input, Output } Direction_T;
+
+
+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;
+
+
+typedef enum ElementTypeCode
+{
+ AllElementTypes = 0,
+ MediumTransportElement = 1,
+ StorageElement = 2,
+ ImportExportElement = 3,
+ DataTransferElement = 4
+}
+ElementTypeCode_T;
+
+
+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 */
+}
+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];
+}
+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. */
+
+#endif /* of multi-include protection. */
--- /dev/null
+Name: mtx
+Version: 1.2.16rel
+Release: 1
+Summary: SCSI media changer control program
+Copyright: Redistributable
+Group: Utilities/System
+Source0: http://prdownloads.sourceforge.net/mtx/%{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 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 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 Nov 18 2001 Eric Green <eric@badtux.org>
+- 1.2.14
+- Moved changelog to end of spec file so it doesn't interfere
+-
+
+* 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
--- /dev/null
+Name: mtx
+Version: @@VERSION@@
+Release: 1
+Summary: SCSI media changer control program
+Copyright: Redistributable
+Group: Utilities/System
+Source0: http://prdownloads.sourceforge.net/mtx/%{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 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 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 Nov 18 2001 Eric Green <eric@badtux.org>
+- 1.2.14
+- Moved changelog to end of spec file so it doesn't interfere
+-
+
+* 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
--- /dev/null
+<!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 at:
+<ul>
+<li> <a href="http://sourceforge.net/project/showfiles.php?group_id=4626">Sourceforge Download Page for 'mtx'</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 <UL> Tags Anonymous</a> Hi, my name is Eric, and I am addicted to the <UL> 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: Fri Nov 16 11:09:19 MST 2001
+<!-- hhmts end -->
+ </body>
+</html>
--- /dev/null
+/* MTX -- SCSI Tape Attached Medium Changer Control Program
+
+ Copyright 1997-1998 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+$Date: 2002/02/05 16:51:11 $
+$Revision: 1.8.2.3 $
+
+ This file created Feb 2000 by Eric Lee Green <eric@badtux.org> from pieces
+ extracted from mtx.c, plus some additional routines.
+
+ 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.
+*/
+
+
+/* FatalError: changed Feb. 2000 elg@estinc.com to eliminate a buffer
+ overflow :-(. That could be important if mtxl is SUID for some reason.
+*/
+
+#include "mtx.h"
+#include "mtxl.h"
+
+/* #define DEBUG_MODE_SENSE 1 */
+/* #define DEBUG */
+/* #define DEBUG_SCSI */
+
+/* zap the following define when we finally add real import/export support */
+#define IMPORT_EXPORT_HACK 1 /* for the moment, import/export == storage */
+
+/* First, do some SCSI routines: */
+
+/* the camlib is used on FreeBSD. */
+#if HAVE_CAMLIB_H
+# include "scsi_freebsd.c"
+#endif
+
+/* the scsi_ctl interface is used on HP/UX. */
+#if HAVE_SYS_SCSI_CTL_H
+# include "scsi_hpux.c"
+#endif
+
+/* the 'sg' interface is used on Linux. */
+#if HAVE_SCSI_SG_H
+# include "scsi_linux.c"
+#endif
+
+/* The 'uscsi' interface is used on Solaris. */
+#if HAVE_SYS_SCSI_IMPL_USCSI_H
+# include "scsi_sun.c"
+#endif
+
+/* The 'tm_buf' interface, as used on AIX. */
+#ifdef HAVE_SYS_SCSI_H
+# include "scsi_aix.c"
+#endif
+
+/* The 'dslib' interface is used on SGI. */
+#if HAVE_DSLIB_H
+# include "scsi_sgi.c"
+#endif
+
+/* Hmm, dunno what to do about Digital Unix at the moment. */
+#ifdef DIGITAL_UNIX
+#include "du/scsi.c"
+#endif
+
+
+#ifdef VMS
+#include "[.vms]scsi.c"
+#endif
+
+extern char *argv0; /* something to let us do good error messages. */
+
+/* Now for some low-level SCSI stuff: */
+
+Inquiry_T *RequestInquiry(DEVICE_TYPE fd, RequestSense_T *RequestSense) {
+ Inquiry_T *Inquiry;
+ CDB_T CDB;
+
+ Inquiry = (Inquiry_T *) xmalloc(sizeof(Inquiry_T));
+
+ CDB[0] = 0x12; /* INQUIRY */
+ CDB[1] = 0; /* EVPD = 0 */
+ CDB[2] = 0; /* Page Code */
+ CDB[3] = 0; /* Reserved */
+ CDB[4] = sizeof(Inquiry_T); /* Allocation Length */
+ CDB[5] = 0; /* Control */
+
+ /* set us a very short timeout, sigh... */
+ SCSI_Set_Timeout(30); /* 30 seconds, sigh! */
+
+ if (SCSI_ExecuteCommand(fd, Input, &CDB, 6,
+ Inquiry, sizeof(Inquiry_T), RequestSense) != 0)
+ {
+ free(Inquiry);
+ return NULL; /* sorry! */
+ }
+ return Inquiry;
+}
+
+
+int BigEndian16(unsigned char *BigEndianData)
+{
+ return (BigEndianData[0] << 8) + BigEndianData[1];
+}
+
+
+int BigEndian24(unsigned char *BigEndianData)
+{
+ return (BigEndianData[0] << 16) + (BigEndianData[1] << 8) + BigEndianData[2];
+}
+
+
+
+
+
+void FatalError(char *ErrorMessage, ...)
+{
+#define FORMAT_BUF_LEN 1024
+ char FormatBuffer[FORMAT_BUF_LEN];
+ char *SourcePointer;
+ char *TargetPointer = FormatBuffer;
+ int Character, LastCharacter = '\0';
+ int numchars = 0;
+
+ va_list ArgumentPointer;
+ va_start(ArgumentPointer, ErrorMessage);
+ /* SourcePointer = "mtx: "; */
+ sprintf(TargetPointer,"%s: ",argv0);
+ numchars=strlen(TargetPointer);
+
+ /* while ((Character = *SourcePointer++) != '\0') { */
+ /* *TargetPointer++ = Character; */
+ /* numchars++; */
+ /* } */
+
+
+ while ((Character = *ErrorMessage++) != '\0')
+ if (LastCharacter == '%')
+ {
+ if (Character == 'm')
+ {
+ SourcePointer = strerror(errno);
+ while ((Character = *SourcePointer++) != '\0') {
+ *TargetPointer++ = Character;
+ numchars++;
+ if (numchars==FORMAT_BUF_LEN-1) break;
+ }
+ if (numchars==FORMAT_BUF_LEN-1) break; /* break outer loop... */
+ }
+ else
+ {
+ *TargetPointer++ = '%';
+ *TargetPointer++ = Character;
+ numchars++;
+ if (numchars==FORMAT_BUF_LEN-1) break;
+ }
+ LastCharacter = '\0';
+ }
+ else
+ {
+ if (Character != '%') {
+ *TargetPointer++ = Character;
+ numchars++;
+ if (numchars==FORMAT_BUF_LEN-1) break;
+ }
+ LastCharacter = Character;
+ }
+ *TargetPointer = '\0'; /* works even if we had to break from above... */
+ vfprintf(stderr, FormatBuffer, ArgumentPointer);
+ va_end(ArgumentPointer);
+#ifndef VMS
+ exit(1);
+#else
+ sys$exit(VMS_ExitCode);
+#endif
+}
+
+/* This is a really slow and stupid 'bzero' implementation'... */
+void slow_bzero(char *buffer, int numchars) {
+ while (numchars--) *buffer++ = 0;
+}
+
+
+/* malloc some memory while checking for out-of-memory conditions. */
+void *xmalloc(size_t Size)
+{
+ void *Result = (void *) malloc(Size);
+ if (Result == NULL)
+ FatalError("cannot allocate %d bytes of memory\n", Size);
+ return Result;
+}
+
+/* malloc some memory, zeroing it too: */
+void *xzmalloc(size_t Size)
+{
+ void *Result=(void *)xmalloc(Size);
+
+ slow_bzero(Result,Size);
+ return Result;
+}
+
+
+int min(int x, int y)
+{
+ return (x < y ? x : y);
+}
+
+
+int max(int x, int y)
+{
+ return (x > y ? x : y);
+}
+
+
+/* Routine to inventory the library. Needed by, e.g., some Breece Hill
+ * loaders. Sends an INITIALIZE_ELEMENT_STATUS command. This command
+ * has no parameters, such as a range to scan :-(.
+ */
+
+int Inventory(DEVICE_TYPE MediumChangerFD) {
+ CDB_T CDB;
+ RequestSense_T RequestSense;
+
+ /* okay, now for the command: */
+ CDB[0]=0x07;
+ CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
+
+ /* set us a very long timeout, sigh... */
+ SCSI_Set_Timeout(30*60); /* 30 minutes, sigh! */
+
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
+ return -1; /* could not do! */
+ }
+ return 0; /* did do! */
+}
+
+/* Routine to send an UNLOAD from the SSC spec to a device. This can be
+ * used either to eject a tape (when sent to a tape drive), or to eject
+ * a magzine (on some Seagate changers, when sent to LUN 1 ).
+ */
+
+int Eject(DEVICE_TYPE fd) {
+ CDB_T CDB;
+ RequestSense_T RequestSense;
+ /* okay, now for the command: */
+
+ CDB[0]=0x1b;
+ CDB[1]=CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
+
+ if (SCSI_ExecuteCommand(fd,Input,&CDB,6,NULL,0,&RequestSense) != 0) {
+ return -1; /* could not do! */
+ }
+ return 0; /* did do! */
+}
+
+/* Routine to read the Mode Sense Element Address Assignment Page */
+/* We try to read the page. If we can't read the page, we return NULL.
+ * Our caller really isn't too worried about why we could not read the
+ * page, it will simply default to some kind of default values.
+ */
+ElementModeSense_T *ReadAssignmentPage(DEVICE_TYPE MediumChangerFD) {
+ CDB_T CDB;
+ ElementModeSense_T *retval; /* processed SCSI. */
+ unsigned char input_buffer[136];
+ ElementModeSensePage_T *sense_page; /* raw SCSI. */
+ RequestSense_T RequestSense; /* see what retval of raw SCSI was... */
+
+ /* okay, now for the command: */
+ CDB[0]=0x1a; /* Mode Sense(6) */
+ CDB[1]=0;
+ CDB[2]=0x1d; /* Mode Sense Element Address Assignment Page */
+ CDB[3]=0;
+ CDB[4]=136; /* allocation_length... */
+ CDB[5]=0;
+
+ /* clear the data buffer: */
+ slow_bzero((char *)&RequestSense,sizeof(RequestSense_T));
+ slow_bzero((char *)input_buffer,sizeof(input_buffer));
+
+ if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,
+ &input_buffer,sizeof(input_buffer),&RequestSense) != 0) {
+#ifdef DEBUG_MODE_SENSE
+ fprintf(stderr,"Could not execute mode sense...\n");
+ fflush(stderr);
+#endif
+ return NULL; /* sorry, couldn't do it. */
+ }
+
+ /* Could do it, now build return value: */
+
+#ifdef DEBUG_MODE_SENSE
+ {
+ int i;
+ for (i=0;i<30;i+=3) {
+ fprintf(stderr,"ib[%d]=%d ib[%d]=%d ib[%d]=%d\n",
+ i,input_buffer[i],i+1,input_buffer[i+1],
+ i+2,input_buffer[i+2]);
+ /* fprintf(stderr,"input_buffer[0]=%d input_buffer[3]=%d\n",
+ * input_buffer[0], input_buffer[3]);
+ */
+ }
+ fflush(stderr);
+ }
+#endif
+ /* first, skip past: mode data header, and block descriptor header if any */
+ sense_page=(ElementModeSensePage_T *)(input_buffer+4+input_buffer[3]);
+
+#ifdef DEBUG_MODE_SENSE
+ fprintf(stderr,"*sense_page=%x %x\n",*((unsigned char *)sense_page),sense_page->PageCode);
+ fflush(stderr);
+#endif
+
+ retval=(ElementModeSense_T *) xzmalloc(sizeof(ElementModeSense_T));
+
+ /* Remember that all SCSI values are big-endian: */
+ retval->MediumTransportStart=(((int)sense_page->MediumTransportStartHi)<<8) +
+ sense_page->MediumTransportStartLo;
+ retval->NumMediumTransport=(((int)(sense_page->NumMediumTransportHi))<<8) +
+ sense_page->NumMediumTransportLo;
+
+ /* HACK! Some HP autochangers don't report NumMediumTransport right! */
+ /* ARG! TAKE OUT THE %#@!# HACK! */
+#ifdef STUPID_DUMB_IDIOTIC_HP_LOADER_HACK
+ if (!retval->NumMediumTransport) {
+ retval->NumMediumTransport=1;
+ }
+#endif
+
+#ifdef DEBUG_MODE_SENSE
+ fprintf(stderr,"rawNumStorage= %d %d rawNumImportExport= %d %d\n",
+ sense_page->NumStorageHi,sense_page->NumStorageLo,
+ sense_page->NumImportExportHi, sense_page->NumImportExportLo);
+ fprintf(stderr,"rawNumTransport=%d %d rawNumDataTransfer=%d %d\n",
+ sense_page->NumMediumTransportHi,sense_page->NumMediumTransportLo,
+ sense_page->NumDataTransferHi,sense_page->NumDataTransferLo);
+ fflush(stderr);
+#endif
+
+ retval->StorageStart=(((int)(sense_page->StorageStartHi))<<8) +
+ sense_page->StorageStartLo;
+ retval->NumStorage=(((int)(sense_page->NumStorageHi))<<8) +
+ sense_page->NumStorageLo;
+ retval->ImportExportStart=(((int)(sense_page->ImportExportStartHi))<<8)+
+ sense_page->ImportExportStartLo;
+ retval->NumImportExport=(((int)(sense_page->NumImportExportHi))<<8)+
+ sense_page->NumImportExportLo;
+ retval->DataTransferStart=(((int)(sense_page->DataTransferStartHi))<<8)+
+ sense_page->DataTransferStartLo;
+ retval->NumDataTransfer=(((int)(sense_page->NumDataTransferHi))<<8)+
+ sense_page->NumDataTransferLo;
+
+ /* allocate a couple spares 'cause some HP autochangers and maybe others
+ * don't properly report the robotics arm(s) count here...
+ */
+ retval->NumElements=retval->NumStorage+retval->NumImportExport+
+ retval->NumDataTransfer+retval->NumMediumTransport;
+
+ retval->MaxReadElementStatusData=(sizeof(ElementStatusDataHeader_T) +
+ 4 * sizeof(ElementStatusPage_T) +
+ (retval->NumElements) *
+ sizeof(TransportElementDescriptor_T));
+
+
+#ifdef IMPORT_EXPORT_HACK
+ retval->NumStorage=retval->NumStorage+retval->NumImportExport;
+#endif
+
+#ifdef DEBUG_MODE_SENSE
+ fprintf(stderr,"NumMediumTransport=%d\n",retval->NumMediumTransport);
+ fprintf(stderr,"NumStorage=%d\n",retval->NumStorage);
+ fprintf(stderr,"NumImportExport=%d\n",retval->NumImportExport);
+ fprintf(stderr,"NumDataTransfer=%d\n",retval->NumDataTransfer);
+ fprintf(stderr,"MaxReadElementStatusData=%d\n",retval->MaxReadElementStatusData);
+ fprintf(stderr,"NumElements=%d\n",retval->NumElements);
+ fflush(stderr);
+#endif
+
+ return retval;
+}
+
+static void FreeElementData(ElementStatus_T *data)
+{
+ free(data->DataTransferElementAddress);
+ free(data->DataTransferElementSourceStorageElementNumber);
+ free(data->DataTransferPrimaryVolumeTag);
+ free(data->DataTransferAlternateVolumeTag);
+ free(data->PrimaryVolumeTag);
+ free(data->AlternateVolumeTag);
+ free(data->StorageElementAddress);
+ free(data->StorageElementIsImportExport);
+ free(data->StorageElementFull);
+ free(data->DataTransferElementFull);
+}
+
+
+
+/* allocate data */
+
+static ElementStatus_T *AllocateElementData(ElementModeSense_T *mode_sense) {
+ ElementStatus_T *retval;
+
+ retval=(ElementStatus_T *) xzmalloc(sizeof(ElementStatus_T));
+
+ /* now for the invidual component arrays.... */
+
+ retval->DataTransferElementAddress = (int *) xzmalloc(sizeof(int)*
+ (mode_sense->NumDataTransfer + 1));
+ retval->DataTransferElementSourceStorageElementNumber =
+ (int *) xzmalloc(sizeof(int)*(mode_sense->NumDataTransfer+1));
+ retval->DataTransferPrimaryVolumeTag=
+ (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
+ retval->DataTransferAlternateVolumeTag=
+ (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumDataTransfer+1));
+ retval->PrimaryVolumeTag=
+ (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
+ retval->AlternateVolumeTag=
+ (barcode *)xzmalloc(sizeof(barcode)*(mode_sense->NumStorage+1));
+ retval->StorageElementAddress=
+ (int *)xzmalloc(sizeof(int)*(mode_sense->NumStorage+1));
+ retval->StorageElementIsImportExport=
+ (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));
+ retval->StorageElementFull=
+ (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumStorage+1));
+ retval->DataTransferElementFull=
+ (boolean *)xzmalloc(sizeof(boolean)*(mode_sense->NumDataTransfer+1));
+
+ return retval; /* sigh! */
+}
+
+
+void copy_barcode(unsigned char *src, unsigned char *dest) {
+ int i;
+
+ for (i=0; i < 37; i++) {
+ *dest++ = *src++;
+ }
+ *dest=0; /* null-terminate, sigh. */
+}
+
+
+/* This #%!@# routine has more parameters than I can count! */
+unsigned char *SendElementStatusRequest(DEVICE_TYPE MediumChangerFD,
+ RequestSense_T *RequestSense,
+ Inquiry_T *inquiry_info,
+ SCSI_Flags_T *flags,
+ int ElementStart,
+ int NumElements,
+ int NumBytes
+ ) {
+
+ CDB_T CDB;
+ boolean is_attached = false;
+
+ unsigned char *DataBuffer; /* size of data... */
+
+#ifdef HAVE_GET_ID_LUN
+ scsi_id_t *scsi_id;
+#endif
+ if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
+ is_attached=true;
+ }
+ if (flags->no_attached) { /* override, sigh */
+ is_attached=false;
+ }
+
+ DataBuffer=(unsigned char *) xmalloc(NumBytes+1);
+
+ slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
+#ifdef HAVE_GET_ID_LUN
+ scsi_id = SCSI_GetIDLun(MediumChangerFD);
+#endif
+
+
+ CDB[0] = 0xB8; /* READ ELEMENT STATUS */
+ if (is_attached) {
+ CDB[0] = 0xB4; /* whoops, READ_ELEMENT_STATUS_ATTACHED! */
+ }
+#ifdef HAVE_GET_ID_LUN
+ CDB[1] = (scsi_id->lun << 5) | ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype; /* Lun + VolTag + Type code */
+ free(scsi_id);
+#else
+ CDB[1] = ((flags->no_barcodes) ? 0 : 0x10) | flags->elementtype; /* Element Type Code = 0, VolTag = 1 */
+#endif
+ CDB[2] = (ElementStart >> 8) & 0xff; /* Starting Element Address MSB */
+ CDB[3] = ElementStart & 0xff; /* Starting Element Address LSB */
+
+
+ CDB[4]= (NumElements >> 8) & 0xff; /* Number Of Elements MSB */
+ CDB[5]= NumElements & 0xff ; /* Number of elements LSB */
+
+ /* CDB[5]=127; */ /* test */
+#ifdef DEBUG
+ fprintf(stderr,"CDB[5]=%d\n" , CDB[5]);
+#endif
+ CDB[6] = 0; /* Reserved */
+
+ CDB[7]= (NumBytes >> 16) & 0xff; /* allocation length MSB */
+ CDB[8]= (NumBytes >> 8) & 0xff;
+ CDB[9]= NumBytes & 0xff; /* allocation length LSB */
+
+ CDB[10] = 0; /* Reserved */
+ CDB[11] = 0; /* Control */
+
+#ifdef DEBUG_BARCODE
+ {
+ int i;
+ fprintf(stderr,"CDB= ");
+ for (i=0;i<12;i++) {
+ fprintf(stderr,"%x ",CDB[i]);
+ }
+ fprintf(stderr,"\n");
+ fflush(stderr);
+ }
+#endif
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
+ DataBuffer,NumBytes, RequestSense) != 0){
+ /* okay, first see if we have sense key of 'illegal request',
+ additional sense code of '24', additional sense qualfier of
+ '0', and field in error of '4'. This means that we issued a request
+ w/bar code reader and did not have one, thus must re-issue the request
+ w/out barcode :-(.
+ */
+
+ /* Okay, most autochangers and tape libraries set a sense key here if
+ * they do not have a bar code reader. For some reason, the ADIC DAT
+ * uses a different sense key? Let's retry w/o bar codes for *ALL*
+ * sense keys, sigh sigh sigh.
+ */
+
+ if ( RequestSense->SenseKey > 1 ) {
+ /* we issued a code requesting bar codes, there is no bar code reader? */
+ /* clear out our sense buffer first... */
+ slow_bzero((char *)RequestSense,sizeof(RequestSense_T));
+
+ CDB[1] &= ~0x10; /* clear bar code flag! */
+
+#ifdef DEBUG_BARCODE
+ {
+ int i;
+ fprintf(stderr,"CDB= ");
+ for (i=0;i<12;i++) {
+ fprintf(stderr,"%x ",CDB[i]);
+ }
+ fprintf(stderr,"\n");
+ fflush(stderr);
+ }
+#endif
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 12,
+ DataBuffer, NumBytes, RequestSense) != 0){
+ free(DataBuffer);
+ return NULL;
+ }
+ } else {
+ free(DataBuffer);
+ return NULL;
+ }
+ }
+
+#ifdef DEBUG_BARCODE
+ /* print a bunch of extra debug data :-(. */
+ PrintRequestSense(RequestSense); /* see what it sez :-(. */
+ {
+ int i;
+ fprintf(stderr,"Data:");
+ for (i=0;i<40;i++) {
+ fprint(stderr,"%02x ",DataBuffer[i]);
+ }
+ fprintf(stderr,"\n");
+ }
+#endif
+ return DataBuffer; /* we succeeded! */
+}
+
+/******************* ParseElementStatus ***********************************/
+/* This does the actual grunt work of parsing element status data. It fills
+ * in appropriate pieces of its input data. It may be called multiple times
+ * while we are gathering element status.
+ */
+
+static void ParseElementStatus(int *EmptyStorageElementAddress,
+ int *EmptyStorageElementCount,
+ unsigned char *DataBuffer,
+ ElementStatus_T *ElementStatus,
+ ElementModeSense_T *mode_sense
+
+ ) {
+
+ unsigned char *DataPointer=DataBuffer;
+ TransportElementDescriptor_T TEBuf;
+ TransportElementDescriptor_T *TransportElementDescriptor;
+ ElementStatusDataHeader_T *ElementStatusDataHeader;
+ Element2StatusPage_T *ElementStatusPage;
+ Element2StatusPage_T ESBuf;
+ int ElementCount;
+ int TransportElementDescriptorLength;
+ int BytesAvailable;
+
+ ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataBuffer;
+
+
+
+ ElementStatusDataHeader = (ElementStatusDataHeader_T *) DataPointer;
+ DataPointer += sizeof(ElementStatusDataHeader_T);
+ ElementCount =
+ BigEndian16(ElementStatusDataHeader->NumberOfElementsAvailable);
+#ifdef DEBUG
+ fprintf(stderr,"ElementCount=%d\n",ElementCount);
+ fflush(stderr);
+#endif
+ while (ElementCount > 0)
+ {
+#ifdef DEBUG
+ int got_element_num=0;
+
+ fprintf(stderr,"Working on element # %d Element Count %d\n",got_element_num,ElementCount);
+ got_element_num++;
+#endif
+
+ memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
+ ElementStatusPage = &ESBuf;
+ DataPointer += sizeof(ElementStatusPage_T);
+ TransportElementDescriptorLength =
+ BigEndian16(ElementStatusPage->ElementDescriptorLength);
+ if (TransportElementDescriptorLength <
+ sizeof(TransportElementDescriptorShort_T)) {
+
+ /* Foo, Storage Element Descriptors can be 4 bytes?! */
+ if ( (ElementStatusPage->ElementTypeCode != MediumTransportElement &&
+ ElementStatusPage->ElementTypeCode != StorageElement &&
+ ElementStatusPage->ElementTypeCode != ImportExportElement ) ||
+ TransportElementDescriptorLength < 4) {
+#ifdef DEBUG
+ fprintf(stderr,"boom! ElementTypeCode=%d\n",ElementStatusPage->ElementTypeCode);
+#endif
+ FatalError("Transport Element Descriptor Length too short: %d\n",TransportElementDescriptorLength);
+ }
+
+ }
+#ifdef DEBUG
+ fprintf(stderr,"Transport Element Descriptor Length=%d\n",TransportElementDescriptorLength);
+#endif
+ BytesAvailable =
+ BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
+ if (BytesAvailable <= 0) {
+ ElementCount--;
+ }
+ while (BytesAvailable > 0)
+ {
+ /* TransportElementDescriptor =
+ (TransportElementDescriptor_T *) DataPointer; */
+ memcpy(&TEBuf, DataPointer,
+ (TransportElementDescriptorLength <= sizeof(TEBuf)) ?
+ TransportElementDescriptorLength :
+ sizeof(TEBuf));
+ TransportElementDescriptor = &TEBuf;
+
+ DataPointer += TransportElementDescriptorLength;
+ BytesAvailable -= TransportElementDescriptorLength;
+ ElementCount--;
+ switch (ElementStatusPage->ElementTypeCode)
+ {
+ case MediumTransportElement:
+ ElementStatus->TransportElementAddress = BigEndian16(TransportElementDescriptor->ElementAddress);
+#ifdef DEBUG
+ fprintf(stderr,"TransportElementAddress=%d\n",ElementStatus->TransportElementAddress);
+#endif
+ break;
+ /* we treat ImportExport elements as if they were normal
+ * storage elements now, sigh...
+ */
+ case ImportExportElement:
+ ElementStatus->ImportExportCount++;
+#ifdef DEBUG
+ fprintf(stderr,"ImportExportElement=%d\n",ElementStatus->StorageElementCount);
+#endif
+ ElementStatus->StorageElementIsImportExport[ElementStatus->StorageElementCount] = 1;
+ /* WARNING: DO *NOT* PUT A 'break' STATEMENT HERE, it is supposed
+ * to run on into the case for a Storage Element!
+ */
+ case StorageElement:
+#ifdef DEBUG
+ fprintf(stderr,"StorageElementCount=%d ElementAddress = %d ",ElementStatus->StorageElementCount,BigEndian16(TransportElementDescriptor->ElementAddress));
+#endif
+ ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount] =
+ BigEndian16(TransportElementDescriptor->ElementAddress);
+ ElementStatus->StorageElementFull[ElementStatus->StorageElementCount] =
+ TransportElementDescriptor->Full;
+#ifdef DEBUG
+ fprintf(stderr,"TransportElement->Full = %d\n",TransportElementDescriptor->Full);
+#endif
+ if (!TransportElementDescriptor->Full)
+ {
+ EmptyStorageElementAddress[(*EmptyStorageElementCount)++] =
+ ElementStatus->StorageElementCount; /* slot idx. */
+ /* ElementStatus->StorageElementAddress[ElementStatus->StorageElementCount]; */
+ }
+ if ( (TransportElementDescriptorLength > 11) &&
+ (ElementStatusPage->VolBits & E2_AVOLTAG)) {
+ copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
+ ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount]);
+ } else {
+ ElementStatus->AlternateVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */;
+ }
+ if ( (TransportElementDescriptorLength > 11) &&
+ (ElementStatusPage->VolBits & E2_PVOLTAG)) {
+ copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
+ ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount]);
+ } else {
+ ElementStatus->PrimaryVolumeTag[ElementStatus->StorageElementCount][0]=0; /* null string. */
+ }
+
+ ElementStatus->StorageElementCount++;
+ /* Note that the original mtx had no check here for
+ buffer overflow, though some drives might mistakingly
+ do one...
+ */
+ if (ElementStatus->StorageElementCount > mode_sense->NumStorage) {
+ fprintf(stderr,"Warning:Too Many Storage Elements Reported (expected %d, now have %d\n",
+ mode_sense->NumStorage,
+ ElementStatus->StorageElementCount);
+ fflush(stderr);
+ return; /* we're done :-(. */
+ }
+
+
+ break;
+
+
+ case DataTransferElement:
+ /* tape drive not installed, go back to top of loop */
+
+ /* if (TransportElementDescriptor->Except) continue ; */
+
+ /* Note: This is for Exabyte tape libraries that improperly
+ report that they have a 2nd tape drive when they don't. We
+ could generalize this in an ideal world, but my attempt to
+ do so failed with dual-drive Exabyte tape libraries that
+ *DID* have the second drive. Sigh.
+ */
+ /* No longer need this test due to code restructuring? */
+ /* if (TransportElementDescriptor->AdditionalSenseCode==0x83 &&
+ TransportElementDescriptor->AdditionalSenseCodeQualifier==0x04)
+ continue;
+ */
+ /* generalize it. Does it work? Let's try it! */
+ /* No, dammit, following does not work on dual-drive Exabyte
+ 'cause if a tape is in the drive, it sets the AdditionalSense
+ code to something (sigh).
+ */
+ /* if (TransportElementDescriptor->AdditionalSenseCode!=0)
+ continue;
+ */
+
+
+
+ ElementStatus->DataTransferElementAddress[ElementStatus->DataTransferElementCount] =
+ BigEndian16(TransportElementDescriptor->ElementAddress);
+ ElementStatus->DataTransferElementFull[ElementStatus->DataTransferElementCount] =
+ TransportElementDescriptor->Full;
+ ElementStatus->DataTransferElementSourceStorageElementNumber[ElementStatus->DataTransferElementCount] =
+ BigEndian16(TransportElementDescriptor
+ ->SourceStorageElementAddress);
+
+ if (ElementStatus->DataTransferElementCount >= mode_sense->NumDataTransfer) {
+ FatalError("Too many Data Transfer Elements Reported\n");
+ }
+ if (ElementStatusPage->VolBits & E2_PVOLTAG) {
+ copy_barcode(TransportElementDescriptor->PrimaryVolumeTag,
+ ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount]);
+ } else {
+ ElementStatus->DataTransferPrimaryVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
+ }
+ if (ElementStatusPage->VolBits & E2_AVOLTAG) {
+ copy_barcode(TransportElementDescriptor->AlternateVolumeTag,
+ ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount]);
+ } else {
+ ElementStatus->DataTransferAlternateVolumeTag[ElementStatus->DataTransferElementCount][0]=0; /* null string */
+ }
+ ElementStatus->DataTransferElementCount++;
+ /* 0 actually is a usable element address */
+ /* if (DataTransferElementAddress == 0) */
+ /* FatalError( */
+ /* "illegal Data Transfer Element Address %d reported\n", */
+ /* DataTransferElementAddress); */
+ break;
+ default:
+ FatalError("illegal Element Type Code %d reported\n",
+ ElementStatusPage->ElementTypeCode);
+ }
+ }
+ }
+}
+
+/********************* Real ReadElementStatus ********************* */
+
+/*
+ * We no longer do the funky trick to figure out ALLOCATION_LENGTH.
+ * Instead, we use the SCSI Generic command rather than SEND_SCSI_COMMAND
+ * under Linux, which gets around the @#%@ 4k buffer size in Linux.
+ * We still have the restriction that Linux cuts off the last two
+ * bytes of the SENSE DATA (Q#@$%@#$^ Linux!). Which means that the
+ * verbose widget won't work :-(.
+
+ * We now look for that "attached" bit in the inquiry_info to see whether
+ * to use READ_ELEMENT_ATTACHED or plain old READ_ELEMENT. In addition, we
+ * look at the device type in the inquiry_info to see whether it is a media
+ * changer or tape device, and if it's a media changer device, we ignore the
+ * attached bit (one beta tester found an old 4-tape DAT changer that set
+ * the attached bit for both the tape device AND the media changer device).
+
+*/
+
+ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags) {
+ ElementStatus_T *ElementStatus;
+
+ unsigned char *DataBuffer; /* size of data... */
+
+ int EmptyStorageElementCount=0;
+ int *EmptyStorageElementAddress; /* [MAX_STORAGE_ELEMENTS]; */
+
+ int empty_idx=0;
+ int invalid_sources=0;
+ boolean is_attached = false;
+ int i,j;
+
+ ElementModeSense_T *mode_sense = NULL;
+
+ if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) {
+ is_attached=true;
+ }
+ if (flags->no_attached) { /* override, sigh */
+ is_attached=false;
+ }
+ if (!is_attached) {
+ mode_sense=ReadAssignmentPage(MediumChangerFD);
+ }
+ if (!mode_sense) {
+ mode_sense=(ElementModeSense_T *) xmalloc(sizeof(ElementModeSense_T));
+ mode_sense->NumMediumTransport=MAX_TRANSPORT_ELEMENTS;
+ mode_sense->NumStorage=MAX_STORAGE_ELEMENTS;
+ mode_sense->NumDataTransfer=MAX_TRANSFER_ELEMENTS;
+ mode_sense->MaxReadElementStatusData= (sizeof(ElementStatusDataHeader_T)
+ + 3 * sizeof(ElementStatusPage_T)
+ + (MAX_STORAGE_ELEMENTS+MAX_TRANSFER_ELEMENTS+MAX_TRANSPORT_ELEMENTS)
+ * sizeof(TransportElementDescriptor_T));
+
+ /* don't care about the others anyhow at the moment... */
+ }
+
+ ElementStatus=AllocateElementData(mode_sense);
+
+ /* Now to initialize it (sigh). */
+ ElementStatus->StorageElementCount = 0;
+ ElementStatus->DataTransferElementCount=0;
+
+
+ /* first, allocate some empty storage stuff: Note that we pass this
+ * down to ParseElementStatus (sigh!)
+ */
+
+ EmptyStorageElementAddress=(int *)xzmalloc((mode_sense->NumStorage+1)*sizeof(int));
+ for (i=0;i<mode_sense->NumStorage;i++) {
+ EmptyStorageElementAddress[i]=-1;
+ }
+
+ /* Okay, now to send some requests for the various types of stuff: */
+
+ /* -----------STORAGE ELEMENTS---------------- */
+ /* Let's start with storage elements: */
+
+ for (i=0;i<mode_sense->NumDataTransfer;i++) {
+ /* initialize them to an illegal # so that we can fix later... */
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i] = -1;
+ }
+
+
+ flags->elementtype=StorageElement; /* sigh! */
+ DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
+ inquiry_info,flags,
+ mode_sense->StorageStart,
+ /* adjust for import/export. */
+ mode_sense->NumStorage-mode_sense->NumImportExport,
+ mode_sense->MaxReadElementStatusData);
+ if (!DataBuffer) {
+ /* darn. Free up stuff and return. */
+ /****FIXME**** do a free on element data! */
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+
+ ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
+ DataBuffer,ElementStatus,mode_sense);
+
+ free(DataBuffer); /* sigh! */
+
+ /* --------------IMPORT/EXPORT--------------- */
+ /* Next let's see if we need to do Import/Export: */
+ if (mode_sense->NumImportExport > 0) {
+ flags->elementtype=ImportExportElement;
+ DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
+ inquiry_info,flags,
+ mode_sense->ImportExportStart,
+ mode_sense->NumImportExport,
+ mode_sense->MaxReadElementStatusData);
+
+ if (!DataBuffer) {
+ /* darn. Free up stuff and return. */
+ /****FIXME**** do a free on element data! */
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+ ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
+ DataBuffer,ElementStatus,mode_sense);
+
+ }
+
+
+
+ /* ----------------- DRIVES ---------------------- */
+
+
+ flags->elementtype=DataTransferElement; /* sigh! */
+ DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
+ inquiry_info,flags,
+ mode_sense->DataTransferStart,
+ mode_sense->NumDataTransfer,
+ mode_sense->MaxReadElementStatusData);
+ if (!DataBuffer) {
+ /* darn. Free up stuff and return. */
+ /****FIXME**** do a free on element data! */
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+
+ ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
+ DataBuffer,ElementStatus,mode_sense);
+
+ free(DataBuffer); /* sigh! */
+
+
+ /* ----------------- Robot Arm(s) -------------------------- */
+
+ /* grr, damned brain dead HP doesn't report that it has any! */
+ if (!mode_sense->NumMediumTransport) {
+ ElementStatus->TransportElementAddress=0; /* default it sensibly :-(. */
+ } else {
+ flags->elementtype=MediumTransportElement; /* sigh! */
+ DataBuffer=SendElementStatusRequest(MediumChangerFD,RequestSense,
+ inquiry_info,flags,
+ mode_sense->MediumTransportStart,
+ 1, /* only get 1, sigh. */
+ mode_sense->MaxReadElementStatusData);
+ if (!DataBuffer) {
+ /* darn. Free up stuff and return. */
+ /****FIXME**** do a free on element data! */
+ FreeElementData(ElementStatus);
+ return NULL;
+ }
+
+ ParseElementStatus(EmptyStorageElementAddress,&EmptyStorageElementCount,
+ DataBuffer,ElementStatus,mode_sense);
+
+ free(DataBuffer); /* sigh! */
+ }
+
+ /*---------------------- Sanity Checking ------------------- */
+
+ if (!ElementStatus->DataTransferElementCount)
+ FatalError("no Data Transfer Element reported\n");
+ if (ElementStatus->StorageElementCount == 0)
+ FatalError("no Storage Elements reported\n");
+
+
+ /* ---------------------- Reset SourceStorageElementNumbers ------- */
+
+ /* okay, re-write the SourceStorageElementNumber code *AGAIN*.
+ * Pass1:
+ * Translate from raw element # to our translated # (if possible).
+ * First, check the SourceStorageElementNumbers against the list of
+ * filled slots. If the slots indicated are empty, we accept that list as
+ * valid. Otherwise decide the SourceStorageElementNumbers are invalid.
+ *
+ * Pass2:
+ *
+ * If we had some invalid (or unknown) SourceStorageElementNumbers
+ * then we must search for free slots, and assign SourceStorageElementNumbers
+ * to those free slots. We happen to already built a list of free
+ * slots as part of the process of reading the storage element numbers
+ * from the tape. So that's easy enough to do!
+ */
+
+#ifdef DEBUG_TAPELIST
+ fprintf(stderr,"empty slots: %d\n",EmptyStorageElementCount);
+ if (EmptyStorageElementCount) {
+ for (i=0; i<EmptyStorageElementCount; i++) {
+ fprintf(stderr,"empty: %d\n",EmptyStorageElementAddress[i]);
+ }
+ }
+#endif
+
+ /* Okay, now we re-assign origin slots if the "real" origin slot
+ * is obviously defective:
+ */
+ /* pass one: */
+ invalid_sources=0; /* no invalid sources yet! */
+ for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
+ int elnum;
+ int translated_elnum = -1;
+ /* if we have an element, then ... */
+ if (ElementStatus->DataTransferElementFull[i]) {
+ elnum=ElementStatus->DataTransferElementSourceStorageElementNumber[i];
+ /* if we have an element number, then ... */
+ if (elnum >= 0) {
+ /* Now to translate the elnum: */
+ for (j=0; j<ElementStatus->StorageElementCount; j++) {
+ if (elnum == ElementStatus->StorageElementAddress[j]) {
+ translated_elnum=j;
+ }
+ }
+ /* now see if the element # is already occupied: */
+ if (ElementStatus->StorageElementFull[translated_elnum]) {
+ invalid_sources=1;
+ break; /* break out of the loop! */
+ } else {
+ /* properly set the source... */
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
+ translated_elnum;
+ }
+
+ } else {
+ /* if element # was not >=0, then we had an invalid source anyhow! */
+ invalid_sources=1;
+ }
+ }
+ }
+
+#ifdef DEBUG_TAPELIST
+ fprintf(stderr,"invalid_sources=%d\n",invalid_sources);
+#endif
+
+ /* Pass2: */
+ /* okay, we have invalid sources, so let's see what they should be: */
+ /* Note: If EmptyStorageElementCount is < # of drives, the leftover
+ * drives will be assigned a -1 (see the initialization loop for
+ * EmptyStorageElementAddress above), which will be reported as "slot 0"
+ * by the user interface. This is an invalid value, but more useful for us
+ * to have than just crapping out here :-(.
+ */
+ if (invalid_sources) {
+ empty_idx=0;
+ for (i=0;i<ElementStatus->DataTransferElementCount;i++) {
+ if (ElementStatus->DataTransferElementFull[i]) {
+#ifdef DEBUG_TAPELIST
+ fprintf(stderr,"for drive %d, changing source %d to %d (empty slot #%d)\n",
+ i,
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i],
+ EmptyStorageElementAddress[empty_idx],
+ empty_idx);
+#endif
+
+ ElementStatus->DataTransferElementSourceStorageElementNumber[i]=
+ EmptyStorageElementAddress[empty_idx++];
+ }
+ }
+ }
+
+ /* and done! */
+ free(mode_sense);
+ free(EmptyStorageElementAddress);
+ return ElementStatus;
+
+}
+
+/*************************************************************************/
+
+
+
+/* Now the actual media movement routine! */
+RequestSense_T *MoveMedium(DEVICE_TYPE MediumChangerFD, int SourceAddress,
+ int DestinationAddress,
+ ElementStatus_T *ElementStatus,
+ Inquiry_T *inquiry_info, SCSI_Flags_T *flags)
+{
+ RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+ CDB_T CDB;
+ if ((inquiry_info->MChngr) && (inquiry_info->PeripheralDeviceType != MEDIUM_CHANGER_TYPE)) { /* if using the ATTACHED API */
+ CDB[0]=0xA7; /* MOVE_MEDIUM_ATTACHED */
+ } else {
+ CDB[0] = 0xA5; /* MOVE MEDIUM */
+ }
+ CDB[1] = 0; /* Reserved */
+ CDB[2] = (ElementStatus->TransportElementAddress >> 8) & 0xFF; /* Transport Element Address MSB */
+ CDB[3] = (ElementStatus->TransportElementAddress) & 0xff; /* Transport Element Address LSB */
+ CDB[4] = (SourceAddress >> 8) & 0xFF; /* Source Address MSB */
+ CDB[5] = SourceAddress & 0xFF; /* Source Address MSB */
+ CDB[6] = (DestinationAddress >> 8) & 0xFF; /* Destination Address MSB */
+ CDB[7] = DestinationAddress & 0xFF; /* Destination Address MSB */
+ CDB[8] = 0; /* Reserved */
+ CDB[9] = 0; /* Reserved */
+ if (flags->invert) {
+ CDB[10] = 1; /* Reserved */
+ } else {
+ CDB[10] = 0;
+ }
+ /* eepos controls the tray for import/export elements, sometimes. */
+ CDB[11] = 0 | (flags->eepos <<6); /* Control */
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 12,
+ NULL, 0, RequestSense) != 0) {
+ return RequestSense;
+ }
+ free(RequestSense);
+ return NULL; /* success! */
+}
+
+/* for Linux, this creates a way to do a short erase... the @#$%@ st.c
+ * driver defaults to doing a long erase!
+ */
+
+RequestSense_T *Erase(DEVICE_TYPE MediumChangerFD) {
+ RequestSense_T *RequestSense = xmalloc(sizeof(RequestSense_T));
+ CDB_T CDB;
+
+ CDB[0]=0x19;
+ CDB[1]=0; /* Short! */
+ CDB[2]=CDB[3]=CDB[4]=CDB[5]=0;
+
+ if (SCSI_ExecuteCommand(MediumChangerFD, Output, &CDB, 6,
+ NULL, 0, RequestSense) != 0) {
+ return RequestSense;
+ }
+ free(RequestSense);
+ return NULL; /* Success! */
+}
+
+
+#ifdef LONG_PRINT_REQUEST_SENSE
+
+static char *sense_keys[] = {
+ "No Sense", /* 00 */
+ "Recovered Error", /* 01 */
+ "Not Ready", /* 02 */
+ "Medium Error", /* 03 */
+ "Hardware Error", /* 04 */
+ "Illegal Request", /* 05 */
+ "Unit Attention", /* 06 */
+ "Data Protect", /* 07 */
+ "Blank Check", /* 08 */
+ "0x09", /* 09 */
+ "0x0a", /* 0a */
+ "Aborted Command", /* 0b */
+ "0x0c", /* 0c */
+ "Volume Overflow", /* 0d */
+ "Miscompare", /* 0e */
+ "0x0f" /* 0f */
+};
+
+static char Yes[]="yes";
+static char No[]="no";
+
+void PrintRequestSense(RequestSense_T *RequestSense)
+{
+ char *msg;
+
+ fprintf(stderr, "mtx: Request Sense: Long Report=yes\n");
+ fprintf(stderr, "mtx: Request Sense: Valid Residual=%s\n", RequestSense->Valid ? Yes : No);
+ if (RequestSense->ErrorCode==0x70) {
+ msg = "Current" ;
+ } else if (RequestSense->ErrorCode==0x71) {
+ msg = "Deferred" ;
+ } else {
+ msg = "Unknown?!" ;
+ }
+ fprintf(stderr, "mtx: Request Sense: Error Code=%0x (%s)\n",RequestSense->ErrorCode,msg);
+ fprintf(stderr, "mtx: Request Sense: Sense Key=%s\n",sense_keys[RequestSense->SenseKey]);
+ fprintf(stderr, "mtx: Request Sense: FileMark=%s\n", RequestSense->Filemark ? Yes : No);
+ fprintf(stderr, "mtx: Request Sense: EOM=%s\n", RequestSense->EOM ? Yes : No);
+ fprintf(stderr, "mtx: Request Sense: ILI=%s\n", RequestSense->ILI ? Yes : No);
+ if (RequestSense->Valid) {
+ fprintf(stderr, "mtx: Request Sense: Residual = %02X %02X %02X %02X\n",RequestSense->Information[0],RequestSense->Information[1],RequestSense->Information[2],RequestSense->Information[3]);
+ }
+ fprintf(stderr,"mtx: Request Sense: Additional Sense Code = %02X\n", RequestSense->AdditionalSenseCode);
+ fprintf(stderr,"mtx: Request Sense: Additional Sense Qualifier = %02X\n", RequestSense->AdditionalSenseCodeQualifier);
+ if (RequestSense->SKSV) {
+ fprintf(stderr,"mtx: Request Sense: Field in Error = %02X\n",RequestSense->BitPointer);
+ }
+ fprintf(stderr,"mtx: Request Sense: BPV=%s\n",RequestSense->BPV ? Yes : No);
+ fprintf(stderr,"mtx: Request Sense: Error in CDB=%s\n",RequestSense->CommandData ? Yes : No);
+ fprintf(stderr,"mtx: Request Sense: SKSV=%s\n",RequestSense->SKSV ? Yes : No);
+ fflush(stderr);
+ if (RequestSense->BPV || RequestSense -> SKSV) {
+ fprintf(stderr,"mtx: Request Sense: Field Pointer = %02X %02X\n",RequestSense->FieldData[0],RequestSense->FieldData[1]);
+ }
+}
+
+
+#else
+void PrintRequestSense(RequestSense_T *RequestSense)
+{
+ int i;
+ fprintf(stderr, "mtx: Request Sense: %02X",
+ ((unsigned char *) RequestSense)[0]);
+ for (i = 1; i < sizeof(RequestSense_T); i++)
+ fprintf(stderr, " %02X", ((unsigned char *) RequestSense)[i]);
+ fprintf(stderr, "\n");
+}
+
+#endif
+
+/* $Date: 2002/02/05 16:51:11 $
+ * $Log: mtxl.c,v $
+ * Revision 1.8.2.3 2002/02/05 16:51:11 elgreen
+ * mtx 1.2.16pre3
+ *
+ * Revision 1.8.2.2 2002/01/22 16:27:47 elgreen
+ * Handle too-big transport element descriptor lengths
+ *
+ * Revision 1.8.2.1 2002/01/17 22:24:35 elgreen
+ * Handle ADIC silliness of saying that it has 1 import/export element whose
+ * descriptor is 0 bytes in length
+ *
+ * Revision 1.8 2001/06/25 23:06:22 elgreen
+ * Readying this for 1.2.13 release
+ *
+ * Revision 1.7 2001/06/25 04:56:35 elgreen
+ * Kai to the rescue *again* :-)
+ *
+ * Revision 1.6 2001/06/24 07:02:01 elgreen
+ * Kai's fix was better than mine.
+ *
+ * Revision 1.5 2001/06/24 06:59:19 elgreen
+ * Kai found bug in the barcode backoff.
+ *
+ * Revision 1.4 2001/06/15 18:56:54 elgreen
+ * Arg, it doesn't help to check for 0 robot arms if you force it to 1!
+ *
+ * Revision 1.3 2001/06/15 14:26:09 elgreen
+ * Fixed brain-dead case of HP loaders that report they have no robot arms.
+ *
+ * Revision 1.2 2001/06/09 17:26:26 elgreen
+ * Added 'nobarcode' command to mtx (to skip the initial request asking for
+ * barcodes for mtx status purposes).
+ *
+ * Revision 1.1.1.1 2001/06/05 17:10:25 elgreen
+ * Initial import into SourceForge
+ *
+ * Revision 1.29 2001/05/01 01:39:23 eric
+ * Remove the Exabyte special case code, which seemed to be barfing me :-(.
+ *
+ * Revision 1.28 2001/04/28 00:03:10 eric
+ * update for 1.2.12.
+ *
+ * Revision 1.27 2001/04/18 19:27:39 eric
+ * whoops, move ImportExport parser into the $#%!@ ImportExport test :-(.
+ *
+ * Revision 1.26 2001/04/18 18:55:44 eric
+ * turn too many storage elements into a warning rather than a fatal error (sigh!)
+ *
+ * Revision 1.25 2001/04/18 18:46:54 eric
+ * $%!@# "C" precedence rules.
+ *
+ * Revision 1.24 2001/04/18 17:58:04 eric
+ * initialize EmptyStorageElementCount (sigh)
+ *
+ * Revision 1.23 2001/04/18 16:55:03 eric
+ * hopefully get the initialization for element status straigtened out.
+ *
+ * Revision 1.22 2001/04/18 16:32:59 eric
+ * Cleaned up all -Wall messages.
+ *
+ * Revision 1.21 2001/04/18 16:08:08 eric
+ * after re-arranging & fixing bugs
+ *
+ * Revision 1.20 2001/04/17 21:28:43 eric
+ * mtx 1.2.11
+ *
+ * Revision 1.19 2001/04/02 20:12:48 eric
+ * checkin, mtx 1.2.11pre6
+ *
+ * Revision 1.18 2001/03/06 01:37:05 eric
+ * Solaris changes
+ *
+ * Revision 1.17 2001/02/26 20:56:12 eric
+ * added debugging, removed Makefile
+ *
+ * Revision 1.16 2001/02/19 23:22:28 eric
+ * Add E2 bits
+ *
+ * Revision 1.15 2001/02/19 23:02:04 eric
+ * Apply SPARC patch.
+ *
+ * Revision 1.14 2001/01/26 15:58:10 eric
+ * Make work on solaris?
+ *
+ * Revision 1.13 2001/01/17 19:39:01 eric
+ * Changed mtx.h and mtxl.c to use autoconf stuff to figure out which SCSI
+ * routines to use (this way, if we ever port to an OS that has an identical
+ * SCSI interface to one of the supported OS's, we don't have to do anything
+ * special!).
+ *
+ * Revision 1.12 2001/01/17 16:36:26 eric
+ * added date & revision strings as needed
+ *
+ * Revision 1.11 2000/11/30 20:35:18 eric
+ * Added Erase command.
+ *
+ */
--- /dev/null
+/*
+ MTX -- SCSI Tape Attached Medium Changer Control Program
+
+ Copyright 1997-1998 Leonard N. Zubkoff <lnz@dandelion.com>
+ This file created by Eric Lee Green <eric@estinc.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.
+
+ $Date: 2001/06/19 21:51:32 $
+ $Revision: 1.2 $
+*/
+
+/* 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"
+
+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);
+
+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);
+
+int Inventory(DEVICE_TYPE MediumChangerFD); /* inventory library */
+int Eject(DEVICE_TYPE fd); /* unload tape or magazine */
+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
+
+#endif
--- /dev/null
+/* Copyright 2001 Enhanced Software Technologies Inc.
+ * Written by Eric Lee Green <eric@estinc.com>
+ *
+ *$Date: 2001/06/05 17:10:25 $
+ *$Revision: 1.1.1.1 $
+
+ 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.
+
+*/
+
+/* These are the SCSI commands for AIX. The syntax for AIX is:
+ *
+ * /dev/scsi<n>/<id>.<lun>
+ *
+ * where <n> is the number of the scsi adapter (0..n) and
+ * <id> and <lun> are the SCSI ID and LUN of the device you wish to
+ * talk to.
+ *
+ * AIX has a very flexible SCSI subsystem, but it is somewhat
+ * clumsy to use.
+ */
+
+/* we do very nasty thing here -- we operate upon device name! */
+DEVICE_TYPE SCSI_OpenDevice(char *DeviceName) {
+ /* okay, we must first parse out the ID and LUN: */
+ char *rptr;
+ struct tm_device_type *retval = (struct tm_device_type *) malloc(sizeof(struct tm_device_type));
+ int id,lun,filenum,idlun;
+
+ if (retval==NULL) {
+ fprintf(stderr,"%s: Allocation error in SCSI_OpenDevice for %s. Exiting.\n",argv0,DeviceName);
+ fflush(stderr);
+ exit(1);
+ }
+
+ rptr=strrchr(DeviceName,'/');
+
+ if (!rptr) {
+ fprintf(stderr,"%s: Illegal device name '%s'. Exiting.\n",argv0,DeviceName);
+ fflush(stderr);
+ exit(1);
+ }
+
+ *rptr++=0;
+
+ if (sscanf(rptr,"%d.%d",&id,&lun) < 2) {
+ /* whoops, we did not get 2 items: */
+ fprintf(stderr,"%s: Illegal device name '%s/%s'. Exiting.\n",argv0,DeviceName,rptr);
+ fflush(stderr);
+ exit(1);
+ }
+
+ /* Okay, now to try to open the DeviceName */
+ if ((filenum=open(DeviceName,0))<0) {
+ fprintf(stderr,"%s: Illegal device name '%s/%s'. Exiting.\n",argv0,DeviceName,rptr);
+ perror(argv0);
+ fflush(stderr);
+ exit(1);
+ }
+
+ retval->filenum=filenum;
+ retval->id=id;
+ retval->lun=lun;
+ retval->DeviceName=DeviceName;
+ return (DEVICE_TYPE) retval;
+}
+
+#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;
+}
+
+/* Okay , now *DO IT!* */
+int SCSI_ExecuteCommand(DEVICE_TYPE DeviceFD,
+ Direction_T Direction,
+ CDB_T *CDB,
+ int CDB_Length,
+ void *DataBuffer,
+ int DataBufferLength,
+ RequestSense_T *RequestSense) {
+
+ int id,lun;
+ struct tm_device_type *fd=(struct tm_device_type *) DeviceFD;
+ struct devinfo info;
+ struct sc_buf sb; /* the sc_buf struct needed to commicate w/adapter. */
+
+
+ id=fd->id;
+ lun=fd->lun;
+
+ /* okay, first of all, make sure we're not asking for a bigger
+ * operation than we are allowed to ask for, by going to the driver
+ * with IOCINFO.
+ */
+ if (ioctl(filenum,IOCINFO,&info)) {
+ fprintf(stderr,"%s: Could not get info for %s. Exiting.\n",argv0,fd->DeviceName);
+ exit(1);
+ }
+
+ /* Now check the max_transfer: */
+ if ((int)info.scsi.max_transfer < DataBufferLength) {
+ fprintf(stderr,"%s: SCSI transfer too large. %d requested, %d allowed.\n",argv0,DataBufferLength,(int)info.scsi.max_transfer);
+ fflush(stderr);
+ exit(1);
+ }
+
+ /* okay, we have our open file, we have the other stuff: Now initialize
+ a transaction with that ID/LUN:
+ */
+
+ if (ioctl(filenum, SCIOSTART,IDLUN(id,lun))) {
+ fprintf(stderr,"%s: Could not start SCSI transaction with %s/%d.%d. Exiting.\n",argv0,fd->DeviceName,id,lun);
+ fflush(stderr);
+ exit(1);
+ }
+
+ /* okay, now to decide command: */
+
+
+
+
+ /*... have finished command...*/
+
+ /* when done w/command, get rid of the connection: */
+ if (ioctl(filenum,SCIOSTOP,IDLUN(id,lun))) {
+ fprintf(stderr,"%s: Could not stop SCSI transaction with %s/%d.%d. Exiting.\n",argv0,fd->DeviceName,id,lun);
+ fflush(stderr);
+ exit(1);
+ }
+
+
--- /dev/null
+/* Copyright 2000 Enhanced Software Technologies Inc. (http://www.estinc.com)
+ Written by Eric Lee Green <eric@estinc.com>
+
+$Date: 2001/06/05 17:10:25 $
+$Revision: 1.1.1.1 $
+
+ 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,
+ /* retries */ 1,
+ /* cbfcnp*/ NULL,
+ /* flags */((DataBufferLength) ?
+ ((Direction == Input) ? CAM_DIR_IN : CAM_DIR_OUT) :
+ CAM_DIR_NONE),
+ /* tag action */ MSG_SIMPLE_Q_TAG,
+ /* data ptr */ DataBuffer,
+ /* xfer_len */ DataBufferLength,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* cdb_len */ CDB_Length,
+ /* timeout */ pass_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? */
+}
--- /dev/null
+/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
+ Changes copyright 2000 Eric Green <eric@estinc.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.
+
+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;
+}
--- /dev/null
+/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
+ Changes in Feb 2000 Eric Green <eric@estinc.com>
+
+$Date: 2001/06/19 21:51:32 $
+$Revision: 1.2 $
+
+ 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@estinc.com> changed
+ * it from using SCSI_IOCTL_SEND_COMMAND to using the SCSI generic interface.
+ */
+
+#ifndef HZ
+#define HZ 100
+#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? */
+
+DEVICE_TYPE SCSI_OpenDevice(char *DeviceName)
+{
+ int timeout=SG_SCSI_DEFAULT_TIMEOUT; /* 5 minutes */
+ int DeviceFD = open(DeviceName, O_RDWR);
+ if (DeviceFD < 0)
+ FatalError("cannot open SCSI device '%s' - %m\n", DeviceName);
+
+ if(ioctl(DeviceFD, SG_SET_TIMEOUT, &timeout)) {
+ FatalError("failed to set sg timeout - %m\n");
+ }
+
+ return (DEVICE_TYPE) DeviceFD;
+}
+
+static int sg_timeout = SG_SCSI_DEFAULT_TIMEOUT ;
+
+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 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
+
+
+#ifdef OLD_EXECUTE_COMMAND_STUFF
+
+int SCSI_ExecuteCommand(int DeviceFD,
+ Direction_T Direction,
+ CDB_T CDB,
+ int CDB_Length,
+ void *DataBuffer,
+ int DataBufferLength,
+ RequestSense_T *RequestSense)
+{
+ unsigned char *Command;
+ int Zero = 0, Result;
+ memset(RequestSense, 0, sizeof(RequestSense_T));
+ switch (Direction)
+ {
+ case Input:
+ Command = (unsigned char *)
+ xmalloc(8 + max(DataBufferLength, sizeof(RequestSense_T)));
+ memcpy(&Command[0], &Zero, 4);
+ memcpy(&Command[4], &DataBufferLength, 4);
+ memcpy(&Command[8], CDB, CDB_Length);
+ break;
+ case Output:
+ Command = (unsigned char *)
+ xmalloc(8 + max(CDB_Length + DataBufferLength, sizeof(RequestSense_T)));
+ memcpy(&Command[0], &DataBufferLength, 4);
+ memcpy(&Command[4], &Zero, 4);
+ memcpy(&Command[8], CDB, CDB_Length);
+ memcpy(&Command[8 + CDB_Length], DataBuffer, DataBufferLength);
+ break;
+ }
+ Result = ioctl(DeviceFD, SCSI_IOCTL_SEND_COMMAND, Command);
+ if (Result != 0)
+ memcpy(RequestSense, &Command[8], sizeof(RequestSense_T));
+ else if (Direction == Input)
+ memcpy(DataBuffer, &Command[8], DataBufferLength);
+ free(Command);
+ return Result;
+}
+
+#endif
--- /dev/null
+/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
+ Changes copyright 2000 Eric Green <eric@estinc.com>
+
+$Date: 2001/06/05 17:10:26 $
+$Revision: 1.1.1.1 $
+
+ 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;
+}
--- /dev/null
+/* Copyright 1997, 1998 Leonard Zubkoff <lnz@dandelion.com>
+ Changes copyright 2000 Eric Green <eric@estinc.com>
+
+$Date: 2001/12/13 16:30:11 $
+$Revision: 1.1.1.1.2.2 $
+
+ 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;
+}
--- /dev/null
+.\" scsitape.1 Document Copyright 2001 Eric Lee Green
+.\"
+.\" 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@estinc.com>.
+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 Eric Lee Green <eric@badtux.org> formerly of
+Enhanced Software Technologies Inc. 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 CVS from
+http://sourceforge.net/projects/mtx .
+
+.SH SEE ALSO
+.BR tapeinfo (1), mtx (1)
--- /dev/null
+/* Copyright 2001 Enhanced Software Technologies Inc.
+ * Released under terms of the GNU General Public License as
+ * required by the license on 'mtxl.c'.
+ * $Date: 2001/06/05 17:10:27 $
+ * $Revision: 1.1.1.1 $
+ */
+
+/* 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"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h> /* will try issuing some ioctls for Solaris, sigh. */
+
+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 unsigned char *device; /* the text of the device thingy. */
+static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) 0;
+
+
+
+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 }
+};
+
+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 */
+};
+
+
+
+/* open_device() -- set the 'fh' variable.... */
+void open_device(void) {
+
+ if (MediumChangerFD) {
+ 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) {
+ /* 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 */
+}
+
+/* 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=Eject(MediumChangerFD);
+ if (i<0) {
+ fprintf(stderr,"scsitape:eject failed\n");
+ fflush(stderr);
+ }
+ return i; /* if it failed, well, sigh.... */
+}
+
+
+
+
+/* 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]=(count >> 16) & 0xff;
+ CDB[3]=(count >>8) & 0xff;
+ CDB[4]=count & 0xff;
+ CDB[5]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+ slow_bzero((unsigned 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((unsigned 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,int code){
+ RequestSense_T sense;
+ CDB_T CDB;
+ unsigned char buffer[6];
+
+ CDB[0]=0x11; /* SET_MARK */
+ CDB[1]=code;
+ CDB[2]=(count >> 16) & 0xff;
+ CDB[3]=(count >>8) & 0xff;
+ CDB[4]=count & 0xff;
+ CDB[5]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+ slow_bzero((unsigned 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((unsigned 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]=(count >> 24) & 0xff;
+ CDB[4]=(count >> 16) & 0xff;
+ CDB[5]=(count >>8) & 0xff;
+ CDB[6]=count & 0xff;
+ CDB[7]=0;
+ CDB[8]=0;
+ CDB[9]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+ slow_bzero((unsigned 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;
+ unsigned 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((unsigned 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]=(count >> 16) & 0xff; /* 5 */
+ buffer[10]=(count >> 8) & 0xff; /* 6 */
+ buffer[11]= count & 0xff; /* 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] = (blockCount >> 16) & 0xff; /* MSB */
+ cmd[3] = (blockCount >> 8) & 0xff;
+ cmd[4] = blockCount & 0xff; /* 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_writet(DEVICE_TYPE fd, char * buf, unsigned int blocksize,
+ unsigned int *len,
+ unsigned int timeout) {
+ 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] = (blockCount >> 16) & 0xff; /* MSB */
+ cmd[3] = (blockCount >> 8) & 0xff;
+ cmd[4] = blockCount & 0xff; /* 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) {
+ unsigned 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_writet(MediumChangerFD,buffer,blocksize,&len,DEFAULT_TIMEOUT);
+ 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) {
+ unsigned char *buffer; /* the buffer we're going to be reading 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 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);
+
+
+ while (1) {
+ 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. */
+ }
+ }
+ }
+ }
+} /* got the goddam cancer of the curly braces. You can tell I used to
+ * do Lisp.
+ */
+
+
+/* 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);
+}
+
--- /dev/null
+diff -ux Makefile mtx-1.2.9/mtxl.c mtx-1.2.9.works/mtxl.c
+--- mtx-1.2.9/mtxl.c Mon Jul 31 12:33:53 2000
++++ mtx-1.2.9.works/mtxl.c Sat Nov 25 16:47:45 2000
+@@ -431,8 +431,10 @@
+ ElementStatus_T *ReadElementStatus(DEVICE_TYPE MediumChangerFD, RequestSense_T *RequestSense, Inquiry_T *inquiry_info, SCSI_Flags_T *flags) {
+ ElementStatusDataHeader_T *ElementStatusDataHeader;
+ ElementStatusPage_T *ElementStatusPage;
++ ElementStatusPage_T ESBuf;
+ ElementStatus_T *ElementStatus;
+ TransportElementDescriptor_T *TransportElementDescriptor;
++ TransportElementDescriptor_T TEBuf;
+
+ unsigned char *DataBuffer; /* size of data... */
+ unsigned char *DataPointer; /* point into the databuffer; */
+@@ -648,7 +651,8 @@
+ got_element_num++;
+ #endif
+
+- ElementStatusPage = (ElementStatusPage_T *) DataPointer;
++ memcpy(&ESBuf, DataPointer, sizeof(ElementStatusPage_T));
++ ElementStatusPage = &ESBuf;
+ DataPointer += sizeof(ElementStatusPage_T);
+ TransportElementDescriptorLength =
+ BigEndian16(ElementStatusPage->ElementDescriptorLength);
+@@ -671,8 +675,8 @@
+ BigEndian24(ElementStatusPage->ByteCountOfDescriptorDataAvailable);
+ while (BytesAvailable > 0)
+ {
+- TransportElementDescriptor =
+- (TransportElementDescriptor_T *) DataPointer;
++ memcpy(&TEBuf, DataPointer, TransportElementDescriptorLength);
++ TransportElementDescriptor = &TEBuf;
+ DataPointer += TransportElementDescriptorLength;
+ BytesAvailable -= TransportElementDescriptorLength;
+ ElementCount--;
--- /dev/null
+.\" tapeinfo.1 Document copyright 2000 Eric Lee Green
+.\" Program Copyright 2000 Eric Lee Green <eric@estinc.com>
+.\"
+.\" 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 Eric Lee Green <eric@badtux.org> formerly of
+Enhanced Software Technologies Inc. The 'mtx' home page is
+http://mtx.sourceforge.net and the actual code
+is currently available there and via CVS from
+http://sourceforge.net/projects/mtx/ .
+
+.SH SEE ALSO
+.BR mt (1), mtx (1), scsitape (1)
--- /dev/null
+/* Copyright 2000 Enhanced Software Technologies Inc.
+ * Released under terms of the GNU General Public License as
+ * required by the license on 'mtxl.c'.
+ * $Date: 2001/06/19 21:51:32 $
+ * $Revision: 1.2 $
+ */
+
+/*#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: Yes\n");
+ } else {
+ printf("Attached Changer: 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 */
+};
+
+
+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(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 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);
+ return;
+}
+
+static unsigned char
+*mode_sense(DEVICE_TYPE fd, int 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]=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);
+ /* This doesn't seem to do anything useful on my Tandberg so I
+ * am taking it out -- elg
+ */
+ /* Put out the # Blocks */
+ /* blocklen=((int)input_buffer[5]<<16)+ */
+ /* ((int)input_buffer[6]<<8)+ */
+ /* input_buffer[7]; */
+ /* printf("Num Blocks: %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);
+ return;
+}
+
+/* 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]);
+ return;
+}
+
+/* ***************************************
+ * 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);
+ return;
+}
+
+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= &(buffer[4]);
+
+ printf("SerialNumber: '");
+ for (i=0;i<lim;i++) {
+ putchar(*bufptr++);
+ }
+ printf("'\n");
+ return ; /* done! */
+}
+
+
+/* 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((unsigned 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]);
+
+ return;
+}
+
+/* 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((unsigned char *)&sense,sizeof(RequestSense_T));
+
+ /* set the timeout: */
+ 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);
+ }
+ return;
+}
+
+/* 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((unsigned 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]=(count >> 16) & 0xff;
+ CDB[3]=(count >>8) & 0xff;
+ CDB[4]=count & 0xff;
+ CDB[5]=0;
+
+ /* we really don't care if this command works or not, sigh. */
+ slow_bzero((unsigned 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);
+ /* ReportConfigPage(fd); */
+ /* ReportPartitionPage(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);
+ }
+
+
+ exit(0);
+}
+
--- /dev/null
+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
+
--- /dev/null
+$!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
--- /dev/null
+#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;
--- /dev/null
+!
+! 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
+
--- /dev/null
+/* 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);
+}
--- /dev/null
+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
--- /dev/null
+ .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
--- /dev/null
+/* 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);
+ }
+}