/****************************************************************************
* *
* COPYRIGHT (c) 1990 - 2004 *
* This Software Provided *
* By *
* Robin's Nest Software Inc. *
* *
* Permission to use, copy, modify, distribute and sell this software and *
* its documentation for any purpose and without fee is hereby granted, *
* provided that the above copyright notice appear in all copies and that *
* both that copyright notice and this permission notice appear in the *
* supporting documentation, and that the name of the author not be used *
* in advertising or publicity pertaining to distribution of the software *
* without specific, written prior permission. *
* *
* THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, *
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN *
* NO EVENT SHALL HE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL *
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR *
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS *
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF *
* THIS SOFTWARE. *
* *
****************************************************************************/
/*
* Module: dt.c
* Author: Robin T. Miller
*
* Description:
* Main line code for generic data test program 'dt'.
*/
#include "dt.h"
#include <ctype.h>
#include <fcntl.h>
#include <math.h>
#include <signal.h>
#if !defined(_QNX_SOURCE)
# if !defined(sun)
# include <sys/ioctl.h>
# endif /* !defined(sun) */
# include <sys/file.h>
# include <sys/param.h>
# if defined(sun) || defined(_OSF_SOURCE)
# include <sys/mman.h>
# endif /* defined(sun) || defined(_OSF_SOURCE) */
#endif /* !defined(_QNX_SOURCE) */
#include <sys/wait.h>
#if defined(DEC)
# include <sys/utsname.h>
#endif /* defined(DEC) */
#if defined(HP_UX)
# include <sys/scsi.h>
# if !defined(SCSI_MAX_Q_DEPTH)
# define SCSI_MAX_Q_DEPTH 255
# endif
#endif /* defined(HP_UX) */
/*
* Modification History:
*
* October 21st, 2004 by Robin Miller.
* Enable file system alignments by default for random I/O,
* since variable sizes and offsets cause false data corruptions.
* When logic is added to track previously written blocks, this
* can be reverted.
*
* July 7th, 2004 by Robin Miller.
* For HP-UX, add option qdepth=N" to set the queue depth.
*
* June 24th, 2004 by Robin Miller.
* Handle errors from ctime_r() - two flavors are around!
* Added "enable/disable=fsalign" option to control behaviour
* of aligning FS offsets and variable record sizes to device size.
* This option is off by default so random non-device sized values
* will now be generated (useful for file system testing). This
* option can be enabled to return to previous behaviour for FS's.
*
* June 22nd, 2004 by Robin Miller.
* Added support for triggers on corruption.
*
* February 27th, 2004 by Robin Miller.
* Switch ctime() to ctime_r() in preparation for threads.
*
* February 24th, 2004 by Robin Miller.
* Allow parsing of aios=0, to ease supporting of OS's which
* don't support POSIX AIO. Makes script writing a little simpler.
*
* December 6th, 2003 by Robin Miller.
* Conditionalize to exclude tty code.
*
* November 17th, 2003 by Robin Miller.
* Breakup output to stdout or stderr, rather than writing
* all output to stderr. If output file is stdout ('-') or a log
* file is specified, then all output reverts to stderr.
* Parse AIO options, but then emit warning if not supported.
* This allows portable scripts to be written without parameterizing
* exlusion of AIO if a platform (OS) does not support it.
*
* October 31st, 2003 by Robin Miller.
* In terminate(), don't attempt flush if device or file is
* not open when we receive the alarm signal.
*
* September 27th, 2003 by Robin Miller.
* Added support for AIX.
*
* June 9th, 2003 by Robin Miller.
* When terminating as result of alarm, flush output data if we
* were writing a file. Thanks to Hank Jakiela for reporting this.
*
* March 20th, 2003 by Robin Miller.
* Fix bug or'ing O_LARGEFILE into open flags (thanks Kris Corwin!).
*
* March 15th, 2003 by Robin Miller.
* Added "prefix=" option to support data pattern prefix string.
*
* March 14th, 2003 by Robin Miller.
* Added "slice=" option to support testing an individual slice.
*
* February 23rd, 2002 by Robin Miller.
* Make porting changes for HP-UX IA64.
*
* May 31st, 2001 by Robin Miller.
* Don't allow different data patterns with multiple processes,
* unless writing to a regular file (each process has own filename).
*
* April 13th, 2001 by Robin Miller.
* Added "capacity=max" for Tru64 Unix, this will force using the
* driver returned media capacity. Prevents lseek/read algorithm when
* using random I/O too! Note: Will inhibit disk driver EOM testing.
*
* February 24th, 2001 by Robin Miller.
* Add conditionalization for QNX RTP (Neutrino).
*
* February 21st, 2001 by Robin Miller.
* Disable EOF/EOM exit status by default. This causes trouble for
* normal testers. Use "enable=eof" to reenable previous behaviour.
*
* February 6th, 2001 by Robin Miller.
* If input file and multiple slices, allow cycling through data
* patterns, since the file is expected to have been written with unique
* patterns in each slice region. Minor update to deleting files too.
*
* February 3rd, 2001 by Robin Miller.
* Lift restriction of bs= or min= sizes being smaller than
* sizeof(u_int32) for IOT pattern or lbdata option. Although this
* isn't *really* correct, enforcing this breaks numerious scripts.
* Although smaller works, it's only by luck and the fact we have
* pad bytes at EOB, which prvented data corruption in the past!
*
* January 28th, 2001 by Robin Miller.
* Added "slices=value" and "enable/disable=unique" options.
* The slices option carves up a disk with each process exercising a
* differnent range of blocks. The unique pattern option sets up a
* unique pattern for each process started for slices & regular files.
*
* January 26th, 2001 by Robin Miller.
* Added report_record() to the report record information.
* Added "iodir={forward,reverse}" option to all reverse I/O
* to rotating media. Add validation checks both before and after
* a device is open (mostly random/reverse I/O checks).
*
* January 24th, 2001 by Robin Miller.
* Added "dsize=value" to set the device block size.
* Added "incr=var" option for variable request sizes.
* Removed some dead code (#if 0 stuff), to cleanup a bit.
* Updates to allow the IOT pattern to use non-modulo 512 byte
* sizes. The logic is: 1) user defined "lbs=value", 2) device block
* size (Tru64 Unix), or 3) default to 512 byte block (original default).
* This change was initiated by Windows/NT IOT disc using 2KB sector size.
*
* January 18th, 2001 by Robin Miller.
* When requesting multiple volumes, ensure the tape device gets
* closed prior to retries, if the rewind operation fails. Otherwise,
* the next tape open fails with EBUSY (exclusive open device).
*
* January 14th, 2001 by Robin Miller.
* Added support for multiple volumes option.
*
* December 30th, 2000 by Robin Miller.
* Make changes to build using MKS/NuTCracker product.
*
* November 19th, 2000 by Robin Miller.
* Add missing enable/disable=fsync goto label statements.
*
* November 10th, 2000 by Robin Miller.
* Added sanity check to warn user about unpredictable results
* when writing to a disk with multiple procs and multiple passes.
*
* November 8th, 2000 by Robin Miller.
* Added "disable=cerrors" to disable device close errors.
* [ Note: This is really a workaround for the Linux tape driver. ]
* In HandleMultiVolume(), if closing the device fails, then
* return that failure to abort the test. With tapes, this means
* flushing the buffered data or writing filemarks has failed.
*
* October 2nd, 2000 by Robin Miller.
* Enhanced report_error() to display errno value (same as Scu).
*
* August 22nd, 2000 by Robin Miller.
* Added boolean flags to track user set min, max, & incr values.
*
* July 14th, 2000 by Robin Miller.
* Added fsync_flag to control sync'ing data to disk files.
*
* May 8th, 2000 by Robin Miller.
* Adding parsing of "version" option, which is used to only
* display the version string, dtversion() in file dtusage.c
*
* May 5th, 2000 by Robin Miller.
* Set proper exit code in terminate(), when exiting due to
* an alarm, i.e., runtime= option. Previously, the end of file
* exit status was being ignored and we exited with failure status.
*
* April 25th, 2000 by Robin Miller.
* Fix problem of not breaking out of test loop if the error
* limit has been reached on previous pass.
*
* March 28th, 2000 by Robin Miller.
* Modify "position=value" to accept a large numeric value,
* otherwise we were limited to 32 bit value on non-64bit systems.
* Do the same thing for "step=value" for 64-bit file offsets.
* When using the "runtime=" option, when the alarm expires and
* we call terminate() to exit, look at the exit_status variable
* instead of error_count for errors, so we don't miss errors!
*
* March 27th, 2000 by Robin Miller.
* o Fixed parsing of a couple flags, including "flags=direct".
* o Added "capacity=value" option to set the drive capacity.
*
* March 20th, 2000 by Robin Miller.
* Don't allow a pass limit of zero. This also avoids a
* core dump when preparing statistics, since the active device
* never got setup.
*
* March 2nd, 2000 by Robin Miller.
* In HandleMultiVolume(), reset the exit status to SUCCESS,
* since this got set to END_OF_FILE earlier. Failure to do this
* means we exit with an END_OF_FILE (254) status (no good :-).
*
* February 17th, 2000 by Robin Miller.
* Adding better support for multi-volume tape testing.
*
* January 17th, 2000 by Robin Miller.
* In copy mode, is input file is stdin, don't do verify.
* Enable random I/O, for ralign, rlimit, or rseed options.
*
* January 14th, 2000 by Robin Miller.
* Don't delete the output file unless we're in test mode.
*
* January 6th, 2000 by Robin Miller.
* Added support for multi-volume media (lot's of changes).
*
* January 1st, 2000 by Robin Miller.
* Added read after write support, "enable=raw" option.
*
* December 31st, 1999 by Robin Miller.
* Added "rseed=" option, so user can specify the random seed.
* Modify reopen_file logic to use O_RDWR if skip_count exists.
*
* November 11th, 1999 by Robin Miller.
* Added logging of diagnostic information to event logger.
*
* November 10th. 1999 by Robin Miller.
* Fixed parsing of "dispose=delete".
*
* November 9th, 1999 by Robin Miller.
* Modify logic associated with making stderr stream buffered.
*
* August 26th, 1999 by Robin Miller.
* If during the write pass no data was transferred, don't do
* the read pass since obviously this will result in a compare error.
*
* August 7th, 1999 by Robin Miller.
* Allow enabling open w/O_LARGEFILE via "flags=large", incase
* _FILE_OFFSET_BITS=64 does _not_ enable this by default.
*
* July 22nd, 1999 by Robin Miller.
* Added support for IOT (DJ's) test pattern.
*
* July 19th, 1999 by Robin Miller.
* Add flag to control log file header normally displayed.
*
* June 28, 1999 by Robin Miller.
* For 32-bit systems, change count and limit variables from
* u_long to double, since u_long limits us to 4GB (too small).
*
* May 27, 1999 by Robin Miller.
* Merge in Goerge Bittner's changes for micro-second delays.
* Conditionalized all common/output file open flags.
*
* April 8, 1999 by Robin Miller.
* Merge in Jeff Detjen's changes for table()/sysinfo timing.
*
* April 7, 1999 by Robin Miller.
* On DEC systems, obtain tick/second via sysconf(_SC_CLK_TCK).
*
* December 21, 1998 by Robin Miller.
* Add pasing of "enable=resets", which enables tape repositioning
* logic when bus/device resets are detected by DUNIX EEI status codes.
*
* December 19, 1998 by Robin Miller.
* - Disable setbuf() for stderr... screws up log files.
* - Write cmd line and version to log file for reference.
*
* December 16, 1998 by Robin Miller.
* Merge in changes made by George Bittner:
* - set random_seed using times() instead of time(0).
* - remove DLM_VALB, per Dan Christians' recommendation.
*
* October 29, 1998 by Robin Miller.
* Implement a random I/O data limit, instead of using the normal
* data limit variable (not good to dual purpose this value).
*
* October 22, 1998 by Robin Miller. (Happy Birthday Mom!)
* When specifying a runtime, ensure we break out of the
* passes loop if we exceed the error limit. Previously, we'd
* loop (possibly with error) for the duration of runtime :-)
*
* June 16, 1998 by Robin Miller.
* Add common open "flags={excl,ndelay,nonblock,rsync}".
* Add output (write) open "oflags={defer,dsync,trunc}".
*
* April 29, 1998 by Robin Miller.
* Add support for an alternate device directory.
*
* April 7, 1998 by Robin Miller.
* Setup the pattern as a pattern string, so non-modulo
* sizeof(pattern) read counts will data compare properly.
*
* March 20, 1998 by Robin Miller.
* When terminating, call close function before reporting stats,
* since in the case of AIO the I/O rundown updates statistics
* with outstanding requests, when we're aborted by a signal.
*
* May 14, 1997 by Robin Miller.
* In report_error(), set exit value to FAILURE instead of errno
* since this makes detecting errors easier in scripts.
*
* May 13, 1997 by Robin Miller.
* If a "skip=" count is specified with an output file "of=",
* open the file for R/W access since skips are accompished via
* read()'s of record size.
*
* March 27, 1997 by Ali Eghlima.
* Added cluster support, so more than one process or one system
* can access a resource. dlm are being used to synchronize all
* access.
*
* March 7, 1997 by Robin Miller.
* If a copy or verify operation is selected, and a data limit
* is specified, then double the data limit to account for double
* the I/O's. Previously, only half of the desired data limit was
* copied and/or verified.
*
* February 3, 1997 by Robin Miller.
* Check status from closing file descriptor, and if it's not
* SUCCESS, use this for the exit status. For tapes, deferred
* writes and failure to write file marks occur at close time.
*
* March 30, 1996 by Robin Miller.
* When writing, now default to an infinite record limit, so
* writes stop when EOF is reached (disks and tapes only).
*
* February 29, 1996 by Robin Miller.
* Added callouts to setup data limit for disk random I/O.
*
* February 28, 1996 by Robin Miller.
* Added support for copying and verifying device/files.
*
* February 17, 1996 by Robin Miller.
* Add "enable=spad" option to control checking of pad bytes
* when long reads of short records is performed, since some
* controllers corrupt data bytes following the short record.
* [ NOTE: At DEC, this action was deemed acceptable, since
* the requester stated the driver could trash a larger buf. ]
*
* December 6, 1995 by Robin Miller.
* Add "ttymin=value" option to give user control over tty VMIN.
*
* November 19, 1995 by Robin Miller.
* When the device type is a disk, default min_size & incr_count to
* BLOCK_SIZE (512) instead of 1 (which is non-modulo for disks).
*
* November 18, 1995 by Robin Miller.
* Removing unaligned data access (ade) test code (code cleanup).
*
* July 24, 1995 by Robin Miller.
* Changed O_NDELAY to O_NONBLOCK which is defined by POSIX. They are
* supposed to do the same thing, but they _do_ have different values.
* [ NOTE: This is only used for serial line modem testing. ]
*
* July 22, 1995 by Robin Miller.
* Add additional checking on "pattern=string" to avoid ambiguity
* with 4-byte hex data pattern strings... which is misleading).
*
* July 15, 1995 by Robin Miller.
* Add "oncerr=action" option to control child process
* error processing.
*
* July 7, 1995 by Robin Miller.
* Add "dlimit=value" option to override default dump limit.
*
* July 6, 1995 by Robin Miller.
* Enable dumping of data bytes on compare errors by default,
* since this program is used mainly as a diagnostic tool, and
* we now limit the number of bytes dumped.
*
* November 5, 1994 by Robin Miller.
* Don't set SIGCHLD signal to SIG_IGN (set to SIG_DFL) or else
* waitpid() won't detect any child processes (OSF R1.3 and QNX).
*
* September 23, 1994 by Robin Miller.
* Make changes necessary to build on QNX 4.21 release. This
* required changing O_DSYNCH to O_DSYNC, and O_FSYNCH to O_SYNC.
*
* November 11, 1993 by Robin Miller.
* Removed code which was inadvertantly disabling data compares
* when there was no output file and verify was disabled. This
* caused data comparisions to be disabled for terminal devices,
* now that the code has been rewritten/restructured (shit!!!).
*
* October 31, 1993 by Robin Miller.
* Enhance device type setup and honor user specified device type.
*
* October 28, 1993 by Robin Miller.
* Correct problem during loopback testing, where the device info
* structure fd's were not marked closed due to two places where
* close() was called directly (oops, missed a couple changes).
* After forking, display which process is the reader and writer.
*
* October 11, 1993 by Robin Miller.
* Conditionalize SunOS code (4.1.2 used) to avoid using strerror()
* function for obtaining error messages (return to perror() method).
*
* October 7, 1993 by Robin Miller.
* Add appropriate casting to block size checking, use ssize_t to
* prevent negative block sizes which don't work with read & write
* system calls (even though the count arg is declared as size_t).
*
* September 16, 1993 by Robin Miller.
* Properly report write pass count when read verify is disabled.
* Only update data pattern when writing files or loopback enabled
* when multiple passes specified (pattern cannot change for reads).
* Support "enable=loop" to enable loopback operation, rather than
* requiring both the same input & output devices (need only one).
* Added "oflags={append,sync}" for DEC OSF/1 systems (write opts).
*
* September 15, 1993 by Robin Miller.
* Added additional check for FIFO's prior to opening the device
* since O_NONBLOCK flag must be set to keep open() from blocking.
*
* September 8, 1993 by Robin Miller.
* Moved generic test functions into file dtgen.c.
*
* September 7, 1993 by Robin Miller.
* Moved tty specific code into dttty.c. This code is dispatched
* to automatically after being setup in the device info structure.
*
* September 4, 1993 by Robin Miller.
* Lots of restructuring & code cleanup. Nearly all testing is
* now appropriately parameterized (helps speed & new tests).
*
* September 3, 1993 by Robin Miller.
* Dispatch to test functions via function lookup table in
* preparation for device specific tests.
*
* September 1, 1993 by Robin Miller.
* Added "min=value", "max=value", and "incr=value" options for
* testing variable length records (mainly for tape devices).
*
* August 31, 1993 by Robin Miller.
* Added "align=rotate" option to rotate the starting data buffer
* address through sizeof(ptr) to force unaligned buffer accesses.
* This feature tests special handling of unaligned DMA addresses.
* Added "pattern=incr" option to use incrementing data pattern.
*
* August 27, 1993 by Robin MIller.
* Added support for DEC OSF/1 POSIX Asynchronous I/O (AIO).
*
* August 20, 1993 by Robin Miller.
* Added handling of reading & writing multiple tape files.
*
* August 18, 1993 by Robin Miller.
* Added "step=" option to specify a step offset for seeks.
* This is the number of bytes stepped after an I/O request.
*
* August 17, 1993 by Robin Miller.
* Added reporting of start & end times, time of errors, and
* added "runtime=" option to specify how long to run.
*
* August 13, 1993 by Robin Miller.
* Added alternate data patterns to use with multiple passes.
*
* August 5, 1993 by Robin Miller.
* Added "files=value" option for processing tape files.
*
* August 3, 1993 by Robin Miller.
* Added "procs=value" option to create multiple processes.
*
* September 19, 1992 by Robin Miller.
* Initialize flush_flag to TRUE so flushing of tty I/O queues
* gets done by default to discard any left over junk.
*
* September 18, 1992 by Robin Miller.
* Added calls to save & restore terminal characteristics.
*
* September 17, 1992 by Robin Miller.
* Added parsing for "pattern=string" to allow ASCII string.
*
* September 12, 1992 by Robin Miller.
* Ensure the stderr stream is fully buffered, so the total time
* is not affected by flushing previous statistics.
*
* September 11, 1992 by Robin Miller.
* Added "oflags={cache,dsync,fsync,temp}" options to allow testing
* the affect of these open flags on QNX systems.
*
* September 10, 1992 by Robin Miller.
* Added parsing of MARK & SPACE parity for QNX Operating System.
*
* September 9, 1992 by Robin Miller.
* Allow loopback to same tty port (avoid exclusive open).
*
* September 5, 1992 by Robin Miller.
* Initial port to QNX 4.1 Operating System.
*
* September 3, 1992 by Robin Miller.
* Make changes necessary to remove BSD dependencies.
*
* August 19, 1992 by Robin Miller.
* Added support for testing tty modem control.
*
* May 25, 1992 by Robin Miller.
* Don't set exclusive open mode for terminal device if debug is
* enabled, so "stty -a" can be done on terminal under test.
*
* May 22, 1992 by Robin Miller.
* Control / force kernel address data exception via flag.
* Added option "enable=ade" (address data exception).
*
* April 24, 1992 by Robin Miller.
* Inform user that either a record count or a data limit must be
* specified. Added flags to determine if we are reading stdin or
* writing to stdout streams.
*
* March 11, 1992 by Robin Miller.
* Changes necessary for port to 64-bit Alpha architecture.
*
* October 16, 1990 by Robin Miller.
* Added "align=offset" option to align memory buffers at a specific
* offset. The default is page aligned.
*
* October 9, 1990 by Robin Miller.
* Added "hz=ticks" option to allow the ticks per second value to
* be specified. On VAX systems, this is consistent (100), but on
* MIPS systems it various and is setup at boot time in the machine
* dependent code.
*
* August 21, 1990 by Robin Miller.
* Changed exit status so scripts can detect and handle errors
* based on the exit code. If not success, fatal error, or end
* of file/tape, the exit code is the error number (errno).
*
* August 21, 1990 by Robin Miller.
* Added "position=offset" option to position to specified offset
* before starting test.
*
* August 16, 1990 by Robin Miller.
* Added "enable=coredump" option so if exiting with error status,
* we'll generate a core dump for analysis purposes.
*
* August 8, 1990 by Robin Miller.
* Changed malloc() to valloc() to align buffer on page boundry.
* On some archetectures, this results on better performance to
* raw devices since the DMA is done directly to the users' buffer.
*
* August 2, 1990 by Robin Miller.
* Added "pf=file" to specify a pattern file to use. In this
* case, the contents of the specified file are used for the
* data pattern.
*
* April 11, 1990 by Robin Miller.
* Added "enable=mmap" option to enable memory mapped I/O.
*
* March 22, 1990 by Robin Miller.
* Added "dispose={delete|keep}" option to control deleting the
* output file at the end of testing. "delete" is the default
* action, to avoid leaving test files around.
*
* Novemeber 9, 1989 by Robin Miller.
* Add additional delay commands (cdelay=, rdelay=, wdelay=) to
* help with testing and debugging various devices. These aren't
* normally enabled and don't appear in the help section.
*
* Novemeber 6, 1989 by Robin Miller.
* Allow start delay (sdelay=) and end delay (edelay=) to be used
* with any device, not just terminals. The parallel output device
* needs an end delay to allow the parallel input device to reopen
* during multiple passes (can't disable cycle request).
*
* November 3, 1989 by Robin Miller.
* Change open mode for output files from read/write (O_RDWR) to
* write only (O_WRONLY) so write only devices can be tested.
*
* October 16, 1989 by Robin Miller.
* Made flushing of input queue before testing optional via
* the "enable=flush" command. This caused a timing problem
* where occasionally the writer started before the reader.
*
* October 4, 1989 by Robin Miller.
* Added fsync() system call to force in-core buffers to disk
* before reporting statistics after writing the file.
*
* September 27, 1989 by Robin Miller.
* Display statistics at end of each read/write pass by default.
* Added enable/disable=pstats to control end of pass statistics.
* Added enable/disable=compare to control data comparisions.
*
* September 18, 1989 by Robin Miller.
* Allow both an input and an output device to be specified.
* This was done mainly for terminal devices so a child could
* be forked to allow proper synchronization.
*
* July 25, 1989 by Robin Miller.
* Added support for testing terminal ports. New options are:
* speed=, timeout=, parity=, flow=, sdelay=, edelay=
*
* January 25, 1989 by Robin Miller.
* Added reporting of pass statistics if debugging is enabled
* so write/read elapsed times can be determined.
*
* December 8, 1988 by Robin Miller.
* Added catching of software terminate signal (SIGTERM) so the
* final statistics will be displayed before exiting.
*/
#if defined(ultrix) || defined(sun)
extern int sys_nerr;
extern char *sys_errlist[];
#endif /* defined(sun) */
/*
* Forward References:
*/
static u_long number(int base); /* Numeric conversion function. */
static large_t large_number(int base); /* Large numeric conversions. */
void parse_args(int argc, char **argv);
static time_t time_value(void); /* Time conversion function. */
/*
* Variable Declarations:
*/
#if !defined(AIO)
int aio_bufs = 0; /* Normally declared in dtaio.c, but for parsing. */
#endif /* !defined(AIO) */
#if defined(MUNSA)
bool munsa_flag = FALSE; /* if TRUE enable MUNSA features*/
dlm_lock_mode_t munsa_lock_type = DLM_NLMODE; /* default munsa lock type*/
dlm_lock_mode_t input_munsa_lock_type = DLM_PRMODE; /* lock for input file*/
dlm_lock_mode_t output_munsa_lock_type = DLM_PWMODE; /* lock for output file*/
char *resnam;
int resnlen, i;
dlm_lkid_t lkid;
dlm_status_t l_stat;
dlm_nsp_t nsp;
#endif /* defined(MUNSA) */
u_int32 pattern = DEFAULT_PATTERN; /* Default data pattern. */
bool user_pattern = FALSE; /* Flags user specified pattern */
bool unique_pattern = TRUE; /* Unique pattern per process. */
char *pattern_string; /* Pointer to pattern string. */
char *prefix_string; /* User defined prefix string. */
bool aio_flag = FALSE; /* Asynchronous I/O (AIO) flag. */
int align_offset = 0; /* Align buffer at this offset. */
#if defined(TTY)
speed_t baud_rate; /* The user selected baud rate. */
#endif /* defined(TTY) */
size_t block_size = BLOCK_SIZE; /* Default block size to use. */
size_t data_size; /* Data buffer size + pad bytes */
size_t dump_limit = 64; /* The dump buffer data limit. */
u_int32 device_size = 0; /* Default device block size. */
pid_t child_pid; /* For the child process ID. */
bool bypass_flag = FALSE; /* Bypass (some) sanity checks. */
bool cerrors_flag = TRUE; /* Report device close errors. */
bool compare_flag = TRUE; /* Controls data comparisions. */
bool core_dump = FALSE; /* Generate core dump on errors */
bool debug_flag = FALSE; /* Enable debug output flag. */
bool Debug_flag = FALSE; /* Verbose debug output flag. */
bool eDebugFlag = FALSE; /* End of file debug flag. */
bool rDebugFlag = FALSE; /* Random (seek) debug flag. */
bool dump_flag = TRUE; /* Dump data buffer on errors. */
#if defined(EEI)
bool eei_flag = TRUE; /* Extended Error Information. */
bool eei_resets = FALSE; /* Handle device reset errors. */
#endif /* defined(EEI) */
v_bool end_of_file = FALSE; /* End of file detected. */
#if defined(_NT_SOURCE)
/*
* This workaround is being added for our Tcl/Tk test scripts.
* The Windows TclX wait{} procedure, isn't returning exit status!
*/
bool eof_status = FALSE; /* Controls EOF exit status. */
#else /* !defined(_NT_SOURCE) */
bool eof_status = TRUE; /* Controls EOF exit status. */
#endif /* defined(_NT_SOURCE) */
vu_long error_count; /* Number of errors detected. */
u_long error_limit = 1; /* Number of errors tolerated. */
int exit_status = SUCCESS; /* Normal success exit status. */
u_long file_limit; /* # of tape files to process. */
bool forked_flag = FALSE; /* Forked child process flag. */
bool fsync_flag = UNINITIALIZED; /* fsync() after writes flag. */
bool fsalign_flag = FALSE; /* Align FS offsets and sizes. */
off_t file_position; /* File position to lseek to. */
off_t last_position; /* Last position lseeked to. */
off_t step_offset; /* Step offset for disk seeks. */
bool flush_flag = TRUE; /* Flush tty input/output queue */
bool keep_existing = TRUE; /* Don't delete existing files. */
bool header_flag = TRUE; /* The log file header flag. */
bool hazard_flag = FALSE; /* Emit Hazard RPCLOGn: prefix. */
bool user_incr = FALSE; /* User specified incr count. */
bool user_min = FALSE; /* User specified min size. */
bool user_max = FALSE; /* User specified max size. */
size_t incr_count; /* Record increment byte count. */
size_t min_size; /* The minimum record size. */
size_t max_size; /* The maximum record size. */
bool lbdata_flag = FALSE; /* Logical block data flag. */
u_int32 lbdata_addr = 0; /* Starting logical block addr. */
size_t lbdata_size = 0; /* Logical block data size. */
bool user_lbdata = FALSE; /* User specified starting lba. */
bool user_lbsize = FALSE; /* User specified lbdata size. */
bool user_position = FALSE; /* User specified file position.*/
bool incr_pattern = FALSE; /* Incrementing data pattern. */
bool iot_pattern = FALSE; /* IOT test pattern selected. */
bool logdiag_flag = FALSE; /* Log diagnostic messages. */
bool loopback = FALSE; /* Loopback to the same device. */
bool micro_flag = FALSE; /* Controls micro-second delay. */
bool mmap_flag = FALSE; /* Do memory mapped file I/O. */
#if defined(TTY)
bool modem_flag = FALSE; /* Testing tty modem control. */
#endif /* defined(TTY) */
bool media_changed = FALSE; /* Shows when media changed. */
bool multi_flag = FALSE; /* Multi-volume media flag. */
v_int multi_volume = 1; /* Multi-volume media count. */
int open_flags = 0; /* Common file open flags. */
int wopen_flags = 0; /* Additional write open flags. */
int ropen_mode = O_RDONLY; /* The read open mode to use. */
int wopen_mode = O_WRONLY; /* The write open mode to use. */
bool pad_check = TRUE; /* Check data buffer pad bytes. */
bool spad_check = FALSE; /* Check short record pad bytes.*/
u_long pass_count; /* Number of passes completed. */
u_long pass_limit = 1UL; /* Default number of passes. */
u_long skip_count; /* # of input record to skip. */
u_long seek_count; /* # of output records to seek. */
large_t record_limit; /* Max # of records to process. */
vu_long records_processed; /* # of full records processed. */
vu_long partial_records; /* # of partial records proc'ed */
large_t data_limit; /* Total data limit per pass. */
u_long random_align = 0UL; /* Random I/O offset alignment. */
large_t rdata_limit = 0; /* The random I/O data limit. */
large_t total_bytes; /* Total bytes transferred. */
large_t total_bytes_read; /* Total bytes read. */
large_t total_bytes_written; /* Total bytes written. */
vu_long total_errors; /* Total errors (all passes). */
large_t total_files; /* Total files (all passes). */
large_t total_files_read; /* Total files read. */
large_t total_files_written; /* Total files written. */
large_t total_records; /* Total records (all passes). */
u_long total_partial; /* Total partial records. */
u_long warning_errors; /* Total non-fatal error count. */
bool pstats_flag = TRUE; /* Display per pass statistics. */
bool raw_flag = FALSE; /* The read after write flag. */
bool rotate_flag = FALSE; /* Force data buffer rotating. */
int rotate_offset = 0; /* Current rotate buffer offset */
bool stats_flag = TRUE; /* Display total statistics. */
bool stdin_flag = FALSE; /* Presume not reading stdin. */
bool stdout_flag = FALSE; /* Presume not writing stdout. */
bool terminating_flag = FALSE; /* Program terminating flag. */
bool ttyport_flag = FALSE; /* Input/output is a terminal. */
bool verbose_flag = TRUE; /* Verbose messages output. */
bool verify_flag = TRUE; /* Verify the read/write data. */
bool verify_only = FALSE; /* Verify of copied data flag. */
char *cmd_line; /* Copy of our command line. */
char *log_file; /* Pointer to log file name. */
char *log_buffer; /* Pointer to log file buffer. */
char *log_bufptr; /* Pointer into log buffer. */
char *msg_buffer; /* Diagnostic message buffer. */
char *input_file; /* Pointer to input file name. */
char *output_file; /* Pointer to output file name. */
char *pattern_file; /* Pointer to pattern file name */
u_char *pattern_buffer; /* Pointer to pattern buffer. */
u_char *pattern_bufptr; /* Pointer into pattern buffer. */
u_char *pattern_bufend; /* Pointer to end of pat buffer */
u_char *base_buffer; /* Base address of data buffer. */
u_char *data_buffer; /* Pointer to data buffer. */
u_char *mmap_buffer; /* Pointer to mmapped buffer. */
u_char *mmap_bufptr; /* Pointer into mmapped buffer. */
u_char *verify_buffer; /* The data verification buffer.*/
size_t patbuf_size; /* The pattern buffer size. */
int pattern_size; /* User specified pattern size. */
int prefix_size; /* User defined prefix size. */
int page_size = 0; /* Define number of bytes/page. */
#if defined(HP_UX)
u_int qdepth = 0xFFFFFFFF; /* Value to set queue depth to. */
#endif
u_int cdelay_count = 0; /* Delay before closing file. */
u_int edelay_count = 0; /* Delay between multiple passes*/
u_int rdelay_count = 0; /* Delay before reading record. */
u_int sdelay_count = 0; /* Delay before starting test. */
u_int tdelay_count = 1; /* Child terminate delay count. */
u_int wdelay_count = 0; /* Delay before writing record. */
u_int random_seed = 0; /* Seed for random # generator. */
bool user_rseed = FALSE; /* Flags user specified rseed. */
bool max_capacity = FALSE; /* Use max capacity from IOCTL. */
large_t user_capacity; /* The user set drive capacity. */
bool variable_flag = FALSE; /* Variable block size flag. */
bool volumes_flag = FALSE; /* Flags the volumes option. */
int volume_limit = 0; /* Number of volumes to process.*/
vu_long volume_records = 1; /* The last volume record limit.*/
#if defined(TTY)
enum opt softcar_opt = OPT_NONE; /* Leave tty soft carrier alone.*/
enum flow flow_type = XON_XOFF; /* Terminal flow type to use. */
#endif /* defined(TTY) */
enum iodir io_dir = FORWARD; /* Default is forward I/O. */
enum iomode io_mode = TEST_MODE; /* Default to testing mode. */
enum iotype io_type = SEQUENTIAL_IO; /* Default to sequential I/O. */
enum dispose dispose_mode = DELETE_FILE; /* Output file dispose mode. */
enum onerrors oncerr_action = CONTINUE; /* The child error action. */
/* Error limit controls tests. */
enum trigger_type trigger = TRIGGER_NONE; /* Trigger for corruptions. */
char *trigger_cmd = NULL; /* The uSers' trigger command. */
#if defined(SOLARIS) || defined(OSFMK) || defined(__QNXNTO__) || defined(AIX)
clock_t hz;
#else
clock_t hz = HZ; /* Default clock ticks / second */
#endif
#if defined(TTY)
/*
* Values for Terminal (serial) Line Testing:
*/
u_short tty_timeout = 3*10; /* Default tty timeout (3 sec). */
/* VTIME = 0.10 second interval */
bool tty_minflag = FALSE; /* User specified VMIN value. */
u_short tty_minimum = 0; /* The tty minimum (VMIN) value */
#endif /* defined(TTY) */
FILE *efp; /* Default error data stream. */
FILE *ofp; /* Default output data stream. */
int pfd = NoFd; /* Pattern file descriptor. */
char *cmdname; /* Pointer to our program name. */
char *string; /* Pointer to argument string. */
/*
* Pointers to various device information.
*/
struct dinfo *active_dinfo; /* Active device information. */
struct dinfo *input_dinfo; /* Input device information. */
struct dinfo *output_dinfo; /* Output device information. */
struct dtype *input_dtype; /* The input device type info. */
struct dtype *output_dtype; /* The output device type info. */
/*
* System time information.
*/
clock_t start_time, end_time, pass_time; /* Per pass elapsed time. */
struct tms stimes, ptimes, etimes; /* For user / system times. */
#if defined(DEC)
bool table_flag = FALSE; /* Table control flag. */
struct tbl_sysinfo s_table, p_table, e_table; /* Table information. */
#endif /* defined(DEC) */
#if defined(_BSD)
union wait child_status; /* For child exit status. */
#else /* !defined(_BSD) */
int child_status; /* For child exit status. */
#endif /* defined(_BSD) */
/*
* Program run time information.
*/
time_t runtime; /* The program run time. */
time_t elapsed_time; /* Amount of time program ran. */
time_t program_start, program_end; /* Program start & end times, */
time_t error_time; /* Time last error occurred. */
bool TimerActive; /* Set after timer activated. */
bool TimerExpired; /* Set after timer has expired. */
char *user_runtime; /* User specific runtime string */
/*
* Data patterns used for multiple passes.
*/
u_int32 data_patterns[] = {
DEFAULT_PATTERN,
0x00ff00ffU,
0x0f0f0f0fU,
0xc6dec6deU,
0x6db6db6dU,
0x55555555U,
0xaaaaaaaaU, /* Complement of previous data pattern. */
0x33333333U, /* Continuous worst case pattern (media defects) */
0x26673333U, /* Frequency burst worst case pattern #1. */
0x66673326U, /* Frequency burst worst case pattern #2. */
0x71c7c71cU, /* Dibit worst case data pattern. */
0x00000000U,
0xffffffffU,
};
int npatterns = sizeof(data_patterns) / sizeof(u_int32);
/*
* main() - Start of data transfer program.
*/
int
main (int argc, char **argv)
{
struct dinfo *dip;
struct dtfuncs *dtf;
char *tmp;
int status;
efp = stderr; /* Initialize our error stream. */
ofp = stdout; /* Initialize our output stream.*/
#if defined(_BSD)
tmp = rindex (argv[0], '/');
#else /* !defined(_BSD) */
tmp = strrchr (argv[0], '/');
#endif /* defined(_BSD) */
cmdname = tmp ? &(tmp[1]) : argv[0];
#if defined(_QNX_SOURCE) && !defined(_QNX_32BIT)
page_size = 0; /* Presume page size doesn't matter. */
#elif defined(_QNX_SOURCE) && defined(_QNX_32BIT)
page_size = 4096; /* Not sure how to query for this... */
#elif defined(DEC) || defined(SOLARIS) || defined(__linux__) || defined(SCO) || defined(__NUTC__) || defined(HP_UX) || defined(AIX)
hz = sysconf(_SC_CLK_TCK);
page_size = sysconf(_SC_PAGESIZE);
#else /* !defined(_QNX_SOURCE) && !defined(_QNX_32BIT) */
page_size = getpagesize();
#endif /* defined(_QNX_SOURCE) && !defined(_QNX_32BIT) */
#if defined(OSFMK) || defined(__QNXNTO__)
hz = CLK_TCK; /* Actually a libc function. */
#endif /* defined(OSFMK) || defined(__QNX_NTO__) */
#if defined(LOG_DIAG_INFO)
/*
* Allow DT_LOG_DIAG to enable logging diagnostic information.
*/
if (tmp = getenv ("DT_LOG_DIAG")) {
logdiag_flag = TRUE;
}
#endif /* defined(LOG_DIAG_INFO) */
data_limit = INFINITY; /* Set to maximum limit. */
parse_args (argc, argv);
/*
* Options parsed, validate options, do initialization, and open
* input & output files to be tested.
*/
program_start = time((time_t) 0); /* Record our start time. */
/*
* If a log file was specified, redirect stderr to that file.
*/
if (log_file) {
ofp = efp; /* Send all output to stderr. */
if (freopen (log_file, "a", efp) == NULL) {
report_error (log_file, TRUE);
exit (exit_status);
}
}
/*
* Make stderr buffered, so timing is not affected by output.
*/
if ((log_buffer = (char *) malloc (LOG_BUFSIZE)) == NULL) {
LogMsg (efp, logLevelCrit, 0,
"Unable to allocate log file buffer of %d bytes, exiting...\n",
LOG_BUFSIZE);
exit (ENOMEM);
}
/*
* The concept here is simple, set stderr buffered so multiple processes
* don't have their output intermixed. This piece of code has been very
* problematic, so it you have problems with garbled output, remove it.
*/
log_bufptr = log_buffer;
/*
* Since stderr is normally unbuffered, we make it buffered here.
*/
if ( isatty(fileno(efp)) ) {
char *stderr_buffer = (char *)malloc(LOG_BUFSIZE);
if (stderr_buffer == NULL) {
LogMsg (efp, logLevelCrit, 0,
"Unable to allocate stderr buffer of %d bytes, exiting...\n",
LOG_BUFSIZE);
exit (ENOMEM);
}
/*
* Can't use log buffer, or we get undesirable results :-)
*/
if (setvbuf(efp, stderr_buffer, _IOFBF, LOG_BUFSIZE) < 0) {
report_error ("setvbuf", TRUE);
exit (exit_status);
}
}
/*
* Write the command line to the log file, if one exists.
*/
if (log_file && header_flag) {
int arg;
/*
* Write the command line for future reference.
*/
Lprintf("Command Line:\n\n %c ", getuid() ? '%' : '#');
for (arg = 0; arg < argc; arg++) {
Lprintf("%s ", argv[arg]);
}
Lprintf("\n\n\t--> %s <--\n", version_str);
Lflush();
}
if (!input_file && !output_file) {
LogMsg (efp, logLevelCrit, 0,
"You must specify an input file, an output file, or both.\n");
exit (FATAL_ERROR);
}
/*
* Disallow both seek type options, to simplify test loops.
*/
if ( (io_dir == REVERSE) && (io_type == RANDOM_IO) ) {
LogMsg (efp, logLevelCrit, 0,
"Please specify one of iodir=reverse or iotype=random, not both!\n");
exit (FATAL_ERROR);
}
if (slice_num) {
if (!num_slices) {
LogMsg (efp, logLevelCrit, 0,
"Please specify number of slices with slice option!\n");
exit (FATAL_ERROR);
} else if (slice_num > num_slices) {
LogMsg (efp, logLevelCrit, 0,
"Please specify slice (%d) <= max slices (%d)\n",
slice_num, num_slices);
exit (FATAL_ERROR);
}
}
#if defined(MUNSA)
if (munsa_flag) {
if (input_file && !output_file) {
input_munsa_lock_type = munsa_lock_type;
if (debug_flag) {
Printf ("input_munsa_lock_type = %d\n",
input_munsa_lock_type);
}
}
if (output_file && !input_file) {
if ((munsa_lock_type == DLM_PWMODE) ||
(munsa_lock_type == DLM_EXMODE)) {
output_munsa_lock_type = munsa_lock_type;
if (debug_flag) {
Printf ("output_munsa_lock_type = %d\n",
output_munsa_lock_type);
}
} else {
LogMsg (efp, logLevelCrit, 0,
"invalid write lock type it should be pw,ex\n");
exit(FATAL_ERROR);
}
}
if (input_file && output_file) {
input_munsa_lock_type = DLM_PRMODE;
output_munsa_lock_type = DLM_PWMODE;
if (debug_flag) {
Printf ("input_munsa_lock_type = %d\n",
input_munsa_lock_type);
Printf ("output_munsa_lock_type = %d\n",
output_munsa_lock_type);
}
}
} /* end if(munsa_flag) .... */
#endif /* defined(MUNSA) */
if ( (!input_file || !output_file) &&
((io_mode == COPY_MODE) || (io_mode == VERIFY_MODE)) ) {
LogMsg (efp, logLevelCrit, 0,
"Copy/verify modes require both input and output devices.\n");
exit (FATAL_ERROR);
}
/*
* When reading multiple tape files, don't require data or record
* limits (these will vary). But when writing multiple tape files,
* we need to know how many records or bytes to be written.
*/
if (input_file && !output_file && file_limit && !record_limit) {
record_limit = INFINITY;
}
/*
* Check the variable record size parameters.
*/
if (min_size && !max_size) max_size = block_size;
if (block_size < max_size) block_size = max_size;
/* NOTE: Other checks are done below now... */
/*
* Calculate the data limit if it wasn't specified by the user.
*/
if ( (data_limit == INFINITY) &&
( (record_limit != 0L) && (record_limit != INFINITY) ) ) {
data_limit = (block_size * record_limit);
}
/*
* Process the pattern file (if one was specified).
*/
if (pattern_file) {
process_pfile (&pfd, pattern_file, O_RDONLY);
}
if (min_size && (max_size <= min_size)) {
LogMsg (efp, logLevelCrit, 0,
"Please specify max count > min count for record sizes.\n");
exit (FATAL_ERROR);
}
/*
* Verify counts are large enough, to avoid false compare errors.
*/
#if 0
if ( !bypass_flag && (iot_pattern || lbdata_flag) &&
((block_size < sizeof(u_int32)) ||
(min_size && (min_size <= sizeof(u_int32)))) ) {
LogMsg (efp, logLevelCrit, 0,
"Please specify block sizes > %d for IOT or Lbdata options!\n",
sizeof(u_int32));
exit (FATAL_ERROR);
}
#endif
if ( (iot_pattern || lbdata_flag) && (block_size < lbdata_size) ) {
LogMsg (efp, logLevelCrit, 0,
"Please specify a block size >= %u (lbdata size) for IOT or Lbdata options!\n",
lbdata_size);
exit (FATAL_ERROR);
}
if ( ((io_mode == COPY_MODE) || (io_mode == VERIFY_MODE)) &&
(iot_pattern || lbdata_flag) ) {
LogMsg (efp, logLevelCrit, 0,
"IOT and Lbdata options disallowed with Copy/Verify options!\n");
exit (FATAL_ERROR);
}
/*
* Do special handling of IOT data pattern.
*/
if (iot_pattern) {
size_t size = block_size;
u_char *buffer = (u_char *) myalloc(size, 0);
setup_pattern (buffer, size);
pattern_string = "IOT Pattern";
/* IOT takes precedence! */
lbdata_flag = FALSE;
user_lbdata = FALSE;
}
/*
* When doing random I/O, enable file system alignments, to help
* prevent false corruptions. This only affects regular files.
*/
if (io_type == RANDOM_IO) {
fsalign_flag = TRUE; /* Align FS sizes & random offsets. */
}
/*
* Setup the pattern as a pattern string, so non-modulo
* sizeof(u_int) read counts will data compare properly.
*/
if (!pattern_buffer) {
size_t size = sizeof(u_int32);
u_char *buffer = (u_char *) Malloc (size);
copy_pattern (pattern, buffer);
setup_pattern (buffer, size);
}
#if defined(TTY)
/*
* The following check was added for tty loopback to same port.
* [ A future version may extend this support to other devices. ]
*/
if (input_file && output_file) {
if (strcmp (input_file, output_file) == 0) {
loopback = TRUE; /* Loopback to the same file. */
}
} else if (loopback) {
if (!input_file && output_file) input_file = output_file;
if (input_file && !output_file) output_file = input_file;
}
#endif /* defined(TTY) */
/*
* Setup the initial device information & validate options.
*/
if (input_file) {
dip = setup_device_info (input_file, input_dtype);
active_dinfo = input_dinfo = dip;
dip->di_mode = READ_MODE;
dip->di_ftype = INPUT_FILE;
status = (*dip->di_funcs->tf_validate_opts)(dip);
if (status == FAILURE) exit (FATAL_ERROR);
}
if (output_file) {
dip = setup_device_info (output_file, output_dtype);
active_dinfo = output_dinfo = dip;
dip->di_mode = WRITE_MODE;
dip->di_ftype = OUTPUT_FILE;
status = (*dip->di_funcs->tf_validate_opts)(dip);
if (status == FAILURE) exit (FATAL_ERROR);
/*
* The following problem was resolved by not switching patterns
* when multiple processes are selected. So, this will go...
*/
#if 0
/*
* Sanity Check: Major source of problem reports due to folks
* using multiple processes with multiple passes, and subsequent
* passes overwriting the previous data pattern!
*/
if ( (pass_limit > 1) && (num_procs > 1) &&
!user_pattern && dip->di_random_access ) {
LogMsg (ofp, logLevelWarn, 0,
"Warning: Multiple passes with multiple processes can cause unpredictable\n");
LogMsg (ofp, logLevelWarn, 0,
"results due to process scheduling, since each pass uses a different pattern!\n");
}
#endif
}
#if defined(sun) && defined(TTY)
/*
* Soft carrier existed on the Sun/386i (Roadrunner) system.
* Setting O_NDELAY was necessary to open the terminal line.
*
* Normally, O_NDELAY must NOT be set, otherwise the terminal
* driver returns EWOULDBLOCK on reads if no data is available.
*
* Note: Without O_NDELAY, the open will hang on modem lines
* (-CLOCAL) if carrier is not asserted.
*/
if (ttyport_flag) {
open_flags = O_NDELAY; /* Incase no soft carrier. */
}
#endif /* defined(sun) && defined(TTY) */
/*
* Do multiple slices processing.
*/
if (num_slices) {
if (input_file && output_file) {
LogMsg (efp, logLevelCrit, 0,
"Please specify only an input or output file, not both!\n");
exit (FATAL_ERROR);
}
/*
* Create multiple slices (if requested).
*/
active_dinfo = dip = (input_file) ? input_dinfo : output_dinfo;
if (!dip->di_random_access) {
LogMsg (efp, logLevelCrit, 0,
"Multiple slices is only supported on random access devices!\n");
exit (FATAL_ERROR);
}
if ((status = FindCapacity (dip)) == FAILURE) {
exit (FATAL_ERROR);
}
/*
* The remaining work is done when starting the processes.
*/
if (slice_num) {
init_slice(active_dinfo, slice_num);
num_slices = 0; /* Operate on a single slice. */
} else {
if ( start_slices() ) {
await_procs();
exit (exit_status);
}
}
}
/*
* Create multiple processes (if requested).
*/
if (num_procs && !loopback && !ttyport_flag) {
if ( start_procs() ) {
await_procs();
exit (exit_status);
}
}
/*
* Open device / Setup system / device specific test information.
*/
if (input_file) {
int open_mode = (ropen_mode | open_flags);
dip = active_dinfo = input_dinfo;
if ((*dip->di_funcs->tf_open)(dip, open_mode) == FAILURE) {
exit (exit_status);
}
system_device_info (dip);
input_dtype = dip->di_dtype;
status = (*dip->di_funcs->tf_validate_opts)(dip);
if (status == FAILURE) exit (FATAL_ERROR);
#if defined(MUNSA)
if (munsa_flag) {
/* first we need to join a namespace */
l_stat = dlm_nsjoin( getuid(), &nsp, DLM_USER);
if ((l_stat != DLM_SUCCESS) && (l_stat != DLM_ATTACHED)) {
Fprintf ("Can't join namespace\n");
dlm_error(0, l_stat);
}
resnam = input_file;
/* now let DLM know what signal to use for blocking routines */
dlm_set_signal(SIGIO, &i);
resnlen = strlen(resnam);
if (debug_flag) {
Printf ("dlm_set_signal: i %d\n", i);
Printf ("resnam %s\n", resnam);
Printf ("grab a NL mode lock\n");
}
l_stat = dlm_lock(nsp,
(uchar_t *)resnam,
resnlen,
0,
&lkid,
DLM_NLMODE,
NULL,
DLM_SYNCSTS,
0, 0, 0, 0);
/* NL mode better be granted SYNC status */
if (l_stat != DLM_SYNCH) {
if (debug_flag) {
Fprintf ("dlm_lock failed\n");
}
dlm_error(&lkid, l_stat);
}
} /* end if(munsa_flag)... */
#endif /* defined(MUNSA) */
/*
* If disk device and random I/O selected, attempt to get
* device / partition capacity to limit random I/O seeks.
*/
if ( user_capacity ||
( ((io_dir == REVERSE) || (io_type == RANDOM_IO)) &&
(dip->di_random_access && !num_slices) ) ) {
if ((status = FindCapacity (dip)) == FAILURE) {
exit (exit_status);
}
}
if (!record_limit) {
record_limit = INFINITY; /* Read until EOF on reads. */
}
}
/*
* Process the output device/file.
*/
if (output_file) {
int open_mode;
dip = active_dinfo = output_dinfo;
/*
* If a skip count was specified, open output file for R/W,
* since skips are accomplished via read()'s. (for pelle)
*/
if (skip_count || raw_flag) {
open_mode = (O_RDWR | wopen_flags | open_flags);
} else {
open_mode = (wopen_mode | wopen_flags | open_flags);
}
/*
* Don't create files in the /dev directory (presume the file
* should exist instead of creating file & misleading user).
*/
if ( (NEL (output_file, DEV_PREFIX, DEV_LEN)) &&
(NEL (output_file, ADEV_PREFIX, ADEV_LEN)) ) {
open_mode |= O_CREAT; /* For creating test files. */
}
/*
* If verify mode, the output device is open for reads.
*/
if (io_mode == VERIFY_MODE) {
open_mode = (ropen_mode | open_flags);
dip->di_mode = READ_MODE;
}
if ((*dip->di_funcs->tf_open)(dip, open_mode) == FAILURE) {
exit (exit_status);
}
#if defined(MUNSA)
if (munsa_flag) {
/* first we need to join a namespace */
l_stat = dlm_nsjoin( getuid(), &nsp, DLM_USER);
if ((l_stat != DLM_SUCCESS) && (l_stat != DLM_ATTACHED)) {
Fprintf ("Can't join namespace\n");
dlm_error(0, l_stat);
}
resnam = output_file;
/* now let DLM know what signal to use for blocking routines */
dlm_set_signal(SIGIO, &i); /* do we need this ????? */
resnlen = strlen(resnam);
if (debug_flag) {
Printf ("dlm_set_signal: i %d\n", i);
Printf ("resnam %s\n", resnam);
Printf ("grab a NL mode lock\n");
}
l_stat = dlm_lock(nsp,
(uchar_t *)resnam,
resnlen,
0,
&lkid,
DLM_NLMODE,
NULL,
DLM_SYNCSTS,
0, 0, 0, 0);
/* NL mode better be granted SYNC status */
if (l_stat != DLM_SYNCH) {
Fprintf ("dlm_lock failed\n");
dlm_error(&lkid, l_stat);
}
} /* end if (munsa_flag)... */
#endif /* defined(MUNSA) */
system_device_info (dip);
output_dtype = dip->di_dtype;
open_flags &= ~O_CREAT; /* Only create on first open. */
status = (*dip->di_funcs->tf_validate_opts)(dip);
if (status == FAILURE) exit (FATAL_ERROR);
/*
* If disk device and random I/O selected, attempt to get
* device / partition capacity to limit random I/O seeks.
*/
if ( user_capacity ||
( ((io_dir == REVERSE) || (io_type == RANDOM_IO)) &&
(dip->di_random_access && !num_slices) ) ) {
if ((status = FindCapacity (dip)) == FAILURE) {
exit (exit_status);
}
}
/*
* For disks and tapes, default writing until EOF is reached.
*/
if ( !record_limit &&
((dip->di_dtype->dt_dtype == DT_DISK) ||
(dip->di_dtype->dt_dtype == DT_BLOCK) ||
(dip->di_dtype->dt_dtype == DT_TAPE)) ) {
record_limit = INFINITY;
}
}
/*
* Set the default lbdata size, if not setup by the system
* dependent functions above. Delaying this check to this
* point allows the device sector size to be setup, instead
* of forcing it to 512 byte blocks. At least this is true
* on Tru64 Unix, where this disk information is available.
*/
if (!lbdata_size) lbdata_size = BLOCK_SIZE;
/*
* This is to catch me (dah!) as much as anyone else :-)
*/
if ( (rdata_limit || random_align) &&
((io_dir != REVERSE) && (io_type != RANDOM_IO)) ) {
Printf ("Warning, random options have no effect without iotype=random!\n");
}
/*
* If random I/O was selected, and a data limit isn't available,
* inform the user we need one, and don't allow testing.
*/
if (rdata_limit == 0UL) rdata_limit = data_limit;
if ( (rdata_limit == 0) && (io_type == RANDOM_IO) ) {
LogMsg (efp, logLevelCrit, 0,
"Please specify a record or data limit for random I/O.\n");
exit (FATAL_ERROR);
}
/*
* Sanity check the random I/O data limits.
*/
if ( (io_type == RANDOM_IO) &&
((file_position + block_size + random_align) > rdata_limit)) {
LogMsg (efp, logLevelCrit, 0,
"The max block size is too large for random data limits!\n");
if (Debug_flag) {
Printf ("file position " FUF ", bs=%ld, ralign=%ld, rlimit=" LUF "\n",
file_position, block_size, random_align, rdata_limit);
}
exit (FATAL_ERROR);
}
/*
* Ensure either a data limit and/or a record count was specified.
*/
if (!record_limit) {
LogMsg (efp, logLevelCrit, 0,
"You must specify a data limit, a record count, or both.\n");
exit (FATAL_ERROR);
}
/*
* Allocate buffer for pre-formatting messages.
*/
msg_buffer = (char *)Malloc(LOG_BUFSIZE);
#if defined(LOG_DIAG_INFO)
/*
* If logging of diagnostic info is enabled, save copy of cmd line.
*/
if (logdiag_flag) {
int arg;
char *bp;
bp = cmd_line = (char *)Malloc(LOG_BUFSIZE);
for (arg = 0; arg < argc; arg++) {
(void)sprintf(bp, "%s ", argv[arg]);
bp += strlen(bp);
}
sprintf(bp, "\n");
}
#endif /* defined(LOG_DIAG_INFO) */
/*
* Catch a couple signals to do elegant cleanup.
*/
(void) signal (SIGHUP, terminate);
(void) signal (SIGINT, terminate);
(void) signal (SIGTERM, terminate);
(void) signal (SIGPIPE, terminate);
/*
* If both an input and an output files were specified, then
* fork and make child process the reader, parent the writer.
*/
if ( (io_mode == TEST_MODE) && input_file && output_file) {
if ( (child_pid = fork()) == (pid_t) -1) {
report_error ("fork", TRUE);
exit (exit_status);
}
forked_flag = TRUE;
if (child_pid) { /* Parent = writer. */
struct dinfo *dip = input_dinfo;
(void) close_file (dip);
input_file = NULL;
if (debug_flag) {
Printf ("Parent PID (Writer) = %d, Child PID (Reader) = %d\n",
getpid(), child_pid);
}
#if !defined(__MSDOS__) || defined(__NUTC__)
signal (SIGCHLD, terminate);
#endif
} else { /* Child = reader. */
struct dinfo *dip = output_dinfo;
(void) close_file (dip);
output_file = NULL;
}
}
/*
* Some drivers require the input device to open before we start
* writing. For example, terminal devices must have speed, parity,
* and flow control setup before we start writing. The parallel
* input device must open before we send the "readya" interrupt.
*/
if (output_file && (sdelay_count != 0)) {
mySleep (sdelay_count); /* Allow reader to start. */
}
/*
* Calculate size necessary for the data buffer & the pad bytes.
*/
data_size = (block_size + PADBUFR_SIZE);
if (rotate_flag) data_size += ROTATE_SIZE;
/*
* For read-after-write (raw) option, we need a verification buffer.
* Note: Other tests can benefit from this too, switch to later.
*/
if (raw_flag) {
verify_buffer = myalloc(data_size, align_offset);
}
/*
* Do the device / test specific initialization.
*
* This function is responsible for allocating the necessary
* data buffers and performing special device setup/checking.
*/
if (input_file) {
dip = input_dinfo;
status = (*dip->di_funcs->tf_initialize)(dip);
if (status == FAILURE) exit (FATAL_ERROR);
}
if (output_file) {
dip = output_dinfo;
status = (*dip->di_funcs->tf_initialize)(dip);
if (status == FAILURE) exit (FATAL_ERROR);
}
/*
* Re-adjust size of data buffer to avoid init'ing too much.
*/
if (rotate_flag) data_size -= ROTATE_SIZE;
/*
* Finally format the prefix string (if any), after the device is
* setup and processes are forked so we can setup a unique prefix.
*/
if (prefix_string) {
status = FmtPrefix(dip, &prefix_string, &prefix_size);
if (status == FAILURE) exit (FATAL_ERROR);
if ((prefix_size) > lbdata_size) {
LogMsg (efp, logLevelCrit, 0,
"The prefix size (%d) is larger than lbdata size (%d)!\n",
prefix_size, lbdata_size);
exit (FATAL_ERROR);
}
}
/*
* Start an alarm timer if the run time was specified.
*/
if (runtime) {
(void) signal (SIGALRM, terminate);
(void) alarm (runtime);
TimerActive = TRUE;
}
/*
* Start of main test loop.
*/
start_time = times (&stimes);
#if defined(LOG_DIAG_INFO)
if (logdiag_flag) {
sprintf(msg_buffer, "Starting: %s", cmd_line);
LogDiagMsg(msg_buffer);
}
#endif /* defined(LOG_DIAG_INFO) */
#if defined(DEC)
if (table_flag) {
status = table(TBL_SYSINFO,0,(char *)&s_table,1,sizeof(struct tbl_sysinfo));
if (status == FAILURE) report_error ("table", FALSE);
}
#endif /* defined(DEC) */
/*
* We support multiple modes of operation: copy, test, & verify
*/
if ( (io_mode == COPY_MODE) || (io_mode == VERIFY_MODE) ) {
/*
* This is ugly, but I'm using pattern buffer as verify buffer.
*/
if ( (io_mode == VERIFY_MODE) || verify_flag) {
pattern_buffer = myalloc (data_size, align_offset);
setup_pattern (pattern_buffer, data_size);
}
pass_time = times (&ptimes); /* Start the pass timer */
#if defined(DEC)
if (table_flag) {
status = table(TBL_SYSINFO,0,(char *)&p_table,1,sizeof(struct tbl_sysinfo));
if (status == FAILURE) report_error ("table", FALSE);
}
#endif /* defined(DEC) */
dip = active_dinfo = input_dinfo;
dtf = dip->di_funcs;
dip->di_mode = READ_MODE;
(void) (*dtf->tf_start_test)(dip);
(void) (*dtf->tf_read_file)(dip);
(void) (*dtf->tf_end_test)(dip);
gather_stats(output_dinfo);
/*
* Now verify the data copied (if requested).
*/
if ( (io_mode == COPY_MODE) && verify_flag &&
!stdin_flag && (error_count < error_limit) ) {
struct dinfo *odip = output_dinfo;
int open_mode = (ropen_mode | open_flags);
report_pass (dip, COPY_STATS); /* Report copy statistics. */
/*
* Verify Pass.
*/
status = (*dtf->tf_reopen_file)(dip, open_mode);
if (status != SUCCESS) terminate(status);
odip->di_mode = READ_MODE; /* Switch to read mode. */
status = (*odip->di_funcs->tf_reopen_file)(odip, open_mode);
if (status != SUCCESS) terminate(status);
pass_time = times (&ptimes); /* Time the verify. */
#if defined(DEC)
if (table_flag) {
status = table(TBL_SYSINFO,0,(char *)&p_table,1,sizeof(struct tbl_sysinfo));
if (status == FAILURE) report_error ("table", FALSE);
}
#endif /* defined(DEC) */
io_mode = VERIFY_MODE;
(void) (*dtf->tf_start_test)(dip);
(void) (*dtf->tf_read_file)(dip);
(void) (*dtf->tf_end_test)(dip);
pass_count++; /* End of copy/verify pass. */
gather_stats(output_dinfo); /* Gather device statistics. */
report_pass (dip, VERIFY_STATS); /* Report the verify stats. */
} else {
pass_count++; /* End of copy pass. */
}
} else { /* not Copy or Verify modes, Test Mode! */
while ( (total_errors < error_limit) &&
((pass_count < pass_limit) || runtime) ) {
if (pattern_buffer) {
pattern_bufptr = pattern_buffer;
}
/*
* Use a different data pattern for each pass.
*/
if ( !user_pattern && !ttyport_flag &&
(loopback || output_file || stdin_flag ||
(input_file && num_slices)) ) {
/*
* Logic:
* - If multiple slices, choose a different pattern
* for each pass, factoring in the process number.
* - Else, each pass gets a different data pattern
* unless multiple processes were selected.
*/
if ( unique_pattern &&
( (!num_procs && num_slices) ||
(num_procs && (dip->di_dtype->dt_dtype == DT_REGULAR)) ) ) {
int pindex = ((cur_proc - 1) + pass_count);
pattern = data_patterns[pindex % npatterns];
} else if (!num_procs) {
pattern = data_patterns[pass_count % npatterns];
}
if (pattern_buffer) copy_pattern (pattern, pattern_buffer);
if (debug_flag) {
Printf ("Using data pattern 0x%08x for pass %u\n",
pattern, (pass_count + 1));
}
}
/*
* Use time for random # generator seed so different areas
* of disk get affected during multiple passes. Seed set
* here, since it must be the same seed during read pass.
* NOTE: Pid is added so seed different for each process.
*/
if ( (io_type == RANDOM_IO) || variable_flag) {
if ( !user_rseed ) {
random_seed = (u_int) times(&ptimes) + getpid();
}
set_rseed (random_seed);
}
pass_time = times (&ptimes); /* Start the pass timer */
#if defined(DEC)
if (table_flag) {
status = table(TBL_SYSINFO,0,(char *)&p_table,1,sizeof(struct tbl_sysinfo));
if (status == FAILURE) report_error ("table", FALSE);
}
#endif /* defined(DEC) */
if (output_file) { /* Write/read the file. */
bool do_read_pass;
dip = active_dinfo = output_dinfo;
dtf = dip->di_funcs;
dip->di_mode = WRITE_MODE;
(void) (*dtf->tf_start_test)(dip);
(void) (*dtf->tf_write_file)(dip);
(void) (*dtf->tf_flush_data)(dip);
(void) (*dtf->tf_end_test)(dip);
if (error_count >= error_limit) break;
do_read_pass = (dip->di_dbytes_written != (large_t) 0);
/*
* Now verify (read and data compare) the data just written.
*/
if (verify_flag && do_read_pass && !raw_flag) { /* Verify data written. */
int open_mode = (ropen_mode | open_flags);
report_pass (dip, WRITE_STATS); /* Report write stats. */
if (multi_flag && media_changed) {
status = RequestFirstVolume(dip, open_flags);
} else {
status = (*dtf->tf_reopen_file)(dip, open_mode);
}
if (status != SUCCESS) break;
if ( (io_type == RANDOM_IO) || variable_flag) {
set_rseed (random_seed);
}
pass_time = times (&ptimes); /* Time just the read. */
#if defined(DEC)
if (table_flag) {
status = table(TBL_SYSINFO,0,(char *)&p_table,1,sizeof(struct tbl_sysinfo));
if (status == FAILURE) report_error ("table", FALSE);
}
#endif /* defined(DEC) */
/*rotate_offset = 0;*/
if (pattern_buffer) {
pattern_bufptr = pattern_buffer;
}
dip->di_mode = READ_MODE;
(void) (*dtf->tf_start_test)(dip);
(void) (*dtf->tf_read_file)(dip);
(void) (*dtf->tf_end_test)(dip);
pass_count++; /* End read/write pass. */
report_pass (dip, READ_STATS); /* Report read stats. */
} else {
pass_count++; /* End of write pass. */
if ( (pass_limit > 1) || runtime) {
/* Report write stats. */
if (raw_flag) {
report_pass (dip, RAW_STATS);
} else {
report_pass (dip, WRITE_STATS);
}
}
}
if ( (pass_count < pass_limit) || runtime) {
int open_mode;
if (skip_count || raw_flag) {
open_mode = (O_RDWR | wopen_flags | open_flags);
} else {
open_mode = (wopen_mode | wopen_flags | open_flags);
}
status = (*dtf->tf_reopen_file)(dip, open_mode);
if (status != SUCCESS) break;
}
} else { /* Reading only. */
dip = active_dinfo = input_dinfo;
dtf = dip->di_funcs;
dip->di_mode = READ_MODE;
(void) (*dtf->tf_start_test)(dip);
(void) (*dtf->tf_read_file)(dip);
(void) (*dtf->tf_end_test)(dip);
pass_count++; /* End of read pass. */
/*
* Prevent pass unless looping, since terminate reports
* the total statistics when called (prevents dup stats).
*/
if ( (pass_limit > 1) || runtime) {
report_pass (dip, READ_STATS); /* Report read stats. */
}
if ( (total_errors < error_limit) &&
((pass_count < pass_limit) || runtime) ) {
int open_mode = (ropen_mode | open_flags);
status = (*dtf->tf_reopen_file)(dip, open_mode);
if (status != SUCCESS) break;
}
}
#if defined(MUNSA)
if (munsa_flag) {
if (debug_flag) {
Printf ("converting to dlm NL-> %d \n", DLM_NLMODE);
}
l_stat = dlm_cvt(&lkid, DLM_NLMODE, NULL, 0, 0, 0, NULL, 0);
if (l_stat != DLM_SUCCESS) {
Fprintf ("dlm_cvt failed\n");
dlm_error(&lkid, l_stat); /* exit with FATAL ERROR */
}
} /* end if(munsa_flag)... */
#endif /* defined(MUNSA) */
}
} /* end 'if ( (io_mode == COPY_MODE) || (io_mode == VERIFY_MODE) ) ' */
#if defined(MUNSA)
if (munsa_flag) {
if ((l_stat = dlm_unlock(&lkid, NULL, 0)) != DLM_SUCCESS)
dlm_error(&lkid, l_stat); /* exit with FATAL ERROR */
if (debug_flag) {
Printf("\n %s: unlocked...\n", resnam);
}
} /* end if(munsa_flag)... */
#endif /* defined(MUNSA) */
terminate (exit_status); /* Terminate with exit status. */
/*NOTREACHED*/
return (exit_status); /* Quiet certain compilers! */
}
#if defined(MUNSA)
/************************************************************************
* *
* dlm_error() - Print all MUNSA dlm related error msg. *
* *
* Inputs: dlm_lkid_t *lk *
* dlm_status_t stat *
* *
* If errors are detected, we simply exit with a fatal error. *
* *
************************************************************************/
void
dlm_error(dlm_lkid_t *lk, dlm_status_t err_stat)
{
dlm_rsbinfo_t rsb; /* used by dlm_sperrno() */
LogMsg (efp, logLevelCrit, 0,
"lock error %s on lkid 0x%lx\n",
dlm_sperrno(err_stat), *lk);
exit (FATAL_ERROR);
}
#endif /* defined(MUNSA) */
/************************************************************************
* *
* parse_args() - Parse 'dt' Program Arguments. *
* *
* Inputs: argc = The number of arguments. *
* argv = Array pointer to arguments. *
* *
* If errors are detected, we simply exit with a fatal error. *
* *
************************************************************************/
void
parse_args (int argc, char **argv)
{
int i;
if (argc == 1) dtusage();
for (i = 1; i < argc; i++) {
string = argv[i];
if (match ("aios=")) {
aio_bufs = (int)number(ANY_RADIX);
if (aio_bufs) aio_flag = TRUE;
continue;
}
if (match ("align=")) {
if (match ("rotate")) {
rotate_flag = TRUE;
continue;
}
align_offset = (u_int) number(ANY_RADIX);
continue;
}
if (match ("bs=")) {
block_size = number(ANY_RADIX);
if ((ssize_t)block_size <= (ssize_t) 0) {
LogMsg (efp, logLevelCrit, 0,
"block size must be positive and non-zero.\n");
exit (FATAL_ERROR);
}
continue;
}
if (match ("capacity=")) {
if (match ("max")) {
max_capacity = TRUE;
} else {
user_capacity = large_number(ANY_RADIX);
}
continue;
}
if (match ("dsize=")) {
device_size = number(ANY_RADIX);
continue;
}
if (match ("lba=")) {
lbdata_flag = TRUE;
lbdata_addr = number(ANY_RADIX);
user_lbdata = TRUE;
continue;
}
if (match ("lbs=")) {
lbdata_flag = TRUE;
lbdata_size = number(ANY_RADIX);
user_lbsize = TRUE;
if ((ssize_t)lbdata_size <= (ssize_t) 0) {
LogMsg (efp, logLevelCrit, 0,
"lbdata size must be positive and non-zero.\n");
exit (FATAL_ERROR);
}
continue;
}
if ( (match ("count=")) || (match ("records=")) ) {
record_limit = number(ANY_RADIX);
continue;
}
if (match ("cdelay=")) {
cdelay_count = (u_int) number(ANY_RADIX);
continue;
}
if (match ("edelay=")) {
edelay_count = (u_int) number(ANY_RADIX);
continue;
}
if (match ("rdelay=")) {
rdelay_count = (u_int) number(ANY_RADIX);
continue;
}
if (match ("sdelay=")) {
sdelay_count = (u_int) number(ANY_RADIX);
continue;
}
if (match ("tdelay=")) {
tdelay_count = (u_int) number(ANY_RADIX);
continue;
}
if (match ("wdelay=")) {
wdelay_count = (u_int) number(ANY_RADIX);
continue;
}
if (match ("errors=")) {
error_limit = number(ANY_RADIX);
continue;
}
if (match ("files=")) {
file_limit = number(ANY_RADIX);
continue;
}
if (match ("hz=")) {
hz = (u_int) number(ANY_RADIX);
continue;
}
if (match ("incr=")) {
user_incr = TRUE;
if (match ("var")) {
variable_flag = TRUE;
} else {
incr_count = number(ANY_RADIX);
}
continue;
}
if (match ("dlimit=")) {
dump_limit = number(ANY_RADIX);
continue;
}
if (match ("limit=")) {
data_limit = large_number(ANY_RADIX);
if (!record_limit) {
record_limit = INFINITY;
}
continue;
}
if (match ("ralign=")) {
io_type = RANDOM_IO;
random_align = number(ANY_RADIX);
continue;
}
if (match ("rlimit=")) {
io_type = RANDOM_IO;
rdata_limit = large_number(ANY_RADIX);
continue;
}
if (match ("max=")) {
user_max = TRUE;
max_size = (size_t) number(ANY_RADIX);
continue;
}
if (match ("min=")) {
user_min = TRUE;
min_size = (size_t) number(ANY_RADIX);
continue;
}
if (match ("enable=")) {
eloop:
if (match(","))
goto eloop;
if (*string == '\0')
continue;
if (match("aio")) {
aio_flag = TRUE;
goto eloop;
}
if (match("bypass")) {
bypass_flag = TRUE;
goto eloop;
}
if (match("cerrors")) {
cerrors_flag = TRUE;
goto eloop;
}
if (match("compare")) {
compare_flag = TRUE;
goto eloop;
}
if (match("coredump")) {
core_dump = TRUE;
goto eloop;
}
if (match("debug")) {
debug_flag = TRUE;
goto eloop;
}
if (match("Debug")) {
Debug_flag = debug_flag = TRUE;
goto eloop;
}
if (match("edebug")) {
eDebugFlag = TRUE;
goto eloop;
}
if (match("rdebug")) {
rDebugFlag = TRUE;
goto eloop;
}
if (match("diag")) {
logdiag_flag = TRUE;
goto eloop;
}
if (match("dump")) {
dump_flag = TRUE;
goto eloop;
}
if (match("eof")) {
eof_status = TRUE;
goto eloop;
}
#if defined(EEI)
if (match("eei")) {
eei_flag = TRUE;
goto eloop;
}
if (match("resets")) {
eei_resets = TRUE;
goto eloop;
}
#endif /* defined(EEI) */
if (match("flush")) {
flush_flag = TRUE;
goto eloop;
}
if (match("fsync")) {
fsync_flag = TRUE;
goto eloop;
}
if (match("fsalign")) {
fsalign_flag = TRUE;
goto eloop;
}
if (match("header")) {
header_flag = TRUE;
goto eloop;
}
if (match("lbdata")) {
lbdata_flag = TRUE;
goto eloop;
}
if (match("loopback")) {
loopback = TRUE;
verify_flag = FALSE;
goto eloop;
}
if (match("microdelay")) {
micro_flag = TRUE;
goto eloop;
}
#if defined(MMAP)
if (match("mmap")) {
mmap_flag = TRUE;
wopen_mode = O_RDWR; /* MUST open read/write. */
goto eloop;
}
#endif /* defined(MMAP) */
#if defined(TTY)
if (match("modem")) {
open_flags = O_NONBLOCK;
modem_flag = TRUE;
goto eloop;
}
#endif /* defined(TTY) */
if (match("multi")) {
multi_flag = TRUE;
goto eloop;
}
if (match("pstats")) {
pstats_flag = TRUE;
goto eloop;
}
if (match("raw")) {
raw_flag = TRUE;
goto eloop;
}
if (match("hazard")) {
efp = ofp; /* Write errors to stdout. */
hazard_flag = TRUE;
goto eloop;
}
if (match("spad")) {
spad_check = TRUE;
goto eloop;
}
#if defined(sun) && defined(TTY)
if (match("softcar")) {
open_flags = O_NDELAY;
softcar_opt = ON;
goto eloop;
}
#endif /* defined(sun) && defined(TTY) */
#if defined(DEC)
if (match("table")) {
table_flag = TRUE;
goto eloop;
}
#endif /* defined(DEC) */
#if defined(TTY)
if (match("ttyport")) {
ttyport_flag = TRUE;
goto eloop;
}
#endif /* defined(TTY) */
if (match("unique")) {
unique_pattern = TRUE;
goto eloop;
}
if (match("verbose")) {
verbose_flag = TRUE;
goto eloop;
}
if (match("verify")) {
verify_flag = TRUE;
goto eloop;
}
LogMsg (efp, logLevelCrit, 0,
"Invalid enable keyword: %s\n", string);
exit (FATAL_ERROR);
}
if (match ("disable=")) {
dloop:
if (match(","))
goto dloop;
if (*string == '\0')
continue;
if (match("aio")) {
aio_flag = FALSE;
goto dloop;
}
if (match("bypass")) {
bypass_flag = FALSE;
goto dloop;
}
if (match("cerrors")) {
cerrors_flag = FALSE;
goto dloop;
}
if (match("compare")) {
compare_flag = FALSE;
goto dloop;
}
if (match("diag")) {
logdiag_flag = FALSE;
goto dloop;
}
if (match("dump")) {
dump_flag = TRUE;
goto dloop;
}
if (match("eof")) {
eof_status = FALSE;
goto dloop;
}
#if defined(EEI)
if (match("eei")) {
eei_flag = FALSE;
goto dloop;
}
if (match("resets")) {
eei_resets = FALSE;
goto dloop;
}
#endif /* defined(EEI) */
if (match("flush")) {
flush_flag = FALSE;
goto dloop;
}
if (match("fsync")) {
fsync_flag = FALSE;
goto dloop;
}
if (match("fsalign")) {
fsalign_flag = FALSE;
goto dloop;
}
if (match("header")) {
header_flag = FALSE;
goto dloop;
}
if (match("lbdata")) {
lbdata_flag = FALSE;
user_lbdata = FALSE;
goto dloop;
}
if (match("loopback")) {
loopback = FALSE;
goto dloop;
}
if (match("microdelay")) {
micro_flag = FALSE;
goto dloop;
}
#if defined(MMAP)
if (match("mmap")) {
mmap_flag = FALSE;
goto dloop;
}
#endif /* defined(MMAP) */
#if defined(TTY)
if (match("modem")) {
open_flags &= ~(O_NONBLOCK);
modem_flag = FALSE;
goto dloop;
}
#endif /* defined(TTY) */
if (match("pad")) {
pad_check = FALSE;
goto dloop;
}
if (match("pstats")) {
pstats_flag = FALSE;
goto dloop;
}
if (match("raw")) {
raw_flag = FALSE;
goto dloop;
}
if (match("hazard")) {
hazard_flag = FALSE;
goto dloop;
}
if (match("spad")) {
spad_check = FALSE;
goto dloop;
}
#if defined(TTY)
if (match("softcar")) {
softcar_opt = OFF;
goto dloop;
}
#endif /* defined(TTY) */
if (match("stats")) {
stats_flag = FALSE;
goto dloop;
}
#if defined(DEC)
if (match("table")) {
table_flag = FALSE;
goto dloop;
}
#endif /* defined(DEC) */
if (match("unique")) {
unique_pattern = FALSE;
goto dloop;
}
if (match("verbose")) {
verbose_flag = FALSE;
goto dloop;
}
if (match("verify")) {
verify_flag = FALSE;
goto dloop;
}
LogMsg (efp, logLevelCrit, 0,
"Invalid disable keyword: %s\n", string);
exit (FATAL_ERROR);
}
if (match ("dispose=")) {
if (match("delete")) {
keep_existing = FALSE;
dispose_mode = DELETE_FILE;
} else if (match("keep")) {
keep_existing = TRUE;
dispose_mode = KEEP_FILE;
} else {
LogMsg (efp, logLevelCrit, 0,
"Dispose modes are 'delete' or 'keep'.\n", string);
exit (FATAL_ERROR);
}
continue;
}
#if defined(MUNSA)
if (match ("munsa=")) {
munsa_flag = TRUE;
flow_str = string;
if (match("cr")) {
munsa_lock_type = DLM_CRMODE; /* Concurrent Read */
} else if (match("pr")) {
munsa_lock_type = DLM_PRMODE; /* Protected Read */
} else if (match("cw")) {
munsa_lock_type = DLM_CWMODE; /* Concurrent Write */
} else if (match("pw")) {
munsa_lock_type = DLM_PWMODE; /* Protected Write */
} else if (match("ex")) {
munsa_lock_type = DLM_EXMODE; /* EXclusive mode */
} else {
LogMsg (efp, logLevelCrit, 0,
"Munsa lock types are 'cr', 'pr','cw', 'pw', or 'ex'.\n");
exit (FATAL_ERROR);
}
continue;
}
#endif /* defined(MUNSA) */
#if defined(TTY)
if (match ("flow=")) {
flow_str = string;
if (match("none")) {
flow_type = FLOW_NONE; /* No flow control. */
} else if (match("cts_rts")) {
flow_type = CTS_RTS; /* CTS/RTS flow control. */
} else if (match("xon_xoff")) {
flow_type = XON_XOFF; /* XON/XOFF flow control. */
} else {
LogMsg (efp, logLevelCrit, 0,
"Flow types are 'none', 'cts_rts', or 'xon_xoff'.\n");
exit (FATAL_ERROR);
}
continue;
}
#endif /* defined(TTY) */
if (match ("if=")) {
input_file = string;
continue;
}
if (match ("of=")) {
output_file = string;
continue;
}
if (match ("pf=")) {
pattern_file = string;
user_pattern = TRUE;
continue;
}
if (match ("log=")) {
log_file = string;
continue;
}
if (match ("iodir=")) {
if (match ("for")) {
io_dir = FORWARD;
} else if (match ("rev")) {
io_dir = REVERSE;
} else {
LogMsg (efp, logLevelCrit, 0,
"Valid I/O directions are: 'forward' or 'reverse'.\n");
exit (FATAL_ERROR);
}
continue;
}
if (match ("iomode=")) {
if (match ("copy")) {
dispose_mode = KEEP_FILE;
io_mode = COPY_MODE;
} else if (match ("test")) {
io_mode = TEST_MODE;
} else if (match ("verify")) {
io_mode = VERIFY_MODE;
verify_only = TRUE;
} else {
LogMsg (efp, logLevelCrit, 0,
"Valid I/O modes are: 'copy', 'test', or verify.\n");
exit (FATAL_ERROR);
}
continue;
}
if (match ("iotype=")) {
if (match ("random")) {
io_type = RANDOM_IO;
} else if (match ("sequential")) {
io_type = SEQUENTIAL_IO;
} else {
LogMsg (efp, logLevelCrit, 0,
"Valid I/O types are: 'random' or 'sequential'.\n");
exit (FATAL_ERROR);
}
continue;
}
/*
* Flags which apply to read and write of a file.
*
* NOTE: I'm not sure all of flags applying to write only!
*/
if (match ("flags=")) {
floop:
if (match(","))
goto floop;
if (*string == '\0')
continue;
#if defined(O_EXCL)
if (match("excl")) {
open_flags |= O_EXCL; /* Exclusive open. */
goto floop;
}
#endif /* defined(O_EXCL) */
#if defined(O_NDELAY)
if (match("ndelay")) {
open_flags |= O_NDELAY; /* Non-delay open. */
goto floop;
}
#endif /* defined(O_NDELAY) */
#if defined(O_NONBLOCK)
if (match("nonblock")) {
open_flags |= O_NONBLOCK; /* Non-blocking open. */
goto floop;
}
#endif /* defined(O_NONBLOCK) */
#if defined(O_CACHE)
if (match("cache")) { /* QNX specific. */
open_flags |= O_CACHE; /* Keep data in cache. */
goto floop;
}
#endif /* defined(O_CACHE) */
#if defined(O_DIRECT)
if (match("direct")) { /* LINUX specific. */
open_flags |= O_DIRECT; /* Direct disk access. */
goto floop;
}
#endif /* defined(O_DIRECT) */
#if defined(O_DIRECTIO)
if (match("direct")) { /* Tru64 Unix (zulu). */
open_flags |= O_DIRECTIO; /* Directio disk access. */
goto floop;
}
#endif /* defined(O_DIRECTIO) */
#if defined(O_FSYNC)
if (match("fsync")) { /* File integrity. */
open_flags |= O_FSYNC; /* Syncronize file I/O. */
goto floop;
}
#endif /* defined(O_FSYNC) */
#if defined(O_RSYNC)
if (match("rsync")) {
open_flags |= O_RSYNC; /* Read I/O integrity. */
goto floop; /* Use with O_DSYNC or O_SYNC. */
}
#endif /* defined(O_RSYNC) */
#if defined(O_SYNC)
if (match("sync")) {
open_flags |= O_SYNC; /* Synchronous all data access. */
goto floop; /* Sync data & file attributes. */
}
#endif /* defined(O_SYNC) */
#if defined(O_LARGEFILE)
if (match("large")) {
open_flags |= O_LARGEFILE; /* Enable large file support. */
goto floop; /* Same as _FILE_OFFSET_BITS=64 */
}
#endif /* defined(O_LARGEFILE) */
} /* End if "flags=" option. */
/*
* Flags which apply to opening a file for writes.
*/
if (match ("oflags=")) {
oloop:
if (match(","))
goto oloop;
if (*string == '\0')
continue;
#if defined(O_APPEND)
if (match("append")) {
wopen_flags |= O_APPEND; /* Append to file. */
goto oloop;
}
#endif /* defined(O_APPEND) */
#if defined(O_DEFER)
if (match("defer")) {
wopen_flags |= O_DEFER; /* Defer updates. */
goto oloop;
}
#endif /* defined(O_DEFER) */
#if defined(O_DSYNC)
if (match("dsync")) { /* Write data integrity. */
wopen_flags |= O_DSYNC; /* Synchronize data written. */
goto oloop;
}
#endif /* defined(O_DSYNC) */
#if defined(O_SYNC)
if (match("sync")) {
wopen_flags |= O_SYNC; /* Synchronous all data access. */
goto oloop; /* Sync data & file attributes. */
}
#endif /* defined(O_SYNC) */
#if defined(O_TRUNC)
if (match("trunc")) {
wopen_flags |= O_TRUNC; /* Truncate output file. */
goto floop;
}
#endif /* defined(O_TRUNC) */
#if defined(O_TEMP)
if (match("temp")) {
wopen_flags |= O_TEMP; /* Temporary file. */
goto oloop;
}
#endif /* defined(O_TEMP) */
} /* End of "oflags=" option. */
#if defined(TTY)
if (match ("parity=")) {
parity_str = string;
if (match("even")) {
parity_code = PARENB;
data_bits_code = CS7;
} else if (match("odd")) {
parity_code = (PARENB | PARODD);
data_bits_code = CS7;
} else if (match("none")) {
parity_code = 0;
data_bits_code = CS8;
#if defined(_QNX_SOURCE)
} else if (match("mark")) {
parity_code = (PARENB | PARODD | PARSTK);
data_bits_code = CS7;
} else if (match("space")) {
parity_code = (PARENB | PARSTK);
data_bits_code = CS7;
#endif /* defined(_QNX_SOURCE) */
} else {
#if defined(_QNX_SOURCE)
LogMsg (efp, logLevelCrit, 0,
"Valid parity settings are: even, odd, mark, space, or none.\n");
#else /* !defined(_QNX_SOURCE) */
LogMsg (efp, logLevelCrit, 0,
"Valid parity settings are: even, odd, or none.\n");
#endif /* defined(_QNX_SOURCE) */
exit (FATAL_ERROR);
}
if (data_bits_code == CS7) {
if (pattern == (u_int32)DEFAULT_PATTERN) {
pattern = (u_int32)ASCII_PATTERN;
} else {
pattern &= (u_int32)0x77777777; /* Strip off parity bits. */
}
}
continue;
}
#endif /* defined(TTY) */
if (match ("oncerr=")) {
if (match("abort")) {
oncerr_action = ABORT;
} else if (match("continue")) {
oncerr_action = CONTINUE;
} else {
LogMsg (efp, logLevelCrit, 0,
"On error actions are 'abort' or 'continue'.\n");
exit (FATAL_ERROR);
}
continue;
}
if (match ("passes=")) {
pass_limit = number(ANY_RADIX);
if (pass_limit == 0) {
LogMsg (efp, logLevelCrit, 0,
"Please specify a pass limit greater than zero!\n");
exit (FATAL_ERROR);
}
continue;
}
if (match ("pattern=")) { /* TODO: Needs to be cleaned up. */
int size = strlen(string);
if (size == 0) {
LogMsg (efp, logLevelCrit, 0,
"Please specify pattern of: { hex-pattern | incr | string }\n");
exit (FATAL_ERROR);
}
user_pattern = TRUE;
if (match ("incr")) { /* Incrementing pattern. */
int v, size = 256;
u_char *buffer = (u_char *) myalloc (size, 0);
u_char *bp = buffer;
for (v = 0; v < size; v++) {
*bp++ = v;
}
incr_pattern = TRUE;
setup_pattern (buffer, size);
} else if ( (size == 3) && match("iot") ) {
iot_pattern = TRUE;
/* Allocate pattern buffer after parsing. */
} else if ( IS_HexString(string) && (size <= 10) ) {
/* valid strings: XXXXXXXX or 0xXXXXXXXX */
pattern = (u_int32)number(HEX_RADIX);
} else { /* Presume ASCII string for data pattern. */
u_char *buffer = (u_char *) myalloc (size, 0);
size = StrCopy (buffer, string, size);
pattern_size = size;
pattern_string = string;
setup_pattern (buffer, size);
}
continue;
}
if (match ("prefix=")) {
prefix_size = strlen(string);
if (prefix_size == 0) {
LogMsg (efp, logLevelCrit, 0,
"Please specify a non-empty prefix string!\n");
exit (FATAL_ERROR);
} else if (prefix_size > BLOCK_SIZE) {
LogMsg (efp, logLevelCrit, 0,
"Please specify a prefix string < %d bytes!\n",
prefix_size);
exit (FATAL_ERROR);
}
prefix_string = Malloc(++prefix_size); /* plus NULL! */
(void)strcpy(prefix_string, string);
continue;
}
if (match ("position=")) {
file_position = (off_t)large_number(ANY_RADIX);
user_position = TRUE;
continue;
}
if (match ("procs=")) {
num_procs = (u_short)number(ANY_RADIX);
if (num_procs > MAX_PROCS) {
LogMsg (efp, logLevelCrit, 0,
"Please limit procs to <= %d!\n", MAX_PROCS);
exit (FATAL_ERROR);
}
continue;
}
#if defined(HP_UX)
if (match ("qdepth=")) {
qdepth = (u_int)number(ANY_RADIX);
if (qdepth > SCSI_MAX_Q_DEPTH) {
LogMsg (efp, logLevelCrit, 0,
"Please specify a SCSI queue depth <= %d!\n", SCSI_MAX_Q_DEPTH);
exit (FATAL_ERROR);
}
continue;
}
#endif /* defined(HP_UX) */
if (match ("rseed=")) {
random_seed = (u_int)number(ANY_RADIX);
user_rseed = TRUE;
continue;
}
if (match ("runtime=")) {
user_runtime = string;
runtime = time_value();
if (!record_limit) {
record_limit = INFINITY;
}
continue;
}
if (match ("seek=")) {
seek_count = number(ANY_RADIX);
continue;
}
if (match ("skip=")) {
skip_count = number(ANY_RADIX);
continue;
}
if (match ("slice=")) {
slice_num = (u_short)number(ANY_RADIX);
if ( (slice_num < 1) || (slice_num > MAX_SLICES) ) {
LogMsg (efp, logLevelCrit, 0,
"Please limit slice to (1 - %d)!\n", MAX_SLICES);
exit (FATAL_ERROR);
}
continue;
}
if (match ("slices=")) {
num_slices = (u_short)number(ANY_RADIX);
if (num_slices > MAX_SLICES) {
LogMsg (efp, logLevelCrit, 0,
"Please limit maximum slices to <= %d!\n", MAX_SLICES);
exit (FATAL_ERROR);
}
continue;
}
if (match ("step=")) {
step_offset = (off_t)large_number(ANY_RADIX);
continue;
}
#if defined(TTY)
if (match ("speed=")) {
speed_str = string;
baud_rate = (u_int32) number(ANY_RADIX);
if (setup_baud_rate (baud_rate) == FAILURE) {
exit (FATAL_ERROR);
}
continue;
}
if (match ("timeout=")) {
tty_timeout = (u_short)number(ANY_RADIX);
continue;
}
if (match ("ttymin=")) {
tty_minflag = TRUE;
tty_minimum = (u_short)number(ANY_RADIX);
continue;
}
#endif /* defined(TTY) */
if (match ("dtype=")) {
struct dtype *dtp;
if ((dtp = setup_device_type (string)) == NULL) {
exit (FATAL_ERROR);
}
input_dtype = output_dtype = dtp;
continue;
}
if (match ("idtype=")) {
if ((input_dtype = setup_device_type (string)) == NULL) {
exit (FATAL_ERROR);
}
continue;
}
if (match ("odtype=")) {
if ((output_dtype = setup_device_type (string)) == NULL) {
exit (FATAL_ERROR);
}
continue;
}
if (match ("trigger=")) {
if ((trigger = check_trigger_type (string)) == TRIGGER_INVALID) {
exit (FATAL_ERROR);
}
continue;
}
if (match ("vrecords=")) {
volume_records = number(ANY_RADIX);
continue;
}
if (match ("volumes=")) {
multi_flag = TRUE;
volumes_flag = TRUE;
volume_limit = number(ANY_RADIX);
continue;
}
if (match ("help")) {
dthelp();
}
if (match ("version")) {
dtversion();
}
LogMsg (efp, logLevelCrit, 0,
"Invalid option '%s' specified, use 'help' for valid options.\n",
string);
exit (FATAL_ERROR);
}
#if !defined(AIO)
if (aio_flag) {
Printf ("Warning, POSIX AIO is NOT supported on this platform, disabling AIO!\n");
aio_flag = FALSE;
}
#endif /* !defined(AIO) */
}
/************************************************************************
* *
* match() Match string against next input argument. *
* *
* Inputs: s = Pointer to string to match against. *
* *
* Outputs: Returns TRUE/FALSE = Match/No Match *
* string = Updated input argument pointer. *
* *
************************************************************************/
int
match (char *s)
{
char *cs;
cs = string;
while (*cs++ == *s) {
if (*s++ == '\0') {
goto true; /* I didn't write this junk. */
}
}
if (*s != '\0') {
return (FALSE);
}
true:
cs--;
string = cs;
return (TRUE);
}
/************************************************************************
* *
* number() Converts ASCII string into numeric value. *
* *
* Inputs: base = The base for numeric conversions. *
* *
* Outputs: Returns converted number, exits on invalid numbers. *
* *
************************************************************************/
static u_long
number (int base)
{
char *eptr, *str = string;
u_long value;
value = CvtStrtoValue (str, &eptr, base);
if (*eptr != '\0') {
LogMsg (efp, logLevelCrit, 0,
"Invalid character detected in number: '%c'\n", *eptr);
exit (FATAL_ERROR);
}
return (value);
}
static large_t
large_number(int base)
{
char *eptr, *str = string;
large_t value;
value = CvtStrtoLarge (str, &eptr, base);
if (*eptr != '\0') {
LogMsg (efp, logLevelCrit, 0,
"Invalid character detected in number: '%c'\n", *eptr);
exit (FATAL_ERROR);
}
return (value);
}
static time_t
time_value(void)
{
char *eptr, *str = string;
time_t value;
value = CvtTimetoValue (str, &eptr);
if (*eptr != '\0') {
LogMsg (efp, logLevelCrit, 0,
"Invalid character detected in time string: '%c'\n", *eptr);
exit (FATAL_ERROR);
}
return (value);
}
/************************************************************************
* *
* report_error() - Report system error information. *
* *
* Inputs: error_info = Additional error info for perror. *
* record_flag = Controls reporting error/time info. *
* *
************************************************************************/
void
report_error(
char *error_info,
int record_flag)
{
struct dinfo *dip = active_dinfo;
int saved_errno = errno;
if (dip) dip->di_errno = errno;
#if defined(LOG_DIAG_INFO)
if (logdiag_flag) {
(void)sprintf(msg_buffer, "%s: '%s', errno = %d - %s\n",
cmdname, error_info, errno, strerror(errno));
LogDiagMsg(msg_buffer);
}
#endif /* defined(LOG_DIAG_INFO) */
LogMsg (efp, logLevelCrit, 0,
"'%s', errno = %d - %s\n", error_info, errno, strerror(errno));
exit_status = FAILURE;
if (record_flag) {
char fmt[SMALL_BUFFER_SIZE];
error_time = time((time_t *) 0);
/* Two flavors of ctime_r(), this is a problem! */
if (ctime_r(&error_time, fmt) == NULL) {
//perror("ctime_r()"); /* wrong ctime_r()? */
Fprintf("Error number %lu occurred on %s",
++error_count, ctime(&error_time));
} else {
/* ctime_r() adds a newline '\n' to time stamp. */
Fprintf("Error number %lu occurred on %s",
++error_count, fmt);
}
}
errno = saved_errno;
}
/*
* General purpose display record information function.
*/
void
report_record(
struct dinfo *dip,
u_long files,
u_long records,
u_int32 lba,
enum test_mode mode,
void *buffer,
size_t bytes )
{
char msg[STRING_BUFFER_SIZE];
char *bp = msg;
if (dip->di_dtype->dt_dtype == DT_TAPE) {
bp += sprintf(bp, "File #%lu, ", files);
}
bp += sprintf(bp, "Record #%lu", records);
#if 0
if (lbdata_flag || iot_pattern || aio_flag) {
#else
if (lba != NO_LBA) {
#endif
bp += sprintf(bp, " (lba %u), ", lba);
} else {
bp += sprintf(bp, ", ");
}
bp += sprintf(bp, "%s %ld bytes %s buffer %#lx...\n",
(mode == READ_MODE) ? "Reading" : "Writing",
(long)bytes,
(mode == READ_MODE) ? "into" : "from",
(u_long)buffer);
Printf (msg);
}
/*
* terminate() - Terminate program with specified exit code.
*
* Inputs:
* code = The exit code or signal number if kill done.
*/
void
terminate (int code)
{
struct dinfo *dip = active_dinfo;
int status;
/*
* If we enter here more than once, just exit to avoid
* possible recursion. kernel should do I/O rundown :-)
*/
if (terminating_flag) {
if (core_dump && (code != SUCCESS)) {
abort(); /* Generate a core dump. */
} else {
exit (code); /* get outta here now... */
}
} else {
terminating_flag++; /* Show we're terminating. */
}
/*
* If we're here due to our timer expiring, and we were writing
* a file, then flush the data (to regular file).
*/
if ( (code == SIGALRM) && dip && (dip->di_mode == WRITE_MODE) ) {
/*
* We may get alarm while open() is in progress, so handle.
*/
if ( !dip->di_closing && (dip->di_fd != NoFd) ) {
(void) (*dip->di_funcs->tf_flush_data)(dip);
}
}
/*
* If an alarm timer is active, cancel it and calculate
* the elapsed run time.
*/
if (TimerActive) {
elapsed_time = runtime - alarm(0);
TimerActive = FALSE;
code = exit_status; /* Set the exit status! */
}
/*
* We only come here for signals when executing multiple
* processes, so abort active procs and continue waiting.
*/
if ((num_procs || num_slices) && child_pid) {
abort_procs(); /* Abort any active procs. */
return;
}
#if !defined(__MSDOS__) || defined(__NUTC__)
if (debug_flag && (code == SIGCHLD)) {
Printf ("Child process exited prematurely, parent exiting...\n");
} else
#endif
/*
* Report stats before waiting for the child process.
*/
#if !defined(__MSDOS__) || defined(__NUTC__)
(void) signal (SIGCHLD, SIG_DFL); /* Don't deliver SIGCHLD now. */
#endif
/*
* Close file, which for AIO waits for outstanding I/O's,
* before reporting statistics so they'll be correct.
*/
if (dip) {
dip->di_proc_eei = FALSE;
status = (*dip->di_funcs->tf_close)(dip);
if (status != SUCCESS) code = status;
}
gather_stats(dip); /* Gather the device statistics. */
gather_totals(); /* Update the total statistics. */
report_stats(dip, TOTAL_STATS);
if (child_pid) { /* Always wait for child status. */
pid_t wpid = (pid_t) 0;
/*
* If exiting with error, ensure the child gets killed.
*/
if (code != SUCCESS) {
(void) kill (child_pid, SIGTERM);
}
if (debug_flag) {
Printf ("Waiting for child PID %d to exit...\n", child_pid);
}
#if defined(_BSD)
do {
if ((wpid = wait (&child_status)) == FAILURE) {
report_error ("wait", TRUE);
break;
}
} while (wpid != child_pid);
if ( (code == SUCCESS) && (wpid != FAILURE) ) {
code = WEXITSTATUS (child_status);
}
#else /* !defined(_BSD) */
if ((wpid = waitpid (child_pid, &child_status, 0)) == FAILURE) {
report_error ("waitpid", TRUE);
} else if (code == SUCCESS) {
code = WEXITSTATUS (child_status);
}
#endif /* defined(_BSD) */
if (debug_flag && (wpid != FAILURE)) {
Printf("Child PID %d, exited with status = %d\n",
wpid, WEXITSTATUS(child_status));
}
}
/*
* Delete the output file, if requested to do so.
*/
if (output_file && (io_mode == TEST_MODE) &&
(dispose_mode == DELETE_FILE) &&
(dip && dip->di_dtype && (dip->di_dtype->dt_dtype == DT_REGULAR)) ) {
(void) delete_file (dip);
}
#if defined(LOG_DIAG_INFO)
if (logdiag_flag) {
sprintf(msg_buffer, "Finished: %s", cmd_line);
LogDiagMsg(msg_buffer);
}
#endif /* defined(LOG_DIAG_INFO) */
if (!eof_status && (code == END_OF_FILE)) {
code = SUCCESS; /* Map end-of-file status to Success! */
}
if (debug_flag && (code != SUCCESS)) {
Printf ("Exiting with status code %d...\n", code);
}
if (core_dump && (code != SUCCESS) && (code != END_OF_FILE)) {
abort(); /* Generate a core dump. */
}
if (forked_flag && tdelay_count && (child_pid == (pid_t) 0)) {
(void)sleep (tdelay_count); /* Allow parent to wait for us. */
}
exit (code);
}
int
nofunc (struct dinfo *dip)
{
return (SUCCESS);
}
static char *multi_prompt =
"\nPlease insert volume #%d in drive %s, press ENTER when ready to proceed: \007";
static char *multi_nready =
"The drive is NOT ready or encountered an error, Retry operation (Yes): \007";
int
HandleMultiVolume (struct dinfo *dip)
{
int status;
status = RequestMultiVolume (dip, FALSE, dip->di_oflags);
if (status == FAILURE) return (status);
if (dip->di_mode == READ_MODE) {
dip->di_volume_bytes = (large_t)(dip->di_dbytes_read + total_bytes_read);
if (verbose_flag) {
if (dip->di_dtype->dt_dtype == DT_TAPE) {
Print (" [ Continuing in file #%lu, record #%lu, bytes read so far " LUF "... ]\n",
(dip->di_files_read + 1), (dip->di_records_read + 1), dip->di_volume_bytes);
} else {
Print (" [ Continuing at record #%lu, bytes read so far " LUF "... ]\n",
(dip->di_records_read + 1), dip->di_volume_bytes);
}
}
dip->di_vbytes_read = (v_large) 0;
} else {
dip->di_volume_bytes = (large_t)(dip->di_dbytes_written + total_bytes_written);
if (verbose_flag) {
if (dip->di_dtype->dt_dtype == DT_TAPE) {
Print (" [ Continuing in file #%lu, record #%lu, bytes written so far " LUF "... ]\n",
(dip->di_files_written + 1), (dip->di_records_written + 1), dip->di_volume_bytes);
} else {
Print (" [ Continuing at record #%lu, bytes written so far " LUF "... ]\n",
(dip->di_records_written + 1), dip->di_volume_bytes);
}
}
dip->di_vbytes_written = (v_large) 0;
}
(void)fflush(ofp);
media_changed = TRUE;
dip->di_volume_records = 0;
if (exit_status == END_OF_FILE) {
exit_status = SUCCESS; /* Ensure END_OF_FILE status is reset! */
}
return (status);
}
int
RequestFirstVolume (struct dinfo *dip, int oflags)
{
int status;
multi_volume = 0;
status = RequestMultiVolume (dip, TRUE, oflags);
dip->di_volume_bytes = (large_t) 0;
dip->di_volume_records = 0;
return (status);
}
int
RequestMultiVolume (struct dinfo *dip, bool reopen, int oflags)
{
struct dtfuncs *dtf = dip->di_funcs;
char buffer[256];
char *bp = buffer;
FILE *fp;
int saved_exit_status;
u_long saved_error_count;
int status;
if (terminating_flag) return (FAILURE);
if ( (status = (*dtf->tf_close)(dip)) == FAILURE) {
return (status);
}
if ( (fp = fopen("/dev/tty", "r+")) == NULL) {
report_error("open of /dev/tty failed", FALSE);
return (FAILURE);
}
multi_volume++;
(void)sprintf(bp, multi_prompt, multi_volume, dip->di_dname);
(void) fputs (bp, fp); fflush(fp);
if (fgets (bp, sizeof(buffer), fp) == NULL) {
Print ("\n");
status = FAILURE; /* eof or an error */
return (status);
}
saved_error_count = error_count;
saved_exit_status = exit_status;
/*
* This is an important step, so allow the user to retry on errors.
*/
do {
if (!reopen) {
status = (*dtf->tf_open)(dip, oflags);
} else {
status = (*dtf->tf_reopen_file)(dip, oflags);
}
if (status == SUCCESS) {
#if !defined(__NUTC__) && !defined(__QNXNTO__) && !defined(AIX)
if (dip->di_dtype->dt_dtype == DT_TAPE) {
status = DoRewindTape (dip);
if (status == FAILURE) {
(void)(*dtf->tf_close)(dip);
}
}
#endif /* !defined(__NUTC__) && !defined(__QNX_NTO__) && !defined(AIX) */
}
if (status == FAILURE) {
(void) fputs (multi_nready, fp); fflush(fp);
if (fgets (bp, sizeof(buffer), fp) == NULL) {
Print ("\n");
break;
}
if ( (bp[0] == 'N') || (bp[0] == 'n') ) {
break;
}
error_count = saved_error_count;
exit_status = saved_exit_status;
} else {
break; /* device is ready! */
}
} while (status == FAILURE);
(void)fclose(fp);
return (status);
}
syntax highlighted by Code2HTML, v. 0.9.1