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

GNU General Public License

+ +

+


+ +

+ +

Table of Contents

+ + +

+ +


+ +

+ + + +

GNU GENERAL PUBLIC LICENSE

+

+Version 2, June 1991 + +

+ +
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ + + +

Preamble

+ +

+ The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + +

+

+ When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + +

+

+ To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +

+

+ For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +

+

+ We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +

+

+ Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +

+

+ Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + +

+

+ The precise terms and conditions for copying, distribution and +modification follow. + +

+ + +

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: +

+ +

+ +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: + + + + +

+ +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. +

+ +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. +

+ +4. + You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + +

+ +5. + You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +

+ +6. + Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + +

+ +7. + If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. +

+ +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. +

+ +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. +

+ +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +

+ +8. + If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +

+ +9. + The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. +

+ +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + +

+ + +10. + If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + + +

NO WARRANTY

+ +

+ +11. + BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + +

+ +12. + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +

+ + +

END OF TERMS AND CONDITIONS

+ + + +

How to Apply These Terms to Your New Programs

+ +

+ If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + +

+

+ To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + +

+ +
+one line to give the program's name and an idea of what it does.
+Copyright (C) yyyy  name of author
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+ +

+Also add information on how to contact you by electronic and paper mail. + +

+

+If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + +

+ +
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
+type `show w'.  This is free software, and you are welcome
+to redistribute it under certain conditions; type `show c' 
+for details.
+
+ +

+The hypothetical commands `show w' and `show c' should show +the appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and +`show c'; they could even be mouse-clicks or menu items--whatever +suits your program. + +

+

+You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + +

