From a4f6885e857140bccd92a9eecaa1decd048d48b7 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Thu, 5 Jun 2008 17:26:55 -0600 Subject: [PATCH] Imported Upstream version 1.2.16rel --- CHANGES | 285 ++++ COMPATABILITY | 203 +++ FAQ | 145 ++ LICENSE | 280 ++++ LICENSE.html | 515 +++++++ Makefile.in | 137 ++ README | 38 + TODO | 14 + config.guess | 973 ++++++++++++ config.h | 34 + config.h.in | 33 + config.sub | 1273 ++++++++++++++++ configure | 2121 ++++++++++++++++++++++++++ configure.in | 100 ++ contrib/MTX.html | 209 +++ contrib/README | 3 + contrib/TapeChanger-MTX-0.71b.tar.gz | Bin 0 -> 6179 bytes contrib/config_sgen_solaris.sh | 153 ++ contrib/mtx-changer | 431 ++++++ contrib/mtx.py | 306 ++++ contrib/mtxctl-0.0.2.tar.gz | Bin 0 -> 5688 bytes contrib/tapechanger.html | 209 +++ contrib/tapeinfo.py | 55 + contrib/tapeload.pl | 118 ++ contrib/tapeunload.pl | 98 ++ du/defs.h | 25 + du/scsi.c | 206 +++ install-sh | 253 +++ loaderinfo.1 | 90 ++ loaderinfo.c | 335 ++++ makedist | 19 + mam2debug.c | 123 ++ mam2debug2.c | 123 ++ mtx.1 | 246 +++ mtx.c | 778 ++++++++++ mtx.doc | 209 +++ mtx.h | 523 +++++++ mtx.spec | 105 ++ mtx.spec.in | 105 ++ mtxl.README.html | 165 ++ mtxl.c | 1316 ++++++++++++++++ mtxl.h | 73 + scsi_aix.c | 144 ++ scsi_freebsd.c | 115 ++ scsi_hpux.c | 123 ++ scsi_linux.c | 397 +++++ scsi_sgi.c | 75 + scsi_sun.c | 141 ++ scsitape.1 | 180 +++ scsitape.c | 851 +++++++++++ sparc-patch1 | 35 + tapeinfo.1 | 72 + tapeinfo.c | 753 +++++++++ vms/000readme | 175 +++ vms/build.com | 34 + vms/defs.h | 42 + vms/descrip.mms | 77 + vms/ldrset.c | 183 +++ vms/ldrset.cld | 9 + vms/ldrutil.mar | 104 ++ vms/scsi.c | 372 +++++ 61 files changed, 16309 insertions(+) create mode 100644 CHANGES create mode 100644 COMPATABILITY create mode 100644 FAQ create mode 100644 LICENSE create mode 100644 LICENSE.html create mode 100644 Makefile.in create mode 100644 README create mode 100644 TODO create mode 100755 config.guess create mode 100644 config.h create mode 100644 config.h.in create mode 100755 config.sub create mode 100755 configure create mode 100755 configure.in create mode 100644 contrib/MTX.html create mode 100644 contrib/README create mode 100644 contrib/TapeChanger-MTX-0.71b.tar.gz create mode 100755 contrib/config_sgen_solaris.sh create mode 100644 contrib/mtx-changer create mode 100644 contrib/mtx.py create mode 100644 contrib/mtxctl-0.0.2.tar.gz create mode 100644 contrib/tapechanger.html create mode 100644 contrib/tapeinfo.py create mode 100644 contrib/tapeload.pl create mode 100644 contrib/tapeunload.pl create mode 100644 du/defs.h create mode 100644 du/scsi.c create mode 100755 install-sh create mode 100644 loaderinfo.1 create mode 100644 loaderinfo.c create mode 100755 makedist create mode 100644 mam2debug.c create mode 100644 mam2debug2.c create mode 100644 mtx.1 create mode 100644 mtx.c create mode 100644 mtx.doc create mode 100644 mtx.h create mode 100644 mtx.spec create mode 100644 mtx.spec.in create mode 100644 mtxl.README.html create mode 100644 mtxl.c create mode 100644 mtxl.h create mode 100644 scsi_aix.c create mode 100644 scsi_freebsd.c create mode 100644 scsi_hpux.c create mode 100644 scsi_linux.c create mode 100644 scsi_sgi.c create mode 100644 scsi_sun.c create mode 100644 scsitape.1 create mode 100644 scsitape.c create mode 100644 sparc-patch1 create mode 100644 tapeinfo.1 create mode 100644 tapeinfo.c create mode 100644 vms/000readme create mode 100644 vms/build.com create mode 100644 vms/defs.h create mode 100644 vms/descrip.mms create mode 100644 vms/ldrset.c create mode 100644 vms/ldrset.cld create mode 100644 vms/ldrutil.mar create mode 100644 vms/scsi.c 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 0000000000000000000000000000000000000000..a42a2e0cd3a29ea9d76fb079bfb9a6ea88466c45 GIT binary patch literal 6179 zcmV+;7~JO{iwFpf?^-+n15{yfWkYCTZf9k3ElpHdEif)OF=8%sVR8WM9BFsk#xY-o zU$K#tf{c0UY$#c_EYS)3Y{?}lCwXzAg2Yln3<3-e9ZlTd-Z^#`052;Jo%X$$&xuIv zH9I@^%%bm1YuIj(`f88_IG#j@Ba371O7Ie`@1{%SARZh z?C)&t?e6XGHL+iFcXxO1v)$E#G5?dsi4(z+N&MD}ZoFVozhn@}c*#7ti=Bqv)XqnM z^rL+GbI%{X)h6Rrt9G;v^Ui0YSB@k&NMeR&>v;<+3M(G<i_XP~cosU5E5ecJM?er##x)^eg06JEIElPLns{L#5*dwS;Y3mlq#xea zR=&mYy_kC-kFTZUHpSWT%eJtr>0=R9k;YQk+~#3(9qd~h6V)24;@#!0Lyfb>Jc~L^as#+5_v%))=$zXl0o9%3vO=d zDc%I@!W)T_`dOwG=1+=Kmh3>@C3g8h!4ZVF4+BLkg~d!`z%fr~DPi@bI$SXdBQXx) zteqRj^PPb&#V8GiNCidLa@ z5`|ZhGX|RMBsk|-II1He;3PS)-{2UJOm%N+!ju$XOJO(qkhp)jE;(eGV z*6L-;#)jwwUg9~v_iu-o{g2MK%8zA5@|V;D{O{KGYK;R6nHB}pFgf@*$arl|Y9;fn z@?&`K^>Mdz{Om;=#MtBTMn+;sk%xHbhwunQ8H-b;T{%z_;Q*^%@L|h7|!~%#|Z2 z^%G)LyPn2TJ%;J(Y5|KM;rhOAxU&tbAp+?0Sgcb=?y-Jg5x(**#VOX8;fAohq+Ow3 zp$Jtj;v@j^5^DsJh%C`WW!n9aXAmVdxC|S=@rKe0LU$hG^jQOj%yBP39bgX71k=@{ zaZ+z6Y|{g^sx81ioJbIMH`2e)u*XXyCUF2j^(1Z12C_q=B)}$LfWRAsv6sN!0HdU# z=T1Pd+Ewl%Y)~UG9oQt&7A4p!Xjgs&UnXH3dm!}7xS0Vs0T~q&pv>!ykEC)sXl(?r zvp=qTR>6IzE_~9x?;+Wnd@b3eX9j1KLO=5`8$MI20Xw zOzdQZX++AVd!ohXX8Y{*uiu=%Y`;?!wtldt7bW>~JjAHE5*0d3! zxE7`8<#6nZk~lek`SSSeRFrkwIGJtfrhpb!*-ClaQ-(H5%|kWfNNazvtH;>?K1}UM zT1j+I_AiPww#uOsIsg~A1^d>Cf>POLYaK;LZi-m-M)P0Fzyf)=Q1ZayAnJ_hiqk>1 z-(Nw#dZVOzbQz^VX`PgPb(Cq!rLNmXLw}e<<1hh}0W?>fPC%eZ!X5}Z=m=w8r3p_D zS}={!rN2v%okGJoFcGrZD1~AOv0f%6@5SGnW3SA~w+$53b6n-@$JlA;1J)288j=YVJ@U8Tlx)Fm28k0zU^F~2 z%`NcbJUDy+qM{!{=XaWEf^H)&BI3ry$8UkKpQ+!CHI7; zVOMrI<^L0DTO>%KN8VbiU_@yGZUCbG*`X%5k{u^blE!xVsK6rG4uIfGn&Ls|D+>!| z8pq`y8b2bc8j45E7MBld76#>zux!wl8N|#r)d3N>Ax;a7r8S8&0HK!;Y!Hc0Pf#HD zjLMOm7)9_Xp7>s(j-C2dk7f}`RFejv+X1T0Cqn)r8fDmmT-+STO|T_C7t25C@HYY1i7G4PyMG;CvikSa)KCmTC{yR2J)!m@`};2l^#7h zP|s-Sn^G?cBMgbEQKIz5eN+}NIG9v^6Mwl$6Xk#cm^PcV4BkHNbe*0HrfTtM zy}L(`XdwRgknQKVO9#+EDfM2Q_r(ttLDKg})J+I(tV2&E^o*aNj+;3L&FNU6b?g^y zY3IuE0*fqaB~@=#NHz&ct>KzS;2e+@?F9GQSIZP`iR@|=*pc+*4F<9_^#MpOhnC7M z3=b*>UOPF=_F5_*QG`$4tf}YtJ!`?Rq0mkEBWoIYQJm-|`jNHyPS%9qvz7)vz9g@T z(7;cdwc1x-t*tRaN!fxh@kXAKGD;v-*c-!b--1q(XkK`+wYHXtCU7bYhC#@j9MWkS zvw$(LB6xf_c~`0dNY|3F0i+7tJ49D)l=|3N$S49nS2`tx38PytmIYyE2xdm50iD4g zfG~4|xP~w5_&h6HT)y$nnKH9H#i8Fbq!;#*l=}4KoLD#v3Xy;}-!yL_sqYCdg>zeGnk&%z?mA)Uxr9|0C)@kyufpgvVdHp$Up_HnRg! zQ^-tsuAhbICU`C>!Hgo;Q7;HR-|$)v-|AyDLr>|!t+6NMB#aVvom<)j0Le~*hEz-r zDiSnG%!q=fVecqYI5>pNS`0r|(31+XAcGr%a|L=BOlNqAz$#^wE%gu@6$+h(Mhj_- zS!zVZiQN7px%0_HgZSIuW;Gv2+-*Lv-vLNw+~s2SdIGX1)%OES zZ~4jW>a_L@qFO{NG)9S*^(hsid1lHW)2ul4*f7Y{b4~a3IFpq)d4*)S7p6c5G@OMS zj!@YNB^i}svI@~P@`DPN8uR{9rSMQMK@C>EMREEOubOXR7Cx%X59eu#Y4F3w1JW3x z9?c*$(hf3yE2xg#^C{3PWNbXWEDI|${S3NSu!_M&WqOtD^#1rNjMvJXy%<_p!76k{ z1&k@8qmhQ<+W0FAl#-QC0A_00*gvBv9A_HWDm^=7KeH4`Fk9U65jFUNQA3ixa4wc#DsDPKSR>_TD?AH0meJ4r9BhXmBucKA4`47V)PWp2nBPh$V0r7H znq?oDT#|V(DXM0m$V_VH6az>^Lme4}ggS-&PnCA&Uk9{d8N+e`F)+o>+TBizW zCNkt`KL?;qO#Oh&2YxhRDA-jv@R3z5M&H#12H-=0gl5W zjBrO{BBQYv#}v=W=*1DfhkQ4}x(%3b9HYa?!U6NE;H@vArECw1mY(WasFFvAbgFlh z*dP}OFg8i!>#S!5j9SNpE1}yuop5n14}I_0siBcVdwJ=^zSI zkeVNy*IRwA2ua%wK@uCAbcDi9*`zrKnL_AVuVYFB`x(t%2`B)nP;53$LNp#I->kJBg;PW4*X}#*}0*jV${G`)kw2ZHiP{X0L-SX3S7u zQc`qmaa{~*`rJ&e7lFCRemx_!+y3W8r`vwnKI;Qsslb^^?a=5O$Ry#=I2;C813nn! z`KqjYb}*bmB%La&P}IX$ut)+}Pr4>Gdmks1&Nb`z&ri=StA{B|FsIP0>;#iEskiUw z0jG{Uj+A-9&`(`F#N;|mudeYR$Id;5+_APas#&kNkmH_0Z!wp$0%3VrAgWSV6Pd%- zAWcMv@QnwP$QWVul8S{@3v}uZ&W7lN20S7z#}tAhOeU6NQ~DO?FxCq~P_eWxg26rA zC03gS=u2uNh?{HaPp}RNT{Y@k&Q7V(373WLVXW7Q9QpPaw?zT5fw zn?86dvqkN=N!-Il07n~PO%R%S9UpwT5NKwmxSarBuy($LzF2N6UgO-rJ>P)L2%~%O zwQm}=-GNxcYXE2AjSydn=5A~It5$PYUju07OKZgq&tR<9(MW_KWdK$Bc;Hw-7?vDA zMtc~%2bEwdQ?Wfb_rnx(X6z*4HsTb{675&jv8KRk0$lx65?sw~(Ey<_dwMfcro^NiT9KNNMLC2%D?H2T0Z4h^h(_ z1?*m9kcSljZ8Tb2Q?New?ojOL$fKR@zEJhY;*k7ZzQcg?HwSb^r*bHoJ9tK0Tdl3# z1-RgivW3dCZP@&a8cOBD-d&IE3S@J&oBal_R=60__AB4 z-TphwT%4Bu@9BvtZHd#9uaz(VP)_7uxBh3r{V(UOL?f~G>ctA~7%jj51#SDy&l>yN z``e8@XxRIVnztIC?tlFWALQcpU2>6lehj1m=Z^nHc+(57!uZ~xuAA$owKf@(q;zEh zdXEaSuftmkc?Tmu#d0Ya&ol|{UyzuZZB1Az_7Z1O8}D{W-CQC!#K!wh_S)kkVNVmV zUEv<;vef1C?)ghwU{AifXjjArNDt%&eW8j6oOXJBnpTLZd8@f;fi<^ZouBr&*RN() z8napIob~#b*yps z;{QM4(*?twjJj(!>WHt&{Q)~e2Gle|;Fg6J7Lx8#%WoxC-$N`uh6gP^a9)?$e5~@<{}Vn+ z(px!;&(f!FvDULN9#EV3R!(=VO8tEL{Et8L`9F8kD_8^hf64nl_O=xN?`&;@1+Yc> ze}DIr{r_itHa^eY+{BBUbaT9V%XgHrcM+^(qzCUN=5HP^cy+*L1B)5sFKS=bzxd+7 zqQtXuPAlT66J5oWfrEimE9Pbbe$Pf%1Xiuns{`a6=xok4YmljoW_=xtSIi^aUj3(Py?%g|8ZRUlH`rC1 z-VVV@KDw{%61-*{)`()3-!5kvrw6^)AiFlAE)mS@u8YIDvqFcSUkz;1P66nSio#!U zpd6hgFxBj)oyUT(RF!IB2RKuH2ddhA$=e+G&@-rJyXMU&bsid!5-)(omAn!b%D!bXFtv2!H}=nMP?B&MiDP=7hM!NPB)23|wNTp)GGvFhPr`~1a$MUPtoq{g9I z5j;V)c&|C!t*iKtqY9w6(1NwWp{`8|05XVQ|akci9X$b;A%fT zx2jvuEutRi`Vdn+g&(?VAk6fBc%^c0)WB7Uei%+TAQw)`W+bLd=8n{Pl#S%<6$GD8!hZv(N(ZioJGy~{%6REcJD=S4MQrfiI^ zFmSctEc8GRRFqJ7-A&FI5!FJL4TUbh-jHw$@lZ( z8JLvPA@36NL%G+x%mB!FS$?$()`QJ`RZ9g!$aLo&{$i?>vYN$&qm_Rj45b!THgWpO zD?79?U9tJZTz?(r;;Yc=r3nLmm5-C)eSglVa|X1Mdv}CNgQ~X={mxvuJ45RXAKrrE zxA|cDNZCmbO!7|8_@GR$MVVglM>D+^aq|Bz)B8b5URc#yL5>$`?f)yu`^o-Wjs3U4 z|L2I$ihRuX#Q&OGTe~X$x3jywwaxMW-R)2Q|DW*L5YPAxLH`;%Z>6qpq5$?pMp%!I z%7+vO8Cp600q!-s+Vm2t4K)Ljj$ISS_==sGNzHIFRKS+`7lj)Xx!Fq!0>!r&cED0| zt}es+>46VoTZ|o0@L47tBnDoYNFyv@)&&8mkelj_bkGxiq0YXqO3POsk!Z@84JR>w@jfUC(sTGq;-v)cciyQd#$!D9{hOe0vn133WP z^{(Y|^>b0Eh6;n0bLD4)d*$exNNRBmuOJ2tfW;ov7^o5Wj(bsX%>!2tNPz|pAYk>G z3Jg{7>T$3K!DTZfs5Bt8IdrWc*sfxTWyVUNz)H?90_~E^2k){0wy@ydF;)Vdy;YD3 zmHRR$r+`2AZ0*9@{z(8WHo5(_dsWgQ7{Td!6+C3001kRN5cSK005Jq B&l&&# literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..de41b7d20aeb7ad9ac63789d4841b17cbeee21ab GIT binary patch literal 5688 zcmV-87RTuyiwFP<*X20?1MNI(bK6Fe`AYnXHpPvk4M~xbE#E1zcavDjs^ZuwS=sEB zt#X0Dkc0&SSUgN|9sT$1#|$0>C{gw~S67Rvq$~n6Jv}}Bo*pdHYbW&&zWAIC9*!PA zdIVnpz~Sha{~bSk$p7j#0(d-rJU)7KboBTL-;N(XKK=q8eO9b=%aYWN0lsk4W$ex7 z>85_zZ94rU5c*@=LiYdrzkU1S$J1AzXO8h`bgcKgasS5;kJtA9@X^D^U%=?|pi^!C z;rl-tj)vnCn0j%NLacmcCu(R}^!V@uY}ZvTFkJ?Y?>SenP(hXqExmKeJj{F-B0EkL zEHXd!B42?Ud+${u!ypKkL+k%~m2KP9wEW9I!}RKN%z^TMi~c`4K0X{9`9FL-dWdh2 zSpT>3|M}a_gM%!I52mqFOFLEv6E8SeddVCHQ)>spt3J$CtacMvtiVoFwTMzkLs-~X z3T#Ln{~*1B#ECt8J`H1V!XS-9-}izU*a3pDvo!QW+g0%pAf0;&;D5`xox&OY46eXS zjUI8s6Ke;@AUERtaNw%<2hQ9MW-11xf0`xm^(`-PI4BRSlYpr7pNqGaLNzt zN%&qNf{TS6xP%3+!*=L*=-R0QFZQlssl3>wQTvREXf{S0d(RZ=vurWJ84)H9LJGuo zkWBFbs!r66X##uRPz@mtCt(V9yh!%@fPhNFSUdKCkSLy}cBCNj!!+S}eR~CAmLi?K zK;A`mpnRC1dUhcU7^Sa4+0I;MUZk*>&)F})^${4)xNnF&Q3&008N3Y>Zw6D`j7gTJ z3>N=hdcIHHc3l<4NDAfd(&`bC7r357w9>mV7yz%<2Hs*u61Q#mnb`lNnEms=;-0hgoKlNVUV1FkWVPUoWwtQYhghQP3!K1N*zRY zseHeWf;G#C@6W)ogWVMARhfS2rE|DI$-THjaDqm{i{$r&N@-zCQKm-}Mh3?;MKe#X z0M0qLafSGTWoSHx&`D9vk47i3LMA4iuuuSLnNPjjtWS0vH&#^ukXiS zo_`IAQV3w4rqRj4!E(7A&Tvg(JUej0XyvPEdN7Op;XGaVJhQ7(+w&8dHVhO@vlwB; zAZq6fTrY7@x)y*ef((#ENLp{=`KeIC4pyY|W};D|IGn}yf+RFl31WGcGDQ+iHDM=j zN*Z#4T!$}KVTS9U;{#M52c&Q9EK$tS2u202QFaK&cacCsSI2&@kg}w`!&tCa1Jo@( zT(ADB-h2MU4<{2(4Uyuor+IyWI)i!TpU6$jjb_p)wmozc0j9paP^5RCiari9nk_P3 zhwop$_~GowU(dh&?)fSF{oC_1`r{RB8nk02sD4TNck3pr!6im`$~Igm%{JlP&{EuSS&OlbTF^xJEIW=-E_vaoIdBaLd>HG+#PsN9i#Z5e z4JemZ2C2!&%9wSQ)u}cdjZ>jGqWNO0%uoB*GrV9b0TgjUSQ~+Jyy4Lu&W}wQwD|g@>_Q7_Tk&Q_$Cm zvJ^E^tY+#uf)6LJM}z;q{OUDaK4W+6270=yxMz7s>4&d%P>a;pJ=VG)s8{5^6_CW6 zs{!K21V`T2!JI9kFisCnuj!uxoQKLAJEnDr<_!VOdzrn-5zeQqcH1$;o7*{amEF`P z7j@lvC67mtR*batLi<3VT@-7#*aMA6vard!T9pz2U6T`-t4_X zi)hbO-scUG_08p1@SHu+H*fa42pqX5fu07BzX@A-t#`sa&$mjP*Gmw@I@Jx+fN|fn zk4r&Sy&Jx#7kVEM?q1K^r*1o7VjNTx3)G#cdM9ysr2-^)f)r64tL0%AZY+{LV2@R+ zhQ7c-cO@kM@QSu+j{jGrMVyeeoX(P!y&sa$2@rCB6n0hQrJzSMu_rjyk^zexitpI~ zZHN$VFnqy2A6WBM)(RuIu{t-_P0q^sYvS4|#vp+yCTpU!leOdLmUE&dT`p|nO2z9w8Cw6JC`kL1aG*%xtdPo?GQZaEzNi&5>CUy)1 z47xlu1$Izf7))F^G#%V*fr2qudM+++JhCDigAf^J-X)}|FqA`Yfx@0^%c3AhM?&0n zh%XcjkjyFkz<+W?;d@B85e%q%spF7-*I}T?o}fRi!MrlRHbK(e1z3T#Mul;vP^J7T zpP_hbHQ`1m*l{?WA|2OX*{+*sO9s9@Q9hhrqmU2GLxsO8V9SWsAy?iu0PkLqfq5m? zznFy|zm>=61KxDE1^2zSFg1w!g@(B;xO0UOM=~$L(NEs~Y63Slq_V`M#nRLF~{{Ci9wBes}fM^BM^E_xD#+W^+|+5)hM zKtY{+5p~Jl-ZZd!EoCHxLD#EOhVQGH%;7L#W zeVef>t$YjQenxflmQ7e0H*wmm&YEH>No7ql-E7oNO>qw|r{Xwk@2{c`w8Oow3xt;F z+yWzBg`)$NPgY%PbKkDF?nLwjdY~H-yRQ=4fLbm|P4BDftFcEDxBObl-41jMSt=9dZ5qCP!Qy1dgCb=Fs>Uu&(zJsP zFeS9pcqLJD`80&1BR)ysOcDhqwbzoB=gM*KtFi4^dn~r4Edp-c9!!3P6 zS`DZ<37dxDUnnsWQU2Vu7ZkN)6hEOzieo4oOE7H;+*Hg*QyBe3u}Ue~5*OLv;XPcK z2}R3I>gSv9_^-jyQ|SH@Mp^XsXWXl|XH$vwu*aYN0e||R?rk8x`R(QTPiHTF9K3q| z@}lQGMNm!soG_Srvn)2@)9!Cb4u$UdCQZr|Xj}aOOI{HpRc?494(Q2@dkhH1i+e zq*fG%4pbthfC+Y2rnZz^0g@jdnc3F_l7~fXZXQ-6HtA9d&hu^*qyr~-reToMk%&dP zZaPiR&kDR?8d{xdUby<(V2T0Ke&vm1jB$QR;cQA4!|z|Bz@jC%q`b!2?gA3pKiq-c zSZTTDs)dOpDCi)ZiUD|#r~$4Z!0q~t57qdP*D@(Em?%DrgR}Fqf5>@3gz!)w&`}^Q z4{atJBvR8UP8LfVRiC!e2_C|lAjx29ujufWlQNpk$p4fhfmju`%?s!tcS!Us2Zl(F zA)3PIj$jEuv~P9LkQsVFN50;4#km8` z9fzx;CmlzXyr+C^O<32^s?OJB99Uqg837FKwvw5Or09HhO;}3LYblAE$do&Otkjq@ zmi5OCG9xJ5Dm9lS-YQ{tFZNm=w;hL-N8OXj%0utuvr^}R)oM%Ku!trsYtyf@T_@+e zs`Do?YKij-o!qH=L}h1PSNO=kjnK6h#YVk0&lXmp$ToNM@vju7=wh1u?3M?hLRwFn z&|f-l!l$5x;LsaXhGERl3&F{sLzj*fe6`?n71G1(<(lp#@hOIg@KnWzerw*!J8s9v zIVV@gZRX?d>G}D~^AmVRg%g3OeaW5sv9jG2(BZ&?H^E+{;<+7hU5ClSCyI@N%F_Vo zWK!YAiXr@E|CWVpD<_4d6yxudlf0|c2Z)@k5wej$8<1R^yQZ4~Xrj$*gd3k`aR3yp zZRaoFhFuFCNo)oOY{#^P#X zq#0=<3NcKrpO;q!D)N)ukOeT0K`?OYaNL82DNr#5;dQT?>9Q6k(6gsc>w~MFw)pMq zJ~=qEO1K zF9?iqn>Q^gRd$n#UPPY4S3!P!s6F(=iPe(IDQhV&*#wjJDl7{>7t_+|6o#dET(>Ne zo{{b)!%`YD;-lEIUFmen2PRval}<++m9=5_vnoRBidor=(RTKs)2Srmwloh#-y5oz zD6%tU^U%1|O<1l^U$YRD2)dmY#6(C&@1r#PX1y*-Tq1nbD(X6CpF}u+tZO-`0+Uih z@k%O?siu?u=;vylr`-`KDyaY&VYK-w?CWi&N)9R)Muia!#v({dNj#L)_5TNrT++ry z={C5tgpsvY3&9@1Jrr(LJ66@LB)ypy_tv`WidXx`F!K!O3ALq&l}stYq%hjM8( z*Kq6V^@&QCN(H)d>|?z1qOPn{cflD8^qxlW`|nVCL3p*kc}0o#CDY2=#xL0efAF8; zd*_<O*dM8z^Q$)+H(*pq)!eQAWWc%ttfgao5mVesZOsKsFp8%vLupH!{NFzzT=r5@sMHkGJ$8fsKG z?`-`Xk1^P;P&MzmeXVN2Hsx|vO}mDY)e1c}_8aMbCDPo7lQ$MPFw{E_=C&6#Lg zTR<}cO|Wy!s=(!Ow!m^`^aR=&gj1 zfAIZpegFUb^xJ1YpMIt}?)d)4(ecsa;`<*DAJg|g9v(kx@BjZ1&AP}GymV3+k6?U! z^6>a%JcjR|UGOzp%aW6BdrGw@axi|3<~NJkd70aCTNp)It`ql74T}xtnyam^v_7z` zGxGa?{N?$sE7z4R>w-&NPP7Na&X6DKoHN4j(uWATU`v*k;uCzBf2pFchgm3T9%&=F zHp>$ye21_GkQ_z)iw|4)UZ0%6$60{xzz`8cdH!eS#pFHEHxT}7IHEXkhH)VhU`R{g znjbfm6nQ|GYWeO$pHKPO`K1q}0O=S?!qcgZ@}~o|yuKZHXslPTr$2nK-?#W)wJu8{ ztobg4QgSO}&dw%zgg>FzLl@VROeH7QcpolkVO%@{SDsw@O)^JHl!(exx$1ef3mk@E}td)U&#;A z!~3{e+Dh6HA{x1cn&J2y4GAKbt`Qg^sgWC6hx@!@bLWM(*CeE?ekJd%VQP$E2;7o# z)5J;W*V3A8C(ZbS0QzWzhX`;k_>yaxvYO&vy|74@wxIs!vR0MZB@f;J%%a;`N8-!A>QzOYth?SUHj4Q=OyOqNu&Ye=IjS%Q^ zKkbwLngY2TrSAX`I37@tBF~=E?4_v*k%Y+NT+sz}+%R=*Q|%Ql9Dx%8UAoQJH8qF{ ze`F%WIZ+lR@W<%SQ&CD>h>A)Sm~2H+bd~6cB;S4$Y8|dQxXr)G1c*()11Gl4n!;(P=7*E>z8v!iC%yTT7 zi%>W&;E1ZYkzFcaVg}}6NCPTYay+J#S!%bq@ZL2tL17Y6F^`_8Y%O6xH1#A8uNZEH zJhQ<2m%f)v#By_d^u>1y)>x!ucK)P;RwB7pJVTn;6hlfT`K0_AnSucP)u|4~ovuF3 zfd5$Kb+OJwzaKoZEIWG+SazVwXJP!Yg^me*0#2_t!-^!wzjpc eZEb5?+uGK)wzaKoZEIWG>h0f3!UpF6cmM!$NeN^C literal 0 HcmV?d00001 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); + } +} -- 2.47.2