+ +
+Yoyodyne, Inc., hereby disclaims all copyright
+interest in the program `Gnomovision'
+(which makes passes at compilers) written 
+by James Hacker.
+
+signature of Ty Coon, 1 April 1989
+Ty Coon, President of Vice
+
+ +

+This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +


+ +

+FSF & GNU inquiries & questions to +gnu@gnu.org. +send other questions to +gnu@gnu.org. +

+Copyright notice above.
+Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111, USA +

+


+ + diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..5eae01e --- /dev/null +++ b/Makefile.in @@ -0,0 +1,137 @@ +# 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 diff --git a/README b/README new file mode 100644 index 0000000..e906899 --- /dev/null +++ b/README @@ -0,0 +1,38 @@ +MTX + +Programs: + mtx is the media changer control program + tapeinfo dumps some interesting stuff out of tape drives' mode pages and +sense pages. + loaderinfo dumps some interesting stuff out of loaders' mode pages and +sense pages. + scsitape sends raw SCSI commands to tape drives. Do not use this unless +you know exactly what you're doing, because you can easily get into a feud +with the system's own tape driver and end up locking up the whole system. + +INSTALLATION: + +WARNING: MUST HAVE GNU 'make' TO DO THIS! (e.g. use 'gmake' on freebsd, not +BSD 'make'!). + +Type ./configure to create a Makefile. Type 'make', then 'make +install'. Type 'man mtx' for info about mtx, and 'man tapeinfo' for +info about tapeinfo. Enjoy. + +Credits: + +The original 'mtx' program is copyright 1996-1997 by Leonard Zubkoff +. This version was modified for multi-drive, +optical changer, and tape library support by Eric Lee Green +. 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 diff --git a/TODO b/TODO new file mode 100644 index 0000000..77cabe0 --- /dev/null +++ b/TODO @@ -0,0 +1,14 @@ +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!). + diff --git a/config.guess b/config.guess new file mode 100755 index 0000000..4e5345f --- /dev/null +++ b/config.guess @@ -0,0 +1,973 @@ +#! /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 . +# 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 <$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 + + 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 + #include + + 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 + 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/^ //' <$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 </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 < +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' /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 + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # 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 < +# include +#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 + 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 diff --git a/config.h b/config.h new file mode 100644 index 0000000..3a39632 --- /dev/null +++ b/config.h @@ -0,0 +1,34 @@ +/* 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 + diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..38802d2 --- /dev/null +++ b/config.h.in @@ -0,0 +1,33 @@ +/* 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 + diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..c4123f2 --- /dev/null +++ b/config.sub @@ -0,0 +1,1273 @@ +#! /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 . +# Please send patches to . +# +# 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 diff --git a/configure b/configure new file mode 100755 index 0000000..bb58481 --- /dev/null +++ b/configure @@ -0,0 +1,2121 @@ +#! /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 <&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 < +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 < +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 < +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 < +#include +#include +#include +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 +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 +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 < +#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 +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 <&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 <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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#include +#include +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 < +#include +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 < +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 <&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 < +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 <&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 < +#include +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 < +#include +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 <&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 < +#include +#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 <&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 < +/* 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 < +/* 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 </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 < 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 <> $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 <> $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 <> $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 <> $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 + diff --git a/configure.in b/configure.in new file mode 100755 index 0000000..52160e9 --- /dev/null +++ b/configure.in @@ -0,0 +1,100 @@ +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) diff --git a/contrib/MTX.html b/contrib/MTX.html new file mode 100644 index 0000000..8ea9fce --- /dev/null +++ b/contrib/MTX.html @@ -0,0 +1,209 @@ + + +TapeChanger::MTX - use 'mtx' to manipulate a tape library + + + + + + + + + + + +
+

+

NAME

+

TapeChanger::MTX - use 'mtx' to manipulate a tape library

+

+


+

SYNOPSIS

+
+  use TapeChanger::MTX;
+
+  my $loaded = TapeChanger::MTX->loadedtape;
+  print "Currently loaded: $loaded\n" if ($loaded);
+
+  TapeChanger::MTX->loadtape('next');
+  my $nowloaded = TapeChanger::MTX->loadedtape; 
+  print "Currently loaded: $nowloaded\n" if ($nowloaded);
+
+
+
+See below for more available functions.
+

+


+

DESCRIPTION

+

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.

+

+


+

VARIABLES

+
+
$TapeChanger::MTX::MT +=item $TapeChanger::MTX::MTX
+
+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'. +

+
$TapeChanger::MTX::DRIVE
+
+
$TapeChanger::MTX::CONTROL
+
+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. +

+
$TapeChanger::MTX::EJECT
+
+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'. +

+
$TapeChanger::MTX::READY_TIME
+
+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. +

+
$TapeChanger::MTX::DEBUG
+
+Print debugging information? Set to '0' for normal verbosity, '1' for +debugging information, or '-1' for 'quiet mode' (be as quiet as possible). +

+

+


+

USAGE

+

This module uses the following functions:

+
+
tape_cmd ( COMMAND )
+
+
mt_cmd ( COMMAND )
+
+Runs 'mtx' and 'mt' as appropriate. COMMAND 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. +

+
numdrives ()
+
+
numslots ()
+
+
loadedtape ()
+
+Returns the number of drives, number of slots, and currently loaded tape +values, respectively, by parsing tape_cmd('status'). +

+
loadtape ( SLOT [, DRIVE] )
+
+Loads a tape into the tape changer, and waits until the drive is again +ready to be written to. SLOT can be any of the following (with the +relevant function indicated): +
+  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.
+

DRIVE is the drive to load, and defaults to 0. Returns 0 if +successful, an error string otherwise.

+

+
loadnexttape ()
+
+
loadprevtape ()
+
+
loadfirsttape ()
+
+
loadlasttape ()
+
+Loads the next, previous, first, and last tapes in the changer +respectively. Use tape_cmd('next'), tape_cmd('previous'), +tape_cmd('first'), and tape_cmd('last'), respectively. +

+
ejecttape ()
+
+Ejects the tape, by first ejecting the tape from the drive +(mt_cmd(rewind) then mt_cmd(offline)) and then returning it to its +slot (tape_cmd(unload)). Returns 1 if successful, 0 otherwise. +

+
resetchanger ()
+
+Resets the changer, ejecting the tape and loading the first one from the +changer. +

+
checkdrive ()
+
+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 mt_cmd(status). Returns 1 if so, 0 otherwise. +

+
reportstatus
+
+Returns a string containing the loaded tape and the drive that it's +mounted on. +

+
cannot_run ()
+
+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. +

+

+


+

NOTES

+

~/.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 setuid() 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.

+

+


+

REQUIREMENTS

+

Perl 5.6.0 or better, an installed 'mtx' binary, and a tape changer and +reader connected to the system.

+

+


+

TODO

+

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).

+

+


+

SEE ALSO

+

mtx, mt, tapechanger. Inspired by stc-changer, which comes +with the AMANDA tape backup package (http://www.amanda.org), and MTX, +available at http://mtx.sourceforge.net.

+

+


+

AUTHOR

+

Tim Skirvin <tskirvin@uiuc.edu>

+

+


+

COPYRIGHT

+

Copyright 2001-2002 by the University of Illinois Board of Trustees and +Tim Skirvin <tskirvin@ks.uiuc.edu>.

+ + + + diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..832ee0a --- /dev/null +++ b/contrib/README @@ -0,0 +1,3 @@ +Everything in here is copyrighted by its original copyright holder. +The contents of this directory are not maintained as part of the 'mtx' +project, they are maintained by their original copyright holders. diff --git a/contrib/TapeChanger-MTX-0.71b.tar.gz b/contrib/TapeChanger-MTX-0.71b.tar.gz new file mode 100644 index 0000000..a42a2e0 Binary files /dev/null and b/contrib/TapeChanger-MTX-0.71b.tar.gz differ diff --git a/contrib/config_sgen_solaris.sh b/contrib/config_sgen_solaris.sh new file mode 100755 index 0000000..1d8938b --- /dev/null +++ b/contrib/config_sgen_solaris.sh @@ -0,0 +1,153 @@ +#!/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 $? diff --git a/contrib/mtx-changer b/contrib/mtx-changer new file mode 100644 index 0000000..cf68ddd --- /dev/null +++ b/contrib/mtx-changer @@ -0,0 +1,431 @@ +#! /bin/sh +############################################################################### +# AMANDA Tape Changer script for use with the MTX tape changer program +# Version 1.0 - Tue Feb 20 13:59:39 CST 2001 +# +# Based on 'stc-changer' by Eric Berggren (eric@ee.pdx.edu) +# Updated by Tim Skirvin (tskirvin@ks.uiuc.edu) +# +# Given that there's no license...let's make this the Perl Artistic License. +# Just make sure you give me and Eric credit if you modify this. +############################################################################### + +### USER CONFIGURATION +# Name of the tape drive (takes place of "tapedev" option in amanda.conf) +# and default driver number in library (usu 0) that DRIVE_NAME points to +DRIVE_NAME="/dev/rmt/0n" +DRIVE_NUM=0 + +# Location of "STC" command and control device +MTX_CMD="/usr/local/sbin/mtx"; +MTX_CONTROL="/dev/scsi/changer/c4t1d0"; + +# Whether tape drive must eject tape before changer retrieves +# (ie, EXB-2x0). Usually okay if set while not necessary, bad if +# required but not set. +DRIVE_MUST_EJECT=1 + +# How long to check drive readiness (in seconds) after mounting (or +# ejecting) a volume (on some libraries, the motion or eject command may +# complete before the drive has the volume fully mounted and online, +# or ready for retrieval, resulting in "Drive not ready"/"Media not +# ready" errors). Do an "mt status" command every 5 seconds upto this +# time. +DRIVE_READY_TIME_MAX=120 + +# tape "mt" command location... +MT_CMD="/usr/bin/mt" # called via "MT_CMD -f DRIVE_NAME rewind" & + # "MT_CMD -f DRIVE_NAME offline" to eject + # and "MT_CMD -f DRIVE_NAME status" to get ready info + +############################################################################## +# +NumDrives=-1 +NumSlots=-1 +LastSlot=-1 +LoadedTape=-1 + +# +# Usage information +# +usage() +{ + echo + echo "Usage: $Progname [arg...]" + echo " -info reports capability and loaded tape" + echo " -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: 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: reason + echo "$ct $CommandResStr" + exit 1 + else + # success; output: 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 diff --git a/contrib/mtx.py b/contrib/mtx.py new file mode 100644 index 0000000..43564c3 --- /dev/null +++ b/contrib/mtx.py @@ -0,0 +1,306 @@ +# Copyright 2000 Enhanced Software Technologies Inc. +# Released under Free Software Foundation's General Public License, +# Version 2 or above +# +# This is an example of how to parse the 'mtx' output from a scripting +# language. +# +# Routine to call 'mtx' and read status, or +# whatever. +# +# We do this here rather than creating a Python "C" module because: +# 1) Reduces complexity of compile environment +# 2) Easier debugging. +# 3) More in keeping with the Unix philosophy of things. +# 4) Easier to add new functionality. +# +# + +import string +import os +import time # we can do some waiting here... + +def readpipe(command): + result="" + + infile=os.popen(command,"r") + + try: + s=infile.read() + except: + s="" + pass + if not s: + return None # didn't get anything :-(. + result=result+s + return result + + + + +# these are returned by the mtx.status routine: +class slotstatus: + def __init__(self,slotnum,middle,barcode=None): + middle=string.strip(middle) + try: + left,right=string.split(middle," ") + except: + left=middle + right=None + pass + # Note: We will not be able to test this at the moment since the + # 220 has no import/export port! + if right=="IMPORT/EXPORT": + self.importexport=1 + else: + self.importexport=0 + pass + self.slotnum=int(slotnum) # make sure it's an integer... + if left=="Full": + self.full=1 + else: + self.full=None + pass + if not barcode: + self.voltag=None + else: + l=string.split(barcode,"=",1) + self.voltag=l[1] + pass + return + pass + +# Drive status lines have this format: +#Data Transfer Element 0:Full (Storage Element 10 Loaded):VolumeTag = B0000009H +#Data Transfer Element 1:Empty + +class drivestatus: + def __init__(self,slotnum,middle,barcode=None): + self.slotnum=slotnum + if middle=="Empty": + self.full=None + self.origin=None + self.voltag=None + return + else: + self.full=1 + pass + + # okay, we know we have a tape in the drive. + # split and find out our origin: we will always have + # an origin, even if the #$@% mtx program had to dig one + # out of the air: + + l=string.split(middle," ") + self.origin=int(l[3]) + + if not barcode: + self.voltag=None # barcode of this element. + else: + l=string.split(barcode," ") + self.voltag=string.strip(l[2]) + pass + return + pass + +# This is the return value for mtx.status. +class mtxstatus: + def __init__(self): + self.numdrives=None + self.numslots=None + self.numexport=None + self.drives=[] # a list of drivestatus instances. + self.slots=[] # a list of slotstatus instances + self.export=[] # another list of slotstatus instances + return + pass + +# call 'mtx' and get barcode info, if possible: +# okay, we now have a string that consists of a number of lines. +# we want to explode this string into its component parts. +# Example format: +# Storage Changer /dev/sgd:2 Drives, 21 Slots +# Data Transfer Element 0:Full (Storage Element '5' Loaded) +# Data Transfer Element 1:Empty +# Storage Element 1:Full :VolumeTag=CLNA0001 +# Storage Element 2:Full :VolumeTag=B0000009 +# Storage Element 3:Full :VolumeTag=B0000010 +# .... +# What we want to do, then, is: +# 1) Turn it into lines by doing a string.split on newline. +# 2) Split the 1st line on ":" to get left and right. +# 3) Split the right half on space to get #drives "Drives," #slots +# 4) process the drives (Full,Empty, etc.) +# 5) For each of the remaining lines: Split on ':' +# 6) Full/Empty status is in 2) +# +configdir="/opt/brupro/bin" # sigh. + +def status(device): + retval=mtxstatus() + command="%s/mtx -f %s status" % (configdir,device) + result=readpipe(command) + if not result: + return None # sorry! + # now to parse: + + try: + lines=string.split(result,"\n") + except: + return None # sorry, no status! + + # print "DEBUG:lines=",lines + + try: + l=string.split(lines[0],":") + except: + return None # sorry, no status! + + # print "DEBUG:l=",l + leftside=l[0] + rightside=l[1] + if len(l) > 2: + barcode=l[3] + else: + barcode=None + pass + t=string.split(rightside) + retval.numdrives=int(t[0]) + retval.numslots=int(t[2]) + + for s in lines[1:]: + if not s: + continue # skip blank lines! + #print "DEBUG:s=",s + parts=string.split(s,":") + leftpart=string.split(parts[0]) + rightpart=parts[1] + try: + barcode=parts[2] + except: + barcode=None + pass + #print "DEBUG:leftpart=",leftpart + if leftpart[0]=="Data" and leftpart[1]=="Transfer": + retval.drives.append(drivestatus(leftpart[3],rightpart,barcode)) + pass + if leftpart[0]=="Storage" and leftpart[1]=="Element": + element=slotstatus(leftpart[2],rightpart,barcode) + if element.importexport: + retval.export.append(element) + else: + retval.slots.append(element) + pass + pass + continue + + return retval + +# Output of a mtx inquiry looks like: +# +#Product Type: Medium Changer +#Vendor ID: 'EXABYTE ' +#Product ID: 'Exabyte EZ17 ' +#Revision: '1.07' +#Attached Changer: No +# +# We simply return a hash table with these values { left:right } format. + +def mtxinquiry(device): + command="%s/mtx -f %s inquiry" % (configdir,device) + str=readpipe(command) # calls the command, returns all its data. + + str=string.strip(str) + lines=string.split(str,"\n") + retval={} + for l in lines: + # DEBUG # + l=string.strip(l) + print "splitting line: '",l,"'" + idx,val=string.split(l,':',1) + val=string.strip(val) + if val[0]=="'": + val=val[1:-1] # strip off single quotes, sigh. + pass + retval[idx]=val + continue + return retval + +# Now for the easy part: + +def load(device,slot,drive=0): + command="%s/mtx -f %s load %s %s >/dev/null " % (configdir,device,slot,drive) + status=os.system(command) + return status + +def unload(device,slot,drive=0): + command="%s/mtx -f %s unload %s %s >/dev/null " % (configdir,device,slot,drive) + return os.system(command) + +def inventory(device): + command="%s/mtx -f %s inventory >/dev/null " % (configdir,device) + return os.system(command) + +def wait_for_inventory(device): + # loop while we have an error return... + errcount=0 + while inventory(device): + if errcount==0: + print "Waiting for loader '%s'" % device + pass + time.sleep(1) + try: + s=status(device) + except: + s=None + pass + if s: + return 0 # well, whatever we're doing, we're inventoried :-( + errcount=errcount+1 + if errcount==600: # we've been waiting for 10 minutes :-( + return 1 # sorry! + continue + return 0 # we succeeded! + +# RCS REVISION LOG: +# $Log: 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 +# =->== +# diff --git a/contrib/mtxctl-0.0.2.tar.gz b/contrib/mtxctl-0.0.2.tar.gz new file mode 100644 index 0000000..de41b7d Binary files /dev/null and b/contrib/mtxctl-0.0.2.tar.gz differ diff --git a/contrib/tapechanger.html b/contrib/tapechanger.html new file mode 100644 index 0000000..8ea9fce --- /dev/null +++ b/contrib/tapechanger.html @@ -0,0 +1,209 @@ + + +TapeChanger::MTX - use 'mtx' to manipulate a tape library + + + + + + + + + + + +
+

+

NAME

+

TapeChanger::MTX - use 'mtx' to manipulate a tape library

+

+


+

SYNOPSIS

+
+  use TapeChanger::MTX;
+
+  my $loaded = TapeChanger::MTX->loadedtape;
+  print "Currently loaded: $loaded\n" if ($loaded);
+
+  TapeChanger::MTX->loadtape('next');
+  my $nowloaded = TapeChanger::MTX->loadedtape; 
+  print "Currently loaded: $nowloaded\n" if ($nowloaded);
+
+
+
+See below for more available functions.
+

+


+

DESCRIPTION

+

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.

+

+


+

VARIABLES

+
+
$TapeChanger::MTX::MT +=item $TapeChanger::MTX::MTX
+
+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'. +

+
$TapeChanger::MTX::DRIVE
+
+
$TapeChanger::MTX::CONTROL
+
+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. +

+
$TapeChanger::MTX::EJECT
+
+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'. +

+
$TapeChanger::MTX::READY_TIME
+
+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. +

+
$TapeChanger::MTX::DEBUG
+
+Print debugging information? Set to '0' for normal verbosity, '1' for +debugging information, or '-1' for 'quiet mode' (be as quiet as possible). +

+

+


+

USAGE

+

This module uses the following functions:

+
+
tape_cmd ( COMMAND )
+
+
mt_cmd ( COMMAND )
+
+Runs 'mtx' and 'mt' as appropriate. COMMAND 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. +

+
numdrives ()
+
+
numslots ()
+
+
loadedtape ()
+
+Returns the number of drives, number of slots, and currently loaded tape +values, respectively, by parsing tape_cmd('status'). +

+
loadtape ( SLOT [, DRIVE] )
+
+Loads a tape into the tape changer, and waits until the drive is again +ready to be written to. SLOT can be any of the following (with the +relevant function indicated): +
+  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.
+

DRIVE is the drive to load, and defaults to 0. Returns 0 if +successful, an error string otherwise.

+

+
loadnexttape ()
+
+
loadprevtape ()
+
+
loadfirsttape ()
+
+
loadlasttape ()
+
+Loads the next, previous, first, and last tapes in the changer +respectively. Use tape_cmd('next'), tape_cmd('previous'), +tape_cmd('first'), and tape_cmd('last'), respectively. +

+
ejecttape ()
+
+Ejects the tape, by first ejecting the tape from the drive +(mt_cmd(rewind) then mt_cmd(offline)) and then returning it to its +slot (tape_cmd(unload)). Returns 1 if successful, 0 otherwise. +

+
resetchanger ()
+
+Resets the changer, ejecting the tape and loading the first one from the +changer. +

+
checkdrive ()
+
+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 mt_cmd(status). Returns 1 if so, 0 otherwise. +

+
reportstatus
+
+Returns a string containing the loaded tape and the drive that it's +mounted on. +

+
cannot_run ()
+
+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. +

+

+


+

NOTES

+

~/.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 setuid() 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.

+

+


+

REQUIREMENTS

+

Perl 5.6.0 or better, an installed 'mtx' binary, and a tape changer and +reader connected to the system.

+

+


+

TODO

+

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).

+

+


+

SEE ALSO

+

mtx, mt, tapechanger. Inspired by stc-changer, which comes +with the AMANDA tape backup package (http://www.amanda.org), and MTX, +available at http://mtx.sourceforge.net.

+

+


+

AUTHOR

+

Tim Skirvin <tskirvin@uiuc.edu>

+

+


+

COPYRIGHT

+

Copyright 2001-2002 by the University of Illinois Board of Trustees and +Tim Skirvin <tskirvin@ks.uiuc.edu>.

+ + + + diff --git a/contrib/tapeinfo.py b/contrib/tapeinfo.py new file mode 100644 index 0000000..d5df136 --- /dev/null +++ b/contrib/tapeinfo.py @@ -0,0 +1,55 @@ +# Copyright 2000 Enhanced Software Technologies Inc. +# All Rights Reserved +# Released under Free Software Foundation's General Public License, +# Version 2 or above + +# Routine to call 'tapeinfo' and read status for a node. This is an +# example of how to parse the 'tapeinfo' output from a scripting language. +# + +import os +import string +import sys + + +configdir="/opt/brupro/bin" # sigh. + +def inquiry(device): + retval={} + + # okay, now do the thing: + + command="%s/tapeinfo -f %s" % (configdir,device) + + # Now to read: + + infile=os.popen(command,"r") + + try: + s=infile.readline() + except: + s="" + pass + if not s: + return None # did not get anything. + while s: + s=string.strip(s) + idx,val=string.split(s,':',1) + val=string.strip(val) + if val[0]=="'": + val=val[1:-1] # strip off single quotes, sigh. + val=string.strip(val) + pass + while "\0" in val: + # zapo! + val=string.replace(val,"\0","") + pass + retval[idx]=val + try: + s=infile.readline() + except: + s="" + pass + continue # to top of loop! + return retval + diff --git a/contrib/tapeload.pl b/contrib/tapeload.pl new file mode 100644 index 0000000..de1f221 --- /dev/null +++ b/contrib/tapeload.pl @@ -0,0 +1,118 @@ +!/usr/bin/perl +########################## tapeload ########################### +# This script uses mtx 1.2.9pre2 to load a tape based +# on its volume tag. You can +# specify a tape drive by number, but if you don`t, it puts the +# tape in the first available drive and returns the number of that drive, +# both from the standard output and as the exit value. +# A negative exit value indicates an error. +# If volume tags are missing from any full slot, bar codes are rescanned +# automatically. +# +# usage: +# tapeload TAPE_LABEL_1 # Loads tape with label TAPE_LABEL_1 into a drive +# or +# tapeload TAPE_LABEL_1 1 # Loads tape with label TAPE_LABEL_1 into drive #1 +# + +# Set this variable to your mtx binary and proper scsi library device. +$MTXBIN="/usr/local/bin/mtx -f /dev/sga" ; + +# Additions and corrections are welcome. +# This software comes with absolutely no warranty and every other imaginable +# disclaimer. +# -- Frank Samuelson sam@milagro.lanl.gov +################################################################## + +@wt= &mdo("status"); #slurp in the list of slots + +# Check to be certain that every full slot has a volume tag +for ($i=0; $i< $#wt; $i++) { # look through every line + if ( $wt[$i] =~ /Full/ && $wt[$i] !~ /VolumeTag/ ) { + # if the element is full, but there is no volume tag, do inventory + @wt= &mdo("inventory status"); + break; + } +} + +#try to find our tape +$slot=-1; +for ($i=0; $i< $#wt; $i++) { # look through every line + if ($wt[$i] =~ / *Storage Element (d*):Full :VolumeTag=(.*)/ ) { + if ($ARGV[0] eq $2) { # We found the tape + $slot=$1; # set the slot number + break; # stop reading the rest of the file. + } + } +} + +if ( $slot>0) { # we found the tape you wanted. + + $drivefound=-1; # set flag to bad value + for ($i=0; $i< $#wt; $i++) { # look through every line + # if this is a tape drive + if ($wt[$i] =~ / *Data Transfer Element (d*):(.*)/ ) { #parse the line + $drive=$1; + $state=$2; +# print STDERR "$wt[$i] $drive $state"; + if ($state =~ /Full/) { # This drive is full. + # if we are looking for a particular drive and this is it + if ( $#ARGV==1 && $drive == $ARGV[1]) { + print STDERR " ERROR: Specified drive $ARGV[1] is full."; + print STDERR @wt; + exit(-6); + } + } elsif ($state =~ /Empty/) { #This is a tape drive and it`s empty. + if ( $#ARGV==1 ) { # If we want a particular drive + if ($drive == $ARGV[1]) { # and this is it, + $drivefound=$drive; # mark it so. + break; + } + } else { # If any old drive will do + $drivefound=$drive; # Mark it. + break; + } + } else { # This is a tape drive, but what the heck is it? + print STDERR " Cannot assess drive status in line"; + print STDERR $wt[$i]; + exit(-7); + } + } + } + + if ( $drivefound < 0 ) { # specified drive was not found + print STDERR "Error: Specified drive $ARGV[1] was not found"; + print STDERR @wt; + exit(-8); + } + # Now we actually load the tape. + @dump=&mdo(" load $slot $drivefound "); + print "$drivefound"; + exit($drivefound); + # The end. + + +} else { + print STDERR " Ug. Tape $ARGV[0] is not in the library."; + print STDERR @wt; + exit(-4); +} + + +sub mdo # a subroutine to call mtx ; +{ +# print STDERR "$_[0]"; + if (!open(FD,"$MTXBIN $_[0] |")) { #call mtx function + print STDERR " ERRKK. Could not start mtx "; + exit (-9); + } + + @twt= ; # 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; +} diff --git a/contrib/tapeunload.pl b/contrib/tapeunload.pl new file mode 100644 index 0000000..e52a4ef --- /dev/null +++ b/contrib/tapeunload.pl @@ -0,0 +1,98 @@ +#!/usr/bin/perl +########################## tapeunload ########################### +# This script uses mtx 1.2.9pre2 to unload a tape +# based on its volume tag. You can +# specify a slot into which the tape should go, but if you don`t, it puts the +# tape into the slot from which it was taken. This slot number +# is returned +# both from the standard output and as the exit value. +# A negative exit value indicates an error. +# If volume tags are missing from any full slot or drive, +# bar codes are rescanned automatically. +# Note: This script assumes that the tape is ready to be removed from +# the drive. That means you may have to unload the tape from the drive +# with "mt offline" before the tape can be moved to a storage slot. +# + +# usage: +# tapeunload TAPE_LABEL_1 +# Removes tape with label TAPE_LABEL_1 from a drive and puts it +# back into its storage slot. Or, +# tapeunload TAPE_LABEL_1 40 +# Removes tape with label TAPE_LABEL_1 from a drive and puts it +# into its storage slot 40. +# + +# Set this variable to your mtx binary and proper scsi library device. +$MTXBIN="/usr/local/bin/mtx -f /dev/sga" ; + +# Additions and corrections are welcome. +# This software comes with absolutely no warranty and every other imaginable +# disclaimer. +# -- Frank Samuelson sam@milagro.lanl.gov + +################################################################## + +@wt= &mdo("status"); #slurp in the list of slots + +# Check to be certain that every full slot has a volume tag +# Rescanning probably will not help. I haven`t seen any bar code +# readers that read tapes that are in the drives. But you never know... +for ($i=0; $i< $#wt; $i++) { # look through every line + if ( $wt[$i] =~ /Full/ && $wt[$i] !~ /VolumeTag/ ) { + # if the element is full, but there is no volume tag, do inventory + @wt= &mdo("inventory status"); + break; + } +} + +#try to find our tape +$drivein=-1; +for ($i=0; $i< $#wt; $i++) { # look through every line + # for a full tape drive + if ($wt[$i] =~ / *Data Transfer Element (d*):Full (Storage Element +(d*) Loaded):VolumeTag = (.*)/ ){ + if ($ARGV[0] eq $3) { # We found our tape + $drivein=$1; # set the drive number + $slottogo=$2; # set the slot number + break; # stop reading the rest of the file. + } + } +} + +if ( $drivein>=0) { # we found the tape you wanted. + if ($#ARGV==1) { #If an alternative slot was requested, set it. + $slottogo=$ARGV[1]; # and let mtx handle the errors. + } + + # Now we unload the tape. + @dump=&mdo(" unload $slottogo $drivein "); + print "$slottogo"; + exit($slottogo); + # The end. + + +} else { + print STDERR " Ug. Tape $ARGV[0] is not in a tape drive."; + print STDERR @wt; + exit(-4); +} + + +sub mdo # a subroutine to call mtx ; +{ +# print STDERR "$_[0]"; + if (!open(FD,"$MTXBIN $_[0] |")) { #call mtx function + print STDERR " ERRKK. Could not start mtx "; + exit (-9); + } + + @twt= ; # 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; +} diff --git a/du/defs.h b/du/defs.h new file mode 100644 index 0000000..2da35e7 --- /dev/null +++ b/du/defs.h @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef int DEVICE_TYPE; + +#ifdef __osf__ +#include +#include +#include +#else /* must be ultrix */ +#include +#endif +#include +#include +#include +#include +#define DEV_CAM "/dev/cam" /* CAM device path */ + + +#define DIGITAL_UNIX diff --git a/du/scsi.c b/du/scsi.c new file mode 100644 index 0000000..7fc1745 --- /dev/null +++ b/du/scsi.c @@ -0,0 +1,206 @@ +/* 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; +} diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..6d7f9dc --- /dev/null +++ b/install-sh @@ -0,0 +1,253 @@ +#!/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 diff --git a/loaderinfo.1 b/loaderinfo.1 new file mode 100644 index 0000000..b25d72a --- /dev/null +++ b/loaderinfo.1 @@ -0,0 +1,90 @@ +.\" tapeinfo.1 Document copyright 2000 Eric Lee Green +.\" Program Copyright 2000 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 LOADERINFO 1 LOADERINFO1.0 +.SH NAME +loaderinfo \- report SCSI tape device info +.SH SYNOPSIS +loaderinfo -f +.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 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) diff --git a/loaderinfo.c b/loaderinfo.c new file mode 100644 index 0000000..1c7fcfe --- /dev/null +++ b/loaderinfo.c @@ -0,0 +1,335 @@ +/* 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 +#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;iNumMediumTransportElements[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 \n"); +} + + +/* we only have one argument: "-f ". */ +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); +} diff --git a/makedist b/makedist new file mode 100755 index 0000000..0af64fa --- /dev/null +++ b/makedist @@ -0,0 +1,19 @@ +#!/bin/sh + +# note -- this assumes 'bash' shell, GNU tar, 'gzip'. + +# pass a version number e.g. 1.4.3 as 1st parameter... +ME=`pwd` + +# okay, now to create a spec file w/the proper version number: +sed -e "1,\$s/@@VERSION@@/${1}/g" mtx.spec + +cd .. +if [ ! -s mtx-${1} ] +then + ln -s "${ME}" "mtx-${1}" +fi + + +tar --exclude CVS -czvhf mtx-${1}.tar.gz mtx-${1} + diff --git a/mam2debug.c b/mam2debug.c new file mode 100644 index 0000000..4b1efc0 --- /dev/null +++ b/mam2debug.c @@ -0,0 +1,123 @@ +/* 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 + 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 +#include +#include +#include + +#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); +} + diff --git a/mam2debug2.c b/mam2debug2.c new file mode 100644 index 0000000..9c30e4e --- /dev/null +++ b/mam2debug2.c @@ -0,0 +1,123 @@ +/* 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 + 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 +#include +#include +#include + +#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); +} + diff --git a/mtx.1 b/mtx.1 new file mode 100644 index 0000000..9362053 --- /dev/null +++ b/mtx.1 @@ -0,0 +1,246 @@ +.\" mtx.1 Document copyright 2000 Eric Lee Green +.\" Program Copyright 1996, 1997 Leonard Zubkoff +.\" Extensive changes 2000 by 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 MTX 1 MTX1.2 +.SH NAME +mtx \- control SCSI media changer devices +.SH SYNOPSIS +mtx [-f ] [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 [ ] +Load media from slot into drive . Drive 0 is assumed +if the drive number is omitted. +.TP 10 +.B unload [] [ ] +Unloads media from drive into slot . If is +omitted, defaults to drive 0 (as do all commands). +If 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 ] transfer +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 +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 [] +Loads drive 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 is omitted, defaults to first drive. + +.TP 10 +.B last [] +Loads drive from the last slot in the media changer. Unloads +the drive if there is already a tape in it. +.TP 10 +.B next [] +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 , 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 . +.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 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) diff --git a/mtx.c b/mtx.c new file mode 100644 index 0000000..7390566 --- /dev/null +++ b/mtx.c @@ -0,0 +1,778 @@ +/* + + 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 + Now maintained by Eric Lee Green + + 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 of Enteka Enterprise + Technology Service for porting MTX to Solaris/SPARC. + + Thanks to Carsten Koch 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 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 ] noattach \n\ + mtx [ -f ] inquiry | inventory \n\ + mtx [ -f ] [nobarcode] status\n\ + mtx [ -f ] first []\n\ + mtx [ -f ] last []\n\ + mtx [ -f ] next []\n\ + mtx [ -f ] previous []\n\ + mtx [ -f ] [invert] load []\n\ + mtx [ -f ] [invert] unload [][]\n\ + mtx [ -f ] [eepos eepos-number] transfer \n\ + mtx [ -f ] 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 argument '%s' to 'load' command\n", + arg1+1); + } + if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) { + FatalError( + "illegal 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 argument '%s' to 'unload' command\n", + arg1+1); + } + + if (arg2 < 0 || arg2 >= ElementStatus->DataTransferElementCount) { + FatalError( + "illegal 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) { + 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 + * + * + */ diff --git a/mtx.doc b/mtx.doc new file mode 100644 index 0000000..ae814e8 --- /dev/null +++ b/mtx.doc @@ -0,0 +1,209 @@ +[WARNING: This document is of historical value only! Please read + 'mtxl.README.html' and 'man mtx' for current documentation! The only + thing useful here is examples of how to use the 'tar' command for + multi-tape backups. +] + + MTX - SCSI Tape Medium Changer Control Program + + Version 1.1 for Linux, Solaris, IRIX, Digital Unix, and OpenVMS + + 2 June 1998 + + Leonard N. Zubkoff + Dandelion Digital + lnz@dandelion.com + + Copyright 1997-1998 by Leonard N. Zubkoff + + + 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. +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. 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. 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 " 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 ] 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 ] 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 ] load + +The "load" command loads the volume in Storage Element +into the Data Transfer Element. An error is signaled if the Data Transfer +Element is already full. + + +mtx [ -f ] unload [ ] + +The "unload" command unloads the volume in the Data Transfer Element into +Storage Element . If 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 ] 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 ] 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 ] 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 ] 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 / + +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. diff --git a/mtx.h b/mtx.h new file mode 100644 index 0000000..1144ea6 --- /dev/null +++ b/mtx.h @@ -0,0 +1,523 @@ +/* MTX -- SCSI Tape Attached Medium Control Program + + Copyright 1997-1998 Leonard N. Zubkoff + + 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 +#include + +#if HAVE_STDLIB_H +# include +#endif + +#if HAVE_FCNTL_H +# include +#endif + +#if HAVE_SYS_TYPES_H +# include +#endif + +#if HAVE_STRING_H +# include +#else +# include +#endif + +#if HAVE_UNISTD_H +# include +#endif + +#if HAVE_STDARG_H +# include +#endif + +#if HAVE_SYS_STAT_H +# include +#endif + +#if HAVE_SYS_IOCTL_H +# include +#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 +# include +# include +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 /* easy (?) access to the CAM user library. */ +# include +# include /* 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 +typedef int DEVICE_TYPE; +#endif + +/* the scsi_ctl interface, as used on HP/UX: */ +#if HAVE_SYS_SCSI_CTL_H +# include +# include +# include +# include + 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 +#include +#include /* 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 +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. */ diff --git a/mtx.spec b/mtx.spec new file mode 100644 index 0000000..e2dbb85 --- /dev/null +++ b/mtx.spec @@ -0,0 +1,105 @@ +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 +- 1.2.14 +- Moved changelog to end of spec file so it doesn't interfere +- + +* Wed Apr 18 2001 Kenneth Porter +- 1.2.12pre1 +- Need to create usr/sbin for install + +* Fri Mar 02 2001 Eric Green +- 1.2.11pre6 +- Move tapeinfo,loaderinfo, scsitape to /usr/sbin rather than /sbin + +* Wed Feb 28 2001 Kenneth Porter +- 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 +- 1.2.11pre3 +- Removed patch, now use ./configure. + +* Mon Nov 27 2000 Eric Green +- 1.2.10 +- Fixed patching to use the portable.patch. + +* Tue Jul 25 2000 Eric Green +- 1.2.8 +- Added portability patch to mtx.spec so should compile on Red Hat Alpha etc. + +* Thu Jun 6 2000 Eric Green +- 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 +- 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 +- Spell sourceforge right so the link at rpmfind.net will work. + +* Thu May 4 2000 Kenneth Porter +- 1.2.5 + +* Thu Oct 29 1998 Ian Macdonald +- moved mtx from /sbin to /bin, seeing as mt is also located there + +* Fri Oct 23 1998 Ian Macdonald +- first RPM release diff --git a/mtx.spec.in b/mtx.spec.in new file mode 100644 index 0000000..2f38e76 --- /dev/null +++ b/mtx.spec.in @@ -0,0 +1,105 @@ +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 +- 1.2.14 +- Moved changelog to end of spec file so it doesn't interfere +- + +* Wed Apr 18 2001 Kenneth Porter +- 1.2.12pre1 +- Need to create usr/sbin for install + +* Fri Mar 02 2001 Eric Green +- 1.2.11pre6 +- Move tapeinfo,loaderinfo, scsitape to /usr/sbin rather than /sbin + +* Wed Feb 28 2001 Kenneth Porter +- 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 +- 1.2.11pre3 +- Removed patch, now use ./configure. + +* Mon Nov 27 2000 Eric Green +- 1.2.10 +- Fixed patching to use the portable.patch. + +* Tue Jul 25 2000 Eric Green +- 1.2.8 +- Added portability patch to mtx.spec so should compile on Red Hat Alpha etc. + +* Thu Jun 6 2000 Eric Green +- 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 +- 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 +- Spell sourceforge right so the link at rpmfind.net will work. + +* Thu May 4 2000 Kenneth Porter +- 1.2.5 + +* Thu Oct 29 1998 Ian Macdonald +- moved mtx from /sbin to /bin, seeing as mt is also located there + +* Fri Oct 23 1998 Ian Macdonald +- first RPM release diff --git a/mtxl.README.html b/mtxl.README.html new file mode 100644 index 0000000..48d1537 --- /dev/null +++ b/mtxl.README.html @@ -0,0 +1,165 @@ + + + + + SCSI Media Changer and Backup Device Control System + + + + +
+

SCSI Media Changer and Backup Device Control System

+
+

+[Also see the SourceForge +project page.] +

+mtx 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). mtx is designed to be a low level driver in a larger +scripted backup solution, such as Amanda. +mtx is not +supposed to itself be a high level interface to the SCSI devices that +it controls. +

+This version has the following features: + +

    +
  • Will deal with LARGE media libraries (over a hundred elements). +
  • Supports multi-drive media changers such as the Exabyte 220 dual- +drive tape library. +
  • Supports the 'invert' bit for optical jukeboxes that need that in +order to flip their media. +
  • Supports the 'eepos' bits for libraries that need this to extend/retract +their import/export tray. +
  • Now supports import/export elements! +
  • Reports volume tags (bar codes) and "alternate volume tags" +(whatever those are!) for those tape libraries +that support them. +
  • Now runs under FreeBSD and at least Solaris 8. + +
  • Now has a 'man' page! +
  • 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). +
+ +

+ 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. + +

Source Code

+The current source code is at: + +RPMs may be available from the following place: + +A .spec file is now included in the 'mtx' distribution for building your +own RPM's. +

+Note that RPMs +are courtesy of Kenneth Porter, +who should be contacted regarding rpm-related problems. +

+ +

Known Bugs And Limitations

+
    +
  • + 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. +
  • +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. +
  • + 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. + +
  • + 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. + +
  • 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). + +
  • VMS and Tru64 support are probably irretrievably busted. + + +
  • This program will only use the first arm of multiple-arm robots unless +the robot re-maps all arms to one element ID. + +
  • 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 +http://download.sourceforge.net/amanda. + +
+ +

Philosophy

+The Unix philosophy is "many small tools chained together". mtx supplies +those small tools so that you can create your own backup and +recovery tools by chaining +mtx pieces together, whether it be with /bin/sh, Perl, Python, or +CAML. + + +

Support

+ + +

See Also:

+ +
+
Maintained by Eric Lee Green
+ Hosted by VA Linux's SourceForge

+ + + + +Last modified: Fri Nov 16 11:09:19 MST 2001 + + + diff --git a/mtxl.c b/mtxl.c new file mode 100644 index 0000000..1ad050d --- /dev/null +++ b/mtxl.c @@ -0,0 +1,1316 @@ +/* MTX -- SCSI Tape Attached Medium Changer Control Program + + Copyright 1997-1998 by Leonard N. Zubkoff + +$Date: 2002/02/05 16:51:11 $ +$Revision: 1.8.2.3 $ + + This file created Feb 2000 by Eric Lee Green 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;iNumStorage;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;iNumDataTransfer;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; iDataTransferElementCount;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; jStorageElementCount; 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;iDataTransferElementCount;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. + * + */ diff --git a/mtxl.h b/mtxl.h new file mode 100644 index 0000000..560c96f --- /dev/null +++ b/mtxl.h @@ -0,0 +1,73 @@ +/* + MTX -- SCSI Tape Attached Medium Changer Control Program + + Copyright 1997-1998 Leonard N. Zubkoff + This file created by Eric Lee Green + + 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 diff --git a/scsi_aix.c b/scsi_aix.c new file mode 100644 index 0000000..1c65b6a --- /dev/null +++ b/scsi_aix.c @@ -0,0 +1,144 @@ +/* Copyright 2001 Enhanced Software Technologies Inc. + * Written by Eric Lee Green + * + *$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/. + * + * where is the number of the scsi adapter (0..n) and + * and 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); + } + + diff --git a/scsi_freebsd.c b/scsi_freebsd.c new file mode 100644 index 0000000..8c88d82 --- /dev/null +++ b/scsi_freebsd.c @@ -0,0 +1,115 @@ +/* Copyright 2000 Enhanced Software Technologies Inc. (http://www.estinc.com) + Written by Eric Lee Green + +$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? */ +} diff --git a/scsi_hpux.c b/scsi_hpux.c new file mode 100644 index 0000000..66a07b9 --- /dev/null +++ b/scsi_hpux.c @@ -0,0 +1,123 @@ +/* Copyright 1997, 1998 Leonard Zubkoff + Changes copyright 2000 Eric Green + + 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 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; +} diff --git a/scsi_linux.c b/scsi_linux.c new file mode 100644 index 0000000..e5378d3 --- /dev/null +++ b/scsi_linux.c @@ -0,0 +1,397 @@ +/* Copyright 1997, 1998 Leonard Zubkoff + Changes in Feb 2000 Eric Green + +$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 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 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 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 diff --git a/scsi_sgi.c b/scsi_sgi.c new file mode 100644 index 0000000..c215de9 --- /dev/null +++ b/scsi_sgi.c @@ -0,0 +1,75 @@ +/* Copyright 1997, 1998 Leonard Zubkoff + Changes copyright 2000 Eric Green + +$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; +} diff --git a/scsi_sun.c b/scsi_sun.c new file mode 100644 index 0000000..ca37a3b --- /dev/null +++ b/scsi_sun.c @@ -0,0 +1,141 @@ +/* Copyright 1997, 1998 Leonard Zubkoff + Changes copyright 2000 Eric Green + +$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; +} diff --git a/scsitape.1 b/scsitape.1 new file mode 100644 index 0000000..2f9d7e9 --- /dev/null +++ b/scsitape.1 @@ -0,0 +1,180 @@ +.\" 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 ] 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 +Set the tape drive's SCSI block size to bytes. (NOTE: if you are +using your OS's native tape driver, THIS IS EVIL!). + +.TP 10 +.B fsf +Go forward by tapemarks. +.TP 10 +.B bsf +Go to immediately previous the 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 + write filemarks ( 'mark 0' flushes the drive's buffers ). +.TP 10 +.B seek +Seek to a logical position that was reported by a previous 'tapeinfo' +command. +.TP 10 +.B write +write blocks from stdin to the tape. Chunk the data into -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 [] [ <#blocks/#bytes> ] +read blocks from the tape, write them to stdout. If we are in variable +block mode, 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 + 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 . +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 +where you are doing variable-block-size reads and wish for 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 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) diff --git a/scsitape.c b/scsitape.c new file mode 100644 index 0000000..1ff3892 --- /dev/null +++ b/scsitape.c @@ -0,0 +1,851 @@ +/* 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 -- set the block size to + fsf -- go forward by filemarks + bsf -- go backward by 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 -- write filemarks. + seek -- seek to position . + + write <-- write blocks from stdin to the tape + read [] [<#blocks/#bytes>] -- read blocks from tape, write to stdout. + + See the 'tapeinfo' program for status info about the tape drive. + + */ + +#include +#include + +#include "mtx.h" +#include "mtxl.h" + +#include +#include +#include +#include /* will try issuing some ioctls for Solaris, sigh. */ + +void Usage(void) { + FatalError("Usage: scsitape -f where is:\n setblk | fsf | bsf | eod | rewind | eject | mark |\n seek | read [ [] \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) { + 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); +} + diff --git a/sparc-patch1 b/sparc-patch1 new file mode 100644 index 0000000..3b62195 --- /dev/null +++ b/sparc-patch1 @@ -0,0 +1,35 @@ +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--; diff --git a/tapeinfo.1 b/tapeinfo.1 new file mode 100644 index 0000000..b53a86c --- /dev/null +++ b/tapeinfo.1 @@ -0,0 +1,72 @@ +.\" tapeinfo.1 Document copyright 2000 Eric Lee Green +.\" Program Copyright 2000 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 TAPEINFO 1 TAPEINFO1.0 +.SH NAME +tapeinfo \- report SCSI tape device info +.SH SYNOPSIS +tapeinfo -f +.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 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) diff --git a/tapeinfo.c b/tapeinfo.c new file mode 100644 index 0000000..820004e --- /dev/null +++ b/tapeinfo.c @@ -0,0 +1,753 @@ +/* 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#] e.g. "TapeAlert:[22]Cleaning Cartridge Worn Out" + * + * Mode Sense: + * Data Compression Page: + * DataCompEnabled: + * DataCompCapable: + * DataDeCompEnabled: + * CompType: + * DeCompType: + * + * 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]: + * Partition[1]:... + * + * Read Block Limits command: + * MinBlock:<#> -- Minimum block size. + * MaxBlock:<#> -- Maximum block size. + */ + +#include +#include +#include "mtx.h" +#include "mtxl.h" + +char *argv0; + +void usage(void) { + FatalError("Usage: tapeinfo -f \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 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;ilength;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> 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 ". */ +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); +} + diff --git a/vms/000readme b/vms/000readme new file mode 100644 index 0000000..854d58a --- /dev/null +++ b/vms/000readme @@ -0,0 +1,175 @@ +MTX -- SCSI Tape Attached Medium Changer Control Program + +Copyright 1997 by Leonard N. Zubkoff + +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 + diff --git a/vms/build.com b/vms/build.com new file mode 100644 index 0000000..2f6308d --- /dev/null +++ b/vms/build.com @@ -0,0 +1,34 @@ +$!x='f$ver(0) +$ if f$parse("[.VMS]A.A").eqs."" +$ then +$ write sys$output "?Error: Use $ @[.VMS]BUILD from the mtx directory" +$ exit 44 +$ endif +$ alpha = f$getsyi("hw_model").ge.1024 +$ vax = .not.alpha +$ exe = "EXE" +$ obj = "OBJ" +$ sysexe="" +$ migrate="" +$ if alpha then exe="ALPHA_EXE" +$ if alpha then obj="ALPHA_OBJ" +$ if alpha then sysexe="/SYSEXE" +$ if alpha then migrate="/MIGRATION/NOOPT" +$ set verify +$ if "''p1'".eqs."LINK" then goto do_link +$ CC /DECC/DEB/NOOP MTX.C/DEB/NOOP/OBJECT=MTX.'obj' +$ if f$search("MTX.''obj';-1").nes."" then - + purge/log MTX.'obj' +$ CC /DECC/DEB/NOOP [.VMS]LDRSET.C/DEB/NOOP/OBJECT=[.VMS]LDRSET.'obj' +$ if f$search("[.VMS]LDRSET.''obj';-1").nes."" then - + purge/log [.VMS]LDRSET.'obj' +$ MACRO'migrate' /DEB [.VMS]LDRUTIL.MAR - + /OBJECT=[.VMS]LDRUTIL.'obj' +$ if f$search("[.VMS]LDRUTIL.''obj';-1").nes."" then - + purge/log [.VMS]LDRUTIL.'obj' +$! +$ do_link: +$ link/notrace mtx.'obj'/exe=mtx.'exe' +$ link [.vms]ldrset.'obj',[.vms]ldrutil.'obj' - + /exe=ldrset.'exe' 'sysexe' +$ exit diff --git a/vms/defs.h b/vms/defs.h new file mode 100644 index 0000000..68dd7d7 --- /dev/null +++ b/vms/defs.h @@ -0,0 +1,42 @@ +#ifdef __DECC +#pragma module MTX "V01-00" +#else +#module MTX "V01-00" +#endif + +typedef int DEVICE_TYPE; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; diff --git a/vms/descrip.mms b/vms/descrip.mms new file mode 100644 index 0000000..fb08467 --- /dev/null +++ b/vms/descrip.mms @@ -0,0 +1,77 @@ +! +! MMS System build for MTX and LDRSET utility +! +!Global build flag macros +! +CDEBUG = /DEB/NOOP +MDEBUG = /DEB + +CFLAGS = /DECC$(CDEBUG) +MFLAGS = $(MDEBUG) + +.IFDEF __AXP__ +.SUFFIXES .ALPHA_OBJ +MFLAGS = /MIGRATE$(MFLAGS)/NOOP +DBG = .ALPHA_DBG +EXE = .ALPHA_EXE +OBJ = .ALPHA_OBJ +OPT = .ALPHA_OPT +SYSEXE=/SYSEXE + +.ELSE +DBG = .DBG +EXE = .EXE +OPT = .OPT +OBJ = .OBJ +SYSEXE= + +.ENDIF + +PURGEOBJ = if f$search("$(MMS$TARGET_NAME)$(OBJ);-1").nes."" then purge/log $(MMS$TARGET_NAME)$(OBJ) + +! +!Bend the default build rules for C, MACRO, and MESSAGE +! +.C$(OBJ) : + $(CC) $(CFLAGS) $(MMS$SOURCE)$(CDEBUG)/OBJECT=$(MMS$TARGET_NAME)$(OBJ) + $(PURGEOBJ) +.MAR$(OBJ) : + $(MACRO) $(MFLAGS) $(MMS$SOURCE)$(MDEBUG)/OBJECT=$(MMS$TARGET_NAME)$(OBJ) + $(PURGEOBJ) +.CLD$(OBJ) : + SET COMMAND/OBJECT=$(MMS$TARGET_NAME)$(OBJ) $(MMS$SOURCE) + $(PURGEOBJ) +.MSG$(OBJ) : + MESSAGE $(MMS$SOURCE)/OBJECT=$(MMS$TARGET_NAME)$(OBJ) + $(PURGEOBJ) + + +DEFAULT : ERROR,- + MTX,- + LDRSET + @ ! + +ERROR : + @ if f$parse("[.VMS]A.A").eqs."" then write sys$output "?Error: Use $ MMS/DESCRIP=[.VMS] from the mtx directory" + +MTX : mtx$(EXE) + @ ! + +mtx$(EXE) : mtx$(OBJ) + $ link/notrace mtx$(OBJ)/exe=mtx$(EXE) + +mtx$(OBJ) : mtx.c,[.vms]scsi.c,[.vms]defs.h + +LDRSET : ldrset$(EXE),ldrset.cld + @ ! + +ldrset.cld : [.vms]ldrset.cld + $ copy [.vms]ldrset.cld []/log + +ldrset$(EXE) : [.vms]ldrset$(OBJ),[.vms]ldrutil$(OBJ) + $ link [.vms]ldrset$(OBJ),[.vms]ldrutil$(OBJ)/exe=ldrset$(EXE)$(SYSEXE) + +[.vms]ldrset$(OBJ) : [.vms]ldrset.c + +[.vms]ldrutil$(OBJ) : [.vms]ldrutil.mar + diff --git a/vms/ldrset.c b/vms/ldrset.c new file mode 100644 index 0000000..c0efd0c --- /dev/null +++ b/vms/ldrset.c @@ -0,0 +1,183 @@ +/* LDRSET - Set the state of the LDR flag in UCB$L_DEVCHAR2 for a +** SCSI magtape. REQUIRES CMKRNL privilege. +** +** Copyright 1999 by TECSys Development, Inc. http://www.tditx.com +** +** This program is free software; you may redistribute and/or modify it under +** the terms of the GNU General Public License Version 2 as published by the +** Free Software Foundation. +** +** This program is distributed in the hope that it will be useful, but +** WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY +** or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +** for complete details. +** +** Description: +** This is a small KERNEL MODE utility program to go set or reset +** the DEV$M_LDR flag in UCB$L_DEVCHAR2 for a specified device. While +** a certain amount of checking is performed, and while the author +** believes that this utility is safe, ONLY YOU can make that +** determination with respect to your environment. There are NO +** GUARANTEES WHATSOEVER that use of this program will not CRASH +** YOUR SYSTEM or worse. TDI disclaims any responsibility for any +** losses or damages from the use of this program. +** +** With all that said... this utility can be used [example: system +** startup] to set the LDR flag for the autoloader tape device. +** With the loader flag properly set, you can re-enable the LDR +** check section in MTX and be fairly well certain that the MTX +** utility will not be used against a drive that is not a SCSI +** MAGTAPE with a LOADER attached. +** +** +** LDRSET.CLD: +** define verb LDRSET +** image sys$disk:[]ldrset.exe +** parameter p1, label=device, +** prompt="Device", +** value(required,type=$FILE) +** qualifier SET, nonnegatable +** qualifier RESET, nonnegatable +** disallow any2 (SET, RESET) +** disallow NOT SET AND NOT RESET +*/ +#ifdef __DECC +#pragma module LDRSET "V01-00" +#else +#module LDRSET "V01-00" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/vms/ldrset.cld b/vms/ldrset.cld new file mode 100644 index 0000000..0dfc543 --- /dev/null +++ b/vms/ldrset.cld @@ -0,0 +1,9 @@ +define verb LDRSET + image sys$disk:[]ldrset.exe + parameter p1, label=device, + prompt="Device", + value(required,type=$FILE) + qualifier SET, nonnegatable + qualifier RESET, nonnegatable + disallow any2 (SET, RESET) + disallow NOT SET AND NOT RESET diff --git a/vms/ldrutil.mar b/vms/ldrutil.mar new file mode 100644 index 0000000..9d0ea31 --- /dev/null +++ b/vms/ldrutil.mar @@ -0,0 +1,104 @@ + .title LDRUTIL - Obtain ucb for assigned channel + .ident /LDRUTIL V1.0/ +; LDRUTIL - VMS UCB LDR bit utility library +; +; TECSys Development, Inc., April 1998 +; +; This file may be copied under the terms and conditions of version 2 +; of the GNU General Public License, as published by the Free +; Software Foundation (Cambridge, Massachusetts). +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to the Free Software +; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +; +; + .link "sys$system:sys.stb"/selective_search + .library /sys$share:lib/ + + .NTYPE ...IS_IT_ALPHA,R22 ;Get the type of R22 + ...IS_IT_ALPHA = <...IS_IT_ALPHA@-4&^XF>-5 + .IIF EQ,...IS_IT_ALPHA, ALPHA=1 + + $ssdef + $ucbdef + $ccbdef + $chfdef + $dcdef + $devdef + $pcbdef + + .psect $$code,exe,rd,nowrt,shr +.IF NDF,ALPHA +.entry finducb,^m ;Find UCB address from channel +.IFF +.call_entry, 2,home_args=TRUE,- + preserve=,- + output=,- + 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 ;Find UCB address from channel +.IFF +.call_entry, 2,home_args=TRUE,- + preserve=,- + output=,- + 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 ;Find UCB address from channel +.IFF +.call_entry, 2,home_args=TRUE,- + preserve=,- + output=,- + label=_setldr +.endc + $cmkrnl_s - + routin = __setldr,- + arglst = (AP) + ret + +.IF NDF,ALPHA +.entry __clrldr,^m ;Find UCB address from channel +.IFF +.call_entry, 2,home_args=TRUE,- + preserve=,- + output=,- + 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 ;Find UCB address from channel +.IFF +.call_entry, 2,home_args=TRUE,- + preserve=,- + output=,- + label=_clrldr +.endc + $cmkrnl_s - + routin = __clrldr,- + arglst = (AP) + ret + + .end diff --git a/vms/scsi.c b/vms/scsi.c new file mode 100644 index 0000000..6cd7685 --- /dev/null +++ b/vms/scsi.c @@ -0,0 +1,372 @@ +/* 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); + } +}