Initial source for rwtest/doio in LTP
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..5747b6e
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,21 @@
+This is an attempt at a credits-file in the style begun by Linux
+Torvalds and the Linux project. It is sorted by name and formatted
+to allow easy grepping and beautification by scripts. The fields
+are: name (N), email (E), web-address (W), PGP key ID and
+fingerprint (P), description (D), and snail-mail address (S).
+
+N: Rich Logan
+D: growfiles
+S: SGI, Eagan MN
+
+N: Mark Maule
+D: doio and iogen
+S: SGI, Eagan MN
+
+N: Glen Overby
+D: doio, iogen, and pan
+S: SGI, Eagan MN
+
+N: Dean Roehrich
+D: doio, iogen, and pan
+S: SGI, Eagan MN
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..78f1d52
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,14 @@
+Building and Installation of Test Tools.
+
+At the time of release, simplicity of the released code was considered
+paramount to understanding and adoption. For this reason no effort was
+put into engineering complicated and convoluted build processes that
+were not necessary. Instead, a minimal approach was taken and the
+build process reflects that. Simply:
+
+cd <tool>
+make
+
+At the time of release, <tool> is one of doio or pan. The build process
+builds the tools in question and leaves them among the sources. There
+are no install routines, yet.
diff --git a/doio/Makefile b/doio/Makefile
new file mode 100644
index 0000000..18f7fb8
--- /dev/null
+++ b/doio/Makefile
@@ -0,0 +1,27 @@
+
+CFLAGS+=
+LDFLAGS+=
+TARGETS=doio iogen rwtest growfiles
+
+SRCS=$(wildcard *.c)
+OBJS=$(patsubst %.c,%.o,$(SRCS))
+
+all: $(TARGETS)
+
+doio: doio.o random_range.o string_to_tokens.o pattern.o write_log.o
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+growfiles: growfiles.o dataascii.o open_flags.o tlibio.o random_range.o \
+ file_lock.o datapid.o databin.o forker.o string_to_tokens.o
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+iogen: iogen.o str_to_bytes.o string_to_tokens.o random_range.o \
+ open_flags.o
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+rwtest: rwtest.ks doio iogen
+ cp rwtest.ks rwtest
+ chmod a+rx rwtest
+
+clean:
+ rm -f $(OBJS) $(TARGETS)
diff --git a/doio/README b/doio/README
new file mode 100644
index 0000000..28ad97c
--- /dev/null
+++ b/doio/README
@@ -0,0 +1,79 @@
+
+$Id: README,v 1.1 2000/05/05 19:34:50 whr Exp $
+
+This file contains some very basic information on:
+ iogen/doio and rwtest
+ growfiles
+
+All tools use the -h flag for printing some form of help (sometimes voluminous).
+They are extremely configurable; the examples below are some common uses.
+Read the help and experiment! This testing tools were originally written
+to test UNICOS's NC1 and IRIX XFS filesystems.
+
+
+IOGEN & DOIO
+=============
+
+This is a pair of programs that does basic I/O operations on a set of files.
+The file offset, I/O length, I/O operation, and what open(2) flags are
+selected randomly from a pre-defined or commandline given set. All data
+written can be verified (this is the usual method).
+
+rwtest is a shell script that is a wrapper of iogen and doio.
+
+Examples:
+---------
+# run forever: 8 process - using record locks
+iogen -i 0 100000b:doio_1 | doio -av -n 8 -m 1000
+
+# run forever: 8 process - using record locks
+iogen -i 0 100000b:doio_2 | doio -akv -n 8 -m 1000
+
+# run forever: max i/o 64b, to /tmp/rwtest01%f, which 500b in size
+rwtest -c -i 0 -T 64b 500b:/tmp/rwtest01%f
+
+
+
+GROWFILES
+=============
+
+Growfiles will create and truncate files in gradual steps using write, and
+lseek. All system calls are checked for proper returns. The writes or the
+whole file content can be verified. It can cause disk fragmentation.
+
+
+Examples:
+---------
+growfiles -E output:
+# run forever: writes of 4090 bytes then on every 100 iterval
+# truncate file by 408990 bytes. Done to 200 files in dir1.
+growfiles -i 0 -g 4090 -T 100 -t 408990 -l -C 10 -c 1000 -d dir1 -S 200
+
+# same as above with writes of 5000 bytes and truncs of 499990
+growfiles -i 0 -g 5000 -T 100 -t 499990 -l -C 10 -c 1000 -d dir2 -S 200
+
+# runs forever: beats on opens and closes of file ocfile - no io
+growfiles -i 0 -g 0 -c 0 -C 0 ocfile
+
+# writes 4096 to files until 50 blocks are written
+growfiles -i 0 -g 4096 -B 50b file1 file2
+
+# write one byte to 750 files in gdir then unlinks them
+growfiles -g 1 -C 0 -d gdir -u -S 750
+
+# run 30 secs: random iosize, random lseek up to eof
+# Only valid for one growfile process per file.
+growfiles -r 1-5000 -R 0--1 -i 0 -L 30 -C 1 g_rand1 g_rand2
+
+# run 30 secs: grow by lseek then write single byte, trunc every 10 itervals
+growfiles -g 5000 -wlu -i 0 -L 30 -C 1 -T 10 g_sleek1 g_lseek2
+
+# run forever: 5 copies of random iosize, random lseek to beyond eof,
+# rand io types doing a trunc every 5 iterations, with unlinks.
+growfiles -i0 -r 1-50000 -R 0--2 -I r -C1 -l -n5 -u -U 100-200 gf_rana gf_ranb
+
+# run forever: 5 copies of random iosize, random lseek to beyond eof,
+# random open flags, rand io types doing a trunc every 10 iterations.
+growfiles -i0 -r 1-50000 -R 0--2 -o random -I r -C0 -l -T 20 -uU100-200 -n 5 gf_rand1 gf_rand2
+
+
diff --git a/doio/doio.c b/doio/doio.c
new file mode 100644
index 0000000..dfe4665
--- /dev/null
+++ b/doio/doio.c
@@ -0,0 +1,5439 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ * doio - a general purpose io initiator with system call and
+ * write logging. See doio.h for the structure which defines
+ * what doio requests should look like.
+ *
+ * Currently doio can handle read,write,reada,writea,ssread,
+ * sswrite, and many varieties of listio requests.
+ * For disk io, if the O_SSD flag is set doio will allocate
+ * the appropriate amount of ssd and do the transfer - thus, doio
+ * can handle all of the primitive types of file io.
+ *
+ * programming
+ * notes:
+ * -----------
+ * messages should generally be printed using doio_fprintf().
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#ifdef CRAY
+#include <sys/iosw.h>
+#endif
+#ifdef sgi
+#include <aio.h> /* for aio_read,write */
+#include <inttypes.h> /* for uint64_t type */
+#include <siginfo.h> /* signal handlers & SA_SIGINFO */
+#endif
+#ifndef CRAY
+#include <sys/uio.h> /* for struct iovec (readv)*/
+#include <sys/mman.h> /* for mmap(2) */
+#include <sys/ipc.h> /* for i/o buffer in shared memory */
+#include <sys/shm.h> /* for i/o buffer in shared memory */
+#endif
+#include <sys/wait.h>
+#ifdef CRAY
+#include <sys/listio.h>
+#include <sys/panic.h>
+#endif
+#include <sys/time.h> /* for delays */
+
+#include "doio.h"
+#include "write_log.h"
+#include "random_range.h"
+
+#ifndef O_SSD
+#define O_SSD 0 /* so code compiles on a CRAY2 */
+#endif
+
+#ifdef sgi
+#define UINT64_T uint64_t
+#else
+#define UINT64_T unsigned long
+#endif
+
+#ifndef O_PARALLEL
+#define O_PARALLEL 0 /* so O_PARALLEL may be used in expressions */
+#endif
+
+#define PPID_CHECK_INTERVAL 5 /* check ppid every <-- iterations */
+#define MAX_AIO 256 /* maximum number of async I/O ops */
+#ifdef _CRAYMPP
+#define MPP_BUMP 16 /* page un-alignment for MPP */
+#else
+#define MPP_BUMP 0
+#endif
+
+
+#define SYSERR strerror(errno)
+
+/*
+ * getopt() string of supported cmdline arguments.
+ */
+
+#define OPTS "aC:d:ehm:n:kr:w:vU:V:M:N:"
+
+#define DEF_RELEASE_INTERVAL 0
+
+/*
+ * Flags set in parse_cmdline() to indicate which options were selected
+ * on the cmdline.
+ */
+
+int a_opt = 0; /* abort on data compare errors */
+int e_opt = 0; /* exec() after fork()'ing */
+int C_opt = 0; /* Data Check Type */
+int d_opt = 0; /* delay between operations */
+int k_opt = 0; /* lock file regions during writes */
+int m_opt = 0; /* generate periodic messages */
+int n_opt = 0; /* nprocs */
+int r_opt = 0; /* resource release interval */
+int w_opt = 0; /* file write log file */
+int v_opt = 0; /* verify writes if set */
+int U_opt = 0; /* upanic() on varios conditions */
+int V_opt = 0; /* over-ride default validation fd type */
+int M_opt = 0; /* data buffer allocation types */
+char TagName[40]; /* name of this doio (see Monster) */
+
+
+/*
+ * Misc globals initialized in parse_cmdline()
+ */
+
+char *Prog = NULL; /* set up in parse_cmdline() */
+int Upanic_Conditions; /* set by args to -U */
+int Release_Interval; /* arg to -r */
+int Nprocs; /* arg to -n */
+char *Write_Log; /* arg to -w */
+char *Infile; /* input file (defaults to stdin) */
+int *Children; /* pids of child procs */
+int Nchildren = 0;
+int Nsiblings = 0; /* tfork'ed siblings */
+int Execd = 0;
+int Message_Interval = 0;
+int Npes = 0; /* non-zero if built as an mpp multi-pe app */
+int Vpe = -1; /* Virtual pe number if Npes >= 0 */
+int Reqno = 1; /* request # - used in some error messages */
+int Reqskipcnt = 0; /* count of I/O requests that are skipped */
+int Validation_Flags;
+char *(*Data_Check)(); /* function to call for data checking */
+int (*Data_Fill)(); /* function to call for data filling */
+int Nmemalloc = 0; /* number of memory allocation strategies */
+int delayop = 0; /* delay between operations - type of delay */
+int delaytime = 0; /* delay between operations - how long */
+
+struct wlog_file Wlog;
+
+int active_mmap_rw = 0; /* Indicates that mmapped I/O is occurring. */
+ /* Used by sigbus_action() in the child doio. */
+int havesigint = 0;
+
+#define SKIP_REQ -2 /* skip I/O request */
+
+#define NMEMALLOC 32
+#define MEM_DATA 1 /* data space */
+#define MEM_SHMEM 2 /* System V shared memory */
+#define MEM_T3ESHMEM 3 /* T3E Shared Memory */
+#define MEM_MMAP 4 /* mmap(2) */
+
+#define MEMF_PRIVATE 0001
+#define MEMF_AUTORESRV 0002
+#define MEMF_LOCAL 0004
+#define MEMF_SHARED 0010
+
+#define MEMF_FIXADDR 0100
+#define MEMF_ADDR 0200
+#define MEMF_AUTOGROW 0400
+#define MEMF_FILE 01000 /* regular file -- unlink on close */
+#define MEMF_MPIN 010000 /* use mpin(2) to lock pages in memory */
+
+struct memalloc {
+ int memtype;
+ int flags;
+ int nblks;
+ char *name;
+ void *space; /* memory address of allocated space */
+ int fd; /* FD open for mmaping */
+ int size;
+} Memalloc[NMEMALLOC];
+
+/*
+ * Global file descriptors
+ */
+
+int Wfd_Append; /* for appending to the write-log */
+int Wfd_Random; /* for overlaying write-log entries */
+
+/*
+ * Structure for maintaining open file test descriptors. Used by
+ * alloc_fd().
+ */
+
+struct fd_cache {
+ char c_file[MAX_FNAME_LENGTH+1];
+ int c_oflags;
+ int c_fd;
+ long c_rtc;
+#ifdef sgi
+ int c_memalign; /* from F_DIOINFO */
+ int c_miniosz;
+ int c_maxiosz;
+#endif
+#ifndef CRAY
+ void *c_memaddr; /* mmapped address */
+ int c_memlen; /* length of above region */
+#endif
+};
+
+#define FD_ALLOC_INCR 32 /* allocate this many fd_map structs */
+ /* at a time */
+
+/*
+ * Globals for tracking Sds and Core usage
+ */
+
+char *Memptr; /* ptr to core buffer space */
+int Memsize; /* # bytes pointed to by Memptr */
+ /* maintained by alloc_mem() */
+
+int Sdsptr; /* sds offset (always 0) */
+int Sdssize; /* # bytes of allocated sds space */
+ /* Maintained by alloc_sds() */
+char Host[16];
+char Pattern[128];
+int Pattern_Length;
+
+/*
+ * Signal handlers, and related globals
+ */
+
+void sigint_handler(); /* Catch SIGINT in parent doio, propagate
+ * to children, does not die. */
+
+void die_handler(); /* Bad sig in child doios, exit 1. */
+void cleanup_handler(); /* Normal kill, exit 0. */
+
+#ifndef CRAY
+void sigbus_handler(); /* Handle sigbus--check active_mmap_rw to
+ decide if this should be a normal exit. */
+#endif
+
+void cb_handler(); /* Posix aio callback handler. */
+void noop_handler(); /* Delayop alarm, does nothing. */
+char *hms();
+char *format_rw();
+char *format_sds();
+char *format_listio();
+char *check_file();
+int doio_fprintf(FILE *stream, char *format, ...);
+void doio_upanic();
+void doio();
+void help();
+void doio_delay();
+int alloc_fd( char *, int );
+int alloc_mem( int );
+int do_read( struct io_req * );
+int do_write( struct io_req * );
+int do_rw( struct io_req * );
+int do_sync( struct io_req * );
+int usage( FILE * );
+int aio_unregister( int );
+int pattern_check(char *buf, int buflen, char *pat, int patlen, int patshift);
+int pattern_fill(char *buf, int buflen, char *pat, int patlen, int patshift);
+int parse_cmdline( int, char **, char * );
+int lock_file_region( char *, int, int, int, int );
+struct fd_cache *alloc_fdcache(char *, int);
+
+/*
+ * Upanic conditions, and a map from symbolics to values
+ */
+
+#define U_CORRUPTION 0001 /* upanic on data corruption */
+#define U_IOSW 0002 /* upanic on bad iosw */
+#define U_RVAL 0004 /* upanic on bad rval */
+
+#define U_ALL (U_CORRUPTION | U_IOSW | U_RVAL)
+
+/*
+ * Name-To-Value map
+ * Used to map cmdline arguments to values
+ */
+struct smap {
+ char *string;
+ int value;
+};
+
+struct smap Upanic_Args[] = {
+ { "corruption", U_CORRUPTION },
+ { "iosw", U_IOSW },
+ { "rval", U_RVAL },
+ { "all", U_ALL },
+ { NULL, 0 }
+};
+
+struct aio_info {
+ int busy;
+ int id;
+ int fd;
+ int strategy;
+ volatile int done;
+#ifdef CRAY
+ struct iosw iosw;
+#endif
+#ifdef sgi
+ aiocb_t aiocb;
+ int aio_ret; /* from aio_return */
+ int aio_errno; /* from aio_error */
+#endif
+ int sig;
+ int signalled;
+ struct sigaction osa;
+};
+
+struct aio_info Aio_Info[MAX_AIO];
+
+struct aio_info *aio_slot();
+int aio_done( struct aio_info * );
+
+/* -C data-fill/check type */
+#define C_DEFAULT 1
+struct smap checkmap[] = {
+ { "default", C_DEFAULT },
+ { NULL, 0 },
+};
+
+/* -d option delay types */
+#define DELAY_SELECT 1
+#define DELAY_SLEEP 2
+#define DELAY_SGINAP 3
+#define DELAY_ALARM 4
+#define DELAY_ITIMER 5 /* POSIX timer */
+
+struct smap delaymap[] = {
+ { "select", DELAY_SELECT },
+ { "sleep", DELAY_SLEEP },
+#ifdef sgi
+ { "sginap", DELAY_SGINAP },
+#endif
+ { "alarm", DELAY_ALARM },
+ { NULL, 0 },
+};
+
+/******
+*
+* strerror() does similar actions.
+
+char *
+syserrno(int err)
+{
+ static char sys_errno[10];
+ sprintf(sys_errno, "%d", errno);
+ return(sys_errno);
+}
+
+******/
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ int i, pid, stat, ex_stat;
+#ifdef CRAY
+ sigset_t omask;
+#else
+ int omask;
+#endif
+ struct sigaction sa;
+
+ umask(0); /* force new file modes to known values */
+#if _CRAYMPP
+ Npes = sysconf(_SC_CRAY_NPES); /* must do this before parse_cmdline */
+ Vpe = sysconf(_SC_CRAY_VPE);
+#endif
+
+ TagName[0] = '\0';
+ parse_cmdline(argc, argv, OPTS);
+
+ random_range_seed(getpid()); /* initialize random number generator */
+
+ /*
+ * If this is a re-exec of doio, jump directly into the doio function.
+ */
+
+ if (Execd) {
+ doio();
+ exit(E_SETUP);
+ }
+
+ /*
+ * Stop on all but a few signals...
+ */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = sigint_handler;
+ sa.sa_flags = SA_RESETHAND; /* sigint is ignored after the */
+ /* first time */
+ for (i = 1; i <= NSIG; i++) {
+ switch(i) {
+#ifdef SIGRECOVERY
+ case SIGRECOVERY:
+ break;
+#endif
+#ifdef SIGCKPT
+ case SIGCKPT:
+#endif
+#ifdef SIGRESTART
+ case SIGRESTART:
+#endif
+ case SIGTSTP:
+ case SIGSTOP:
+ case SIGCONT:
+ case SIGCLD:
+ case SIGBUS:
+ case SIGSEGV:
+ case SIGQUIT:
+ break;
+ default:
+ sigaction(i, &sa, NULL);
+ }
+ }
+
+ /*
+ * If we're logging write operations, make a dummy call to wlog_open
+ * to initialize the write history file. This call must be done in
+ * the parent, to ensure that the history file exists and/or has
+ * been truncated before any children attempt to open it, as the doio
+ * children are not allowed to truncate the file.
+ */
+
+ if (w_opt) {
+ strcpy(Wlog.w_file, Write_Log);
+
+ if (wlog_open(&Wlog, 1, 0666) < 0) {
+ doio_fprintf(stderr,
+ "Could not create/truncate write log %s\n",
+ Write_Log);
+ exit(2);
+ }
+
+ wlog_close(&Wlog);
+ }
+
+ /*
+ * Malloc space for the children pid array. Initialize all entries
+ * to -1.
+ */
+
+ Children = (int *)malloc(sizeof(int) * Nprocs);
+ for (i = 0; i < Nprocs; i++) {
+ Children[i] = -1;
+ }
+
+ omask = sigblock(sigmask(SIGCLD));
+
+ /*
+ * Fork Nprocs. This [parent] process is a watchdog, to notify the
+ * invoker of procs which exit abnormally, and to make sure that all
+ * child procs get cleaned up. If the -e option was used, we will also
+ * re-exec. This is mostly for unicos/mk on mpp's, to ensure that not
+ * all of the doio's don't end up in the same pe.
+ *
+ * Note - if Nprocs is 1, or this doio is a multi-pe app (Npes > 1),
+ * jump directly to doio(). multi-pe apps can't fork(), and there is
+ * no reason to fork() for 1 proc.
+ */
+
+ if (Nprocs == 1 || Npes > 1) {
+ doio();
+ exit(0);
+ } else {
+ for (i = 0; i < Nprocs; i++) {
+ if ((pid = fork()) == -1) {
+ doio_fprintf(stderr,
+ "(parent) Could not fork %d children: %s (%d)\n",
+ i+1, SYSERR, errno);
+ exit(E_SETUP);
+ }
+
+ Children[Nchildren] = pid;
+ Nchildren++;
+
+ if (pid == 0) {
+ if (e_opt) {
+ char *exec_path;
+
+ exec_path = argv[0];
+ argv[0] = (char *)malloc(strlen(exec_path + 1));
+ sprintf(argv[0], "-%s", exec_path);
+
+ execvp(exec_path, argv);
+ doio_fprintf(stderr,
+ "(parent) Could not execvp %s: %s (%d)\n",
+ exec_path, SYSERR, errno);
+ exit(E_SETUP);
+ } else {
+ doio();
+ exit(E_SETUP);
+ }
+ }
+ }
+
+ /*
+ * Parent spins on wait(), until all children exit.
+ */
+
+ ex_stat = E_NORMAL;
+
+ while (Nprocs) {
+ if ((pid = wait(&stat)) == -1) {
+ if (errno == EINTR)
+ continue;
+ }
+
+ for (i = 0; i < Nchildren; i++)
+ if (Children[i] == pid)
+ Children[i] = -1;
+
+ Nprocs--;
+
+ if (WIFEXITED(stat)) {
+ switch (WEXITSTATUS(stat)) {
+ case E_NORMAL:
+ /* noop */
+ break;
+
+ case E_INTERNAL:
+ doio_fprintf(stderr,
+ "(parent) pid %d exited because of an internal error\n",
+ pid);
+ ex_stat |= E_INTERNAL;
+ break;
+
+ case E_SETUP:
+ doio_fprintf(stderr,
+ "(parent) pid %d exited because of a setup error\n",
+ pid);
+ ex_stat |= E_SETUP;
+ break;
+
+ case E_COMPARE:
+ doio_fprintf(stderr,
+ "(parent) pid %d exited because of data compare errors\n",
+ pid);
+
+ ex_stat |= E_COMPARE;
+
+ if (a_opt)
+ kill(0, SIGINT);
+
+ break;
+
+ case E_USAGE:
+ doio_fprintf(stderr,
+ "(parent) pid %d exited because of a usage error\n",
+ pid);
+
+ ex_stat |= E_USAGE;
+ break;
+
+ default:
+ doio_fprintf(stderr,
+ "(parent) pid %d exited with unknown status %d\n",
+ pid, WEXITSTATUS(stat));
+ ex_stat |= E_INTERNAL;
+ break;
+ }
+ } else if (WIFSIGNALED(stat) && WTERMSIG(stat) != SIGINT) {
+ doio_fprintf(stderr,
+ "(parent) pid %d terminated by signal %d\n",
+ pid, WTERMSIG(stat));
+
+ ex_stat |= E_SIGNAL;
+ }
+
+ fflush(NULL);
+ }
+ }
+
+ exit(ex_stat);
+
+} /* main */
+
+/*
+ * main doio function. Each doio child starts here, and never returns.
+ */
+
+void
+doio()
+{
+ int rval, i, infd, nbytes;
+ char *cp;
+ struct io_req ioreq;
+ struct sigaction sa, def_action, ignore_action, exit_action;
+#ifndef CRAY
+ struct sigaction sigbus_action;
+#endif
+
+ Memsize = Sdssize = 0;
+
+ /*
+ * Initialize the Pattern - write-type syscalls will replace Pattern[1]
+ * with the pattern passed in the request. Make sure that
+ * strlen(Pattern) is not mod 16 so that out of order words will be
+ * detected.
+ */
+
+ gethostname(Host, sizeof(Host));
+ if ((cp = strchr(Host, '.')) != NULL)
+ *cp = '\0';
+
+ Pattern_Length = sprintf(Pattern, "-:%d:%s:%s*", getpid(), Host, Prog);
+
+ if (!(Pattern_Length % 16)) {
+ Pattern_Length = sprintf(Pattern, "-:%d:%s:%s**",
+ getpid(), Host, Prog);
+ }
+
+ /*
+ * Open a couple of descriptors for the write-log file. One descriptor
+ * is for appending, one for random access. Write logging is done for
+ * file corruption detection. The program doio_check is capable of
+ * doing corruption detection based on a doio write-log.
+ */
+
+ if (w_opt) {
+
+ strcpy(Wlog.w_file, Write_Log);
+
+ if (wlog_open(&Wlog, 0, 0666) == -1) {
+ doio_fprintf(stderr,
+ "Could not open write log file (%s): wlog_open() failed\n",
+ Write_Log);
+ exit(E_SETUP);
+ }
+ }
+
+ /*
+ * Open the input stream - either a file or stdin
+ */
+
+ if (Infile == NULL) {
+ infd = 0;
+ } else {
+ if ((infd = open(Infile, O_RDWR)) == -1) {
+ doio_fprintf(stderr,
+ "Could not open input file (%s): %s (%d)\n",
+ Infile, SYSERR, errno);
+ exit(E_SETUP);
+ }
+ }
+
+ /*
+ * Define a set of signals that should never be masked. Receipt of
+ * these signals generally indicates a programming error, and we want
+ * a corefile at the point of error. We put SIGQUIT in this list so
+ * that ^\ will force a user core dump.
+ *
+ * Note: the handler for these should be SIG_DFL, all of them
+ * produce a corefile as the default action.
+ */
+
+ ignore_action.sa_handler = SIG_IGN;
+ ignore_action.sa_flags = 0;
+ sigemptyset(&ignore_action.sa_mask);
+
+ def_action.sa_handler = SIG_DFL;
+ def_action.sa_flags = 0;
+ sigemptyset(&def_action.sa_mask);
+
+#ifdef sgi
+ exit_action.sa_sigaction = cleanup_handler;
+ exit_action.sa_flags = SA_SIGINFO;
+ sigemptyset(&exit_action.sa_mask);
+
+ sa.sa_sigaction = die_handler;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+
+ sigbus_action.sa_sigaction = sigbus_handler;
+ sigbus_action.sa_flags = SA_SIGINFO;
+ sigemptyset(&sigbus_action.sa_mask);
+#else
+ exit_action.sa_handler = cleanup_handler;
+ exit_action.sa_flags = 0;
+ sigemptyset(&exit_action.sa_mask);
+
+ sa.sa_handler = die_handler;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+
+#ifndef CRAY
+ sigbus_action.sa_handler = sigbus_handler;
+ sigbus_action.sa_flags = 0;
+ sigemptyset(&sigbus_action.sa_mask);
+#endif
+#endif
+
+ for (i = 1; i <= NSIG; i++) {
+ switch(i) {
+ /* Signals to terminate program on */
+ case SIGINT:
+ sigaction(i, &exit_action, NULL);
+ break;
+
+#ifndef CRAY
+ /* This depends on active_mmap_rw */
+ case SIGBUS:
+ sigaction(i, &sigbus_action, NULL);
+ break;
+#endif
+
+ /* Signals to Ignore... */
+ case SIGSTOP:
+ case SIGCONT:
+#ifdef SIGRECOVERY
+ case SIGRECOVERY:
+#endif
+ sigaction(i, &ignore_action, NULL);
+ break;
+
+ /* Signals to trap & report & die */
+ /*case SIGTRAP:*/
+ /*case SIGABRT:*/
+#ifdef SIGERR /* cray only signals */
+ case SIGERR:
+ case SIGBUFIO:
+ case SIGINFO:
+#endif
+ /*case SIGFPE:*/
+ case SIGURG:
+ case SIGHUP:
+ case SIGTERM:
+ case SIGPIPE:
+ case SIGIO:
+ case SIGUSR1:
+ case SIGUSR2:
+ sigaction(i, &sa, NULL);
+ break;
+
+
+ /* Default Action for all other signals */
+ default:
+ sigaction(i, &def_action, NULL);
+ break;
+ }
+ }
+
+ /*
+ * Main loop - each doio proc does this until the read returns eof (0).
+ * Call the appropriate io function based on the request type.
+ */
+
+ while ((nbytes = read(infd, (char *)&ioreq, sizeof(ioreq)))) {
+
+ /*
+ * Periodically check our ppid. If it is 1, the child exits to
+ * help clean up in the case that the main doio process was
+ * killed.
+ */
+
+ if (Reqno && ((Reqno % PPID_CHECK_INTERVAL) == 0)) {
+ if (getppid() == 1) {
+ doio_fprintf(stderr,
+ "Parent doio process has exited\n");
+ alloc_mem(-1);
+ exit(E_SETUP);
+ }
+ }
+
+ if (nbytes == -1) {
+ doio_fprintf(stderr,
+ "read of %d bytes from input failed: %s (%d)\n",
+ sizeof(ioreq), SYSERR, errno);
+ alloc_mem(-1);
+ exit(E_SETUP);
+ }
+
+ if (nbytes != sizeof(ioreq)) {
+ doio_fprintf(stderr,
+ "read wrong # bytes from input stream, expected %d, got %d\n",
+ sizeof(ioreq), nbytes);
+ alloc_mem(-1);
+ exit(E_SETUP);
+ }
+
+ if (ioreq.r_magic != DOIO_MAGIC) {
+ doio_fprintf(stderr,
+ "got a bad magic # from input stream. Expected 0%o, got 0%o\n",
+ DOIO_MAGIC, ioreq.r_magic);
+ alloc_mem(-1);
+ exit(E_SETUP);
+ }
+
+ /*
+ * If we're on a Release_Interval multiple, relase all ssd and
+ * core space, and close all fd's in Fd_Map[].
+ */
+
+ if (Reqno && Release_Interval && ! (Reqno%Release_Interval)) {
+ if (Memsize) {
+#ifdef NOTDEF
+ sbrk(-1 * Memsize);
+#else
+ alloc_mem(-1);
+#endif
+ }
+
+#ifdef _CRAY1
+ if (Sdssize) {
+ ssbreak(-1 * btoc(Sdssize));
+ Sdsptr = 0;
+ Sdssize = 0;
+ }
+#endif /* _CRAY1 */
+
+ alloc_fd(NULL, 0);
+ }
+
+ switch (ioreq.r_type) {
+ case READ:
+ case READA:
+ rval = do_read(&ioreq);
+ break;
+
+ case WRITE:
+ case WRITEA:
+ rval = do_write(&ioreq);
+ break;
+
+ case READV:
+ case AREAD:
+ case PREAD:
+ case LREAD:
+ case LREADA:
+ case LSREAD:
+ case LSREADA:
+ case WRITEV:
+ case AWRITE:
+ case PWRITE:
+ case MMAPR:
+ case MMAPW:
+ case LWRITE:
+ case LWRITEA:
+ case LSWRITE:
+ case LSWRITEA:
+ case LEREAD:
+ case LEREADA:
+ case LEWRITE:
+ case LEWRITEA:
+ rval = do_rw(&ioreq);
+ break;
+
+#ifdef CRAY
+ case SSREAD:
+ case SSWRITE:
+ rval = do_ssdio(&ioreq);
+ break;
+
+ case LISTIO:
+ rval = do_listio(&ioreq);
+ break;
+#endif
+
+#ifdef sgi
+ case RESVSP:
+ case UNRESVSP:
+#ifdef F_FSYNC
+ case DFFSYNC:
+#endif
+ rval = do_fcntl(&ioreq);
+ break;
+#endif /* sgi */
+
+#ifndef CRAY
+ case FSYNC2:
+ case FDATASYNC:
+ rval = do_sync(&ioreq);
+ break;
+#endif
+ default:
+ doio_fprintf(stderr,
+ "Don't know how to handle io request type %d\n",
+ ioreq.r_type);
+ alloc_mem(-1);
+ exit(E_SETUP);
+ }
+
+ if (rval == SKIP_REQ){
+ Reqskipcnt++;
+ }
+ else if (rval != 0) {
+ alloc_mem(-1);
+ doio_fprintf(stderr,
+ "doio(): operation %d returned != 0\n",
+ ioreq.r_type);
+ exit(E_SETUP);
+ }
+
+ if (Message_Interval && Reqno % Message_Interval == 0) {
+ doio_fprintf(stderr, "Info: %d requests done (%d skipped) by this process\n", Reqno, Reqskipcnt);
+ }
+
+ Reqno++;
+
+ if(delayop != 0)
+ doio_delay();
+ }
+
+ /*
+ * Child exits normally
+ */
+ alloc_mem(-1);
+ exit(E_NORMAL);
+
+} /* doio */
+
+void
+doio_delay()
+{
+ struct timeval tv_delay;
+ struct sigaction sa_al, sa_old;
+ sigset_t al_mask;
+
+ switch(delayop) {
+ case DELAY_SELECT:
+ tv_delay.tv_sec = delaytime / 1000000;
+ tv_delay.tv_usec = delaytime % 1000000;
+ /*doio_fprintf(stdout, "delay_select: %d %d\n",
+ tv_delay.tv_sec, tv_delay.tv_usec);*/
+ select(0, NULL, NULL, NULL, &tv_delay);
+ break;
+
+ case DELAY_SLEEP:
+ sleep(delaytime);
+ break;
+
+#ifdef sgi
+ case DELAY_SGINAP:
+ sginap(delaytime);
+ break;
+#endif
+
+ case DELAY_ALARM:
+ sa_al.sa_flags = 0;
+ sa_al.sa_handler = noop_handler;
+ sigemptyset(&sa_al.sa_mask);
+ sigaction(SIGALRM, &sa_al, &sa_old);
+ sigemptyset(&al_mask);
+ alarm(delaytime);
+ sigsuspend(&al_mask);
+ sigaction(SIGALRM, &sa_old, 0);
+ break;
+ }
+}
+
+
+/*
+ * Format IO requests, returning a pointer to the formatted text.
+ *
+ * format_strat - formats the async i/o completion strategy
+ * format_rw - formats a read[a]/write[a] request
+ * format_sds - formats a ssread/sswrite request
+ * format_listio- formats a listio request
+ *
+ * ioreq is the doio io request structure.
+ */
+
+struct smap sysnames[] = {
+ { "READ", READ },
+ { "WRITE", WRITE },
+ { "READA", READA },
+ { "WRITEA", WRITEA },
+ { "SSREAD", SSREAD },
+ { "SSWRITE", SSWRITE },
+ { "LISTIO", LISTIO },
+ { "LREAD", LREAD },
+ { "LREADA", LREADA },
+ { "LWRITE", LWRITE },
+ { "LWRITEA", LWRITEA },
+ { "LSREAD", LSREAD },
+ { "LSREADA", LSREADA },
+ { "LSWRITE", LSWRITE },
+ { "LSWRITEA", LSWRITEA },
+
+ /* Irix System Calls */
+ { "PREAD", PREAD },
+ { "PWRITE", PWRITE },
+ { "AREAD", AREAD },
+ { "AWRITE", AWRITE },
+ { "LLREAD", LLREAD },
+ { "LLAREAD", LLAREAD },
+ { "LLWRITE", LLWRITE },
+ { "LLAWRITE", LLAWRITE },
+ { "RESVSP", RESVSP },
+ { "UNRESVSP", UNRESVSP },
+ { "DFFSYNC", DFFSYNC },
+
+ /* Irix and Linux System Calls */
+ { "READV", READV },
+ { "WRITEV", WRITEV },
+ { "MMAPR", MMAPR },
+ { "MMAPW", MMAPW },
+ { "FSYNC2", FSYNC2 },
+ { "FDATASYNC", FDATASYNC },
+
+ { "unknown", -1 },
+};
+
+struct smap aionames[] = {
+ { "poll", A_POLL },
+ { "signal", A_SIGNAL },
+ { "recall", A_RECALL },
+ { "recalla", A_RECALLA },
+ { "recalls", A_RECALLS },
+ { "suspend", A_SUSPEND },
+ { "callback", A_CALLBACK },
+ { "synch", 0 },
+ { "unknown", -1 },
+};
+
+char *
+format_oflags(int oflags)
+{
+ char flags[255];
+
+
+ flags[0]='\0';
+ switch(oflags & 03) {
+ case O_RDONLY: strcat(flags,"O_RDONLY,"); break;
+ case O_WRONLY: strcat(flags,"O_WRONLY,"); break;
+ case O_RDWR: strcat(flags,"O_RDWR,"); break;
+ default: strcat(flags,"O_weird"); break;
+ }
+
+ if(oflags & O_EXCL)
+ strcat(flags,"O_EXCL,");
+
+ if(oflags & O_SYNC)
+ strcat(flags,"O_SYNC,");
+#ifdef CRAY
+ if(oflags & O_RAW)
+ strcat(flags,"O_RAW,");
+ if(oflags & O_WELLFORMED)
+ strcat(flags,"O_WELLFORMED,");
+#ifdef O_SSD
+ if(oflags & O_SSD)
+ strcat(flags,"O_SSD,");
+#endif
+ if(oflags & O_LDRAW)
+ strcat(flags,"O_LDRAW,");
+ if(oflags & O_PARALLEL)
+ strcat(flags,"O_PARALLEL,");
+ if(oflags & O_BIG)
+ strcat(flags,"O_BIG,");
+ if(oflags & O_PLACE)
+ strcat(flags,"O_PLACE,");
+ if(oflags & O_ASYNC)
+ strcat(flags,"O_ASYNC,");
+#endif
+
+#ifdef sgi
+ if(oflags & O_DIRECT)
+ strcat(flags,"O_DIRECT,");
+ if(oflags & O_DSYNC)
+ strcat(flags,"O_DSYNC,");
+ if(oflags & O_RSYNC)
+ strcat(flags,"O_RSYNC,");
+#endif
+
+ return(strdup(flags));
+}
+
+char *
+format_strat(int strategy)
+{
+ char msg[64];
+ char *aio_strat;
+
+ switch (strategy) {
+ case A_POLL: aio_strat = "POLL"; break;
+ case A_SIGNAL: aio_strat = "SIGNAL"; break;
+ case A_RECALL: aio_strat = "RECALL"; break;
+ case A_RECALLA: aio_strat = "RECALLA"; break;
+ case A_RECALLS: aio_strat = "RECALLS"; break;
+ case A_SUSPEND: aio_strat = "SUSPEND"; break;
+ case A_CALLBACK: aio_strat = "CALLBACK"; break;
+ case 0: aio_strat = "<zero>"; break;
+ default:
+ sprintf(msg, "<error:%#o>", strategy);
+ aio_strat = strdup(msg);
+ break;
+ }
+
+ return(aio_strat);
+}
+
+char *
+format_rw(
+ struct io_req *ioreq,
+ int fd,
+ void *buffer,
+ int signo,
+ char *pattern,
+#ifdef CRAY
+ struct iosw *iosw
+#else
+ void *iosw
+#endif
+ )
+{
+ static char *errbuf=NULL;
+ char *aio_strat, *cp;
+ struct read_req *readp = &ioreq->r_data.read;
+ struct write_req *writep = &ioreq->r_data.write;
+ struct read_req *readap = &ioreq->r_data.read;
+ struct write_req *writeap = &ioreq->r_data.write;
+
+ if(errbuf == NULL)
+ errbuf = (char *)malloc(32768);
+
+ cp = errbuf;
+ cp += sprintf(cp, "Request number %d\n", Reqno);
+
+ switch (ioreq->r_type) {
+ case READ:
+ cp += sprintf(cp, "syscall: read(%d, %#lo, %d)\n",
+ fd, buffer, readp->r_nbytes);
+ cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n",
+ fd, readp->r_file, readp->r_oflags);
+ cp += sprintf(cp, " read done at file offset %d\n",
+ readp->r_offset);
+ break;
+
+ case WRITE:
+ cp += sprintf(cp, "syscall: write(%d, %#lo, %d)\n",
+ fd, buffer, writep->r_nbytes);
+ cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n",
+ fd, writep->r_file, writep->r_oflags);
+ cp += sprintf(cp, " write done at file offset %d - pattern is %s\n",
+ writep->r_offset, pattern);
+ break;
+
+ case READA:
+ aio_strat = format_strat(readap->r_aio_strat);
+
+ cp += sprintf(cp, "syscall: reada(%d, %#lo, %d, %#lo, %d)\n",
+ fd, buffer, readap->r_nbytes, iosw, signo);
+ cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n",
+ fd, readap->r_file, readp->r_oflags);
+ cp += sprintf(cp, " reada done at file offset %d\n",
+ readap->r_offset);
+ cp += sprintf(cp, " async io completion strategy is %s\n",
+ aio_strat);
+ break;
+
+ case WRITEA:
+ aio_strat = format_strat(writeap->r_aio_strat);
+
+ cp += sprintf(cp, "syscall: writea(%d, %#lo, %d, %#lo, %d)\n",
+ fd, buffer, writeap->r_nbytes, iosw, signo);
+ cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n",
+ fd, writeap->r_file, writeap->r_oflags);
+ cp += sprintf(cp, " writea done at file offset %d - pattern is %s\n",
+ writeap->r_offset, pattern);
+ cp += sprintf(cp, " async io completion strategy is %s\n",
+ aio_strat);
+ break;
+
+ }
+
+ return errbuf;
+}
+
+#ifdef CRAY
+char *
+format_sds(
+ struct io_req *ioreq,
+ void *buffer,
+ int sds,
+ char *pattern
+ )
+{
+ int i;
+ static char *errbuf=NULL;
+ char *cp;
+
+ struct ssread_req *ssreadp = &ioreq->r_data.ssread;
+ struct sswrite_req *sswritep = &ioreq->r_data.sswrite;
+
+ if(errbuf == NULL)
+ errbuf = (char *)malloc(32768);
+
+ cp = errbuf;
+ cp += sprintf(cp, "Request number %d\n", Reqno);
+
+
+ switch (ioreq->r_type) {
+ case SSREAD:
+ cp += sprintf(cp, "syscall: ssread(%#o, %#o, %d)\n",
+ buffer, sds, ssreadp->r_nbytes);
+ break;
+
+ case SSWRITE:
+ cp += sprintf(cp, "syscall: sswrite(%#o, %#o, %d) - pattern was %s\n",
+ buffer, sds, sswritep->r_nbytes, pattern);
+ break;
+ }
+ return errbuf;
+}
+#endif /* CRAY */
+
+/*
+ * Perform the various sorts of disk reads
+ */
+
+int
+do_read(req)
+struct io_req *req;
+{
+ int fd, offset, nbytes, oflags, rval;
+ char *addr, *file;
+#ifdef CRAY
+ struct aio_info *aiop;
+ int aio_id, aio_strat, signo;
+#endif
+#ifdef sgi
+ struct fd_cache *fdc;
+#endif
+
+ /*
+ * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+ * r_nbytes are at the same offset in the read_req and reada_req
+ * structures.
+ */
+
+ file = req->r_data.read.r_file;
+ oflags = req->r_data.read.r_oflags;
+ offset = req->r_data.read.r_offset;
+ nbytes = req->r_data.read.r_nbytes;
+
+ /*printf("read: %s, %#o, %d %d\n", file, oflags, offset, nbytes);*/
+
+ /*
+ * Grab an open file descriptor
+ * Note: must be done before memory allocation so that the direct i/o
+ * information is available in mem. allocate
+ */
+
+ if ((fd = alloc_fd(file, oflags)) == -1)
+ return -1;
+
+ /*
+ * Allocate core or sds - based on the O_SSD flag
+ */
+
+#ifndef wtob
+#define wtob(x) (x * sizeof(UINT64_T))
+#endif
+
+#ifdef CRAY
+ if (oflags & O_SSD) {
+ if (alloc_sds(nbytes) == -1)
+ return -1;
+
+ addr = (char *)Sdsptr;
+ } else {
+ if ((rval = alloc_mem(nbytes + wtob(1) * 2 + MPP_BUMP * sizeof(UINT64_T))) < 0) {
+ return rval;
+ }
+
+ addr = Memptr;
+
+ /*
+ * if io is not raw, bump the offset by a random amount
+ * to generate non-word-aligned io.
+ */
+ if (! (req->r_data.read.r_uflags & F_WORD_ALIGNED)) {
+ addr += random_range(0, wtob(1) - 1, 1, NULL);
+ }
+ }
+#else
+#ifdef sgi
+ /* get memory alignment for using DIRECT I/O */
+ fdc = alloc_fdcache(file, oflags);
+
+ if ((rval = alloc_mem(nbytes + wtob(1) * 2 + fdc->c_memalign)) < 0) {
+ return rval;
+ }
+
+ addr = Memptr;
+
+
+ if( (req->r_data.read.r_uflags & F_WORD_ALIGNED) ) {
+ /*
+ * Force memory alignment for Direct I/O
+ */
+ if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) {
+ addr += fdc->c_memalign - ((long)addr % fdc->c_memalign);
+ }
+ } else {
+ addr += random_range(0, wtob(1) - 1, 1, NULL);
+ }
+#else
+ /* what is !CRAY && !sgi ? */
+ if ((rval = alloc_mem(nbytes + wtob(1) * 2)) < 0) {
+ return rval;
+ }
+
+ addr = Memptr;
+#endif /* !CRAY && sgi */
+#endif /* CRAY */
+
+
+ switch (req->r_type) {
+ case READ:
+ /* move to the desired file position. */
+ if (lseek(fd, offset, SEEK_SET) == -1) {
+ doio_fprintf(stderr,
+ "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n",
+ fd, offset, SYSERR, errno);
+ return -1;
+ }
+
+ if ((rval = read(fd, addr, nbytes)) == -1) {
+ doio_fprintf(stderr,
+ "read() request failed: %s (%d)\n%s\n",
+ SYSERR, errno,
+ format_rw(req, fd, addr, -1, NULL, NULL));
+ doio_upanic(U_RVAL);
+ return -1;
+ } else if (rval != nbytes) {
+ doio_fprintf(stderr,
+ "read() request returned wrong # of bytes - expected %d, got %d\n%s\n",
+ nbytes, rval,
+ format_rw(req, fd, addr, -1, NULL, NULL));
+ doio_upanic(U_RVAL);
+ return -1;
+ }
+ break;
+
+#ifdef CRAY
+ case READA:
+ /*
+ * Async read
+ */
+
+ /* move to the desired file position. */
+ if (lseek(fd, offset, SEEK_SET) == -1) {
+ doio_fprintf(stderr,
+ "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n",
+ fd, offset, SYSERR, errno);
+ return -1;
+ }
+
+ aio_strat = req->r_data.read.r_aio_strat;
+ signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+ aio_id = aio_register(fd, aio_strat, signo);
+ aiop = aio_slot(aio_id);
+
+ if (reada(fd, addr, nbytes, &aiop->iosw, signo) == -1) {
+ doio_fprintf(stderr, "reada() failed: %s (%d)\n%s\n",
+ SYSERR, errno,
+ format_rw(req, fd, addr, signo, NULL, &aiop->iosw));
+ aio_unregister(aio_id);
+ doio_upanic(U_RVAL);
+ rval = -1;
+ } else {
+ /*
+ * Wait for io to complete
+ */
+
+ aio_wait(aio_id);
+
+ /*
+ * make sure the io completed without error
+ */
+
+ if (aiop->iosw.sw_count != nbytes) {
+ doio_fprintf(stderr,
+ "Bad iosw from reada()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n",
+ 1, 0, nbytes,
+ aiop->iosw.sw_flag,
+ aiop->iosw.sw_error,
+ aiop->iosw.sw_count,
+ format_rw(req, fd, addr, signo, NULL, &aiop->iosw));
+ aio_unregister(aio_id);
+ doio_upanic(U_IOSW);
+ rval = -1;
+ } else {
+ aio_unregister(aio_id);
+ rval = 0;
+ }
+ }
+
+ if (rval == -1)
+ return rval;
+ break;
+#endif /* CRAY */
+ }
+
+ return 0; /* if we get here, everything went ok */
+}
+
+/*
+ * Perform the verious types of disk writes.
+ */
+
+int
+do_write(req)
+struct io_req *req;
+{
+ static int pid = -1;
+ int fd, nbytes, oflags, signo;
+ int logged_write, rval, got_lock;
+ int aio_strat, aio_id;
+ long offset, woffset;
+ char *addr, pattern, *file, *msg;
+ struct wlog_rec wrec;
+ struct flock flk;
+ struct aio_info *aiop;
+#ifdef sgi
+ struct fd_cache *fdc;
+#endif
+
+ /*
+ * Misc variable setup
+ */
+
+ signo = 0;
+ nbytes = req->r_data.write.r_nbytes;
+ offset = req->r_data.write.r_offset;
+ pattern = req->r_data.write.r_pattern;
+ file = req->r_data.write.r_file;
+ oflags = req->r_data.write.r_oflags;
+
+ /*printf("pwrite: %s, %#o, %d %d\n", file, oflags, offset, nbytes);*/
+
+ /*
+ * Allocate core memory and possibly sds space. Initialize the data
+ * to be written.
+ */
+
+ Pattern[0] = pattern;
+
+
+ /*
+ * Get a descriptor to do the io on
+ */
+
+ if ((fd = alloc_fd(file, oflags)) == -1)
+ return -1;
+
+ /*printf("write: %d, %s, %#o, %d %d\n",
+ fd, file, oflags, offset, nbytes);*/
+
+ /*
+ * Allocate SDS space for backdoor write if desired
+ */
+
+#ifdef CRAY
+ if (oflags & O_SSD) {
+#ifndef _CRAYMPP
+ if ((rval = alloc_mem(nbytes + wtob(1))) < 0) {
+ return rval;
+ }
+
+ (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+ /*pattern_fill(Memptr, nbytes, Pattern, Pattern_Length, 0);*/
+
+ if (alloc_sds(nbytes) == -1)
+ return -1;
+
+ if (sswrite((long)Memptr, Sdsptr, btoc(nbytes)) == -1) {
+ doio_fprintf(stderr, "sswrite(%d, %d, %d) failed: %s (%d)\n",
+ (long)Memptr, Sdsptr, btoc(nbytes),
+ SYSERR, errno);
+ fflush(stderr);
+ return -1;
+ }
+
+ addr = (char *)Sdsptr;
+#else
+ doio_fprintf(stderr, "Invalid O_SSD flag was generated for MPP system\n");
+ fflush(stderr);
+ return -1;
+#endif /* !CRAYMPP */
+ } else {
+ if ((rval = alloc_mem(nbytes + wtob(1)) < 0)) {
+ return rval;
+ }
+
+ addr = Memptr;
+
+ /*
+ * if io is not raw, bump the offset by a random amount
+ * to generate non-word-aligned io.
+ */
+
+ if (! (req->r_data.write.r_uflags & F_WORD_ALIGNED)) {
+ addr += random_range(0, wtob(1) - 1, 1, NULL);
+ }
+
+ (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+ if( addr != Memptr )
+ memmove( addr, Memptr, nbytes);
+ }
+#else /* CRAY */
+#ifdef sgi
+ /* get memory alignment for using DIRECT I/O */
+ fdc = alloc_fdcache(file, oflags);
+
+ if ((rval = alloc_mem(nbytes + wtob(1) * 2 + fdc->c_memalign)) < 0) {
+ return rval;
+ }
+
+ addr = Memptr;
+
+ if( (req->r_data.write.r_uflags & F_WORD_ALIGNED) ) {
+ /*
+ * Force memory alignment for Direct I/O
+ */
+ if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) {
+ addr += fdc->c_memalign - ((long)addr % fdc->c_memalign);
+ }
+ } else {
+ addr += random_range(0, wtob(1) - 1, 1, NULL);
+ }
+
+ (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+ if( addr != Memptr )
+ memmove( addr, Memptr, nbytes);
+
+#else /* sgi */
+ if ((rval = alloc_mem(nbytes + wtob(1) * 2)) < 0) {
+ return rval;
+ }
+
+ addr = Memptr;
+
+ (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+ if( addr != Memptr )
+ memmove( addr, Memptr, nbytes);
+#endif /* sgi */
+#endif /* CRAY */
+
+ rval = -1;
+ got_lock = 0;
+ logged_write = 0;
+
+ if (k_opt) {
+ if (lock_file_region(file, fd, F_WRLCK, offset, nbytes) < 0) {
+ alloc_mem(-1);
+ exit(E_INTERNAL);
+ }
+
+ got_lock = 1;
+ }
+
+ /*
+ * Write a preliminary write-log entry. This is done so that
+ * doio_check can do corruption detection across an interrupt/crash.
+ * Note that w_done is set to 0. If doio_check sees this, it
+ * re-creates the file extents as if the write completed, but does not
+ * do any checking - see comments in doio_check for more details.
+ */
+
+ if (w_opt) {
+ if (pid == -1) {
+ pid = getpid();
+ }
+ wrec.w_async = (req->r_type == WRITEA) ? 1 : 0;
+ wrec.w_oflags = oflags;
+ wrec.w_pid = pid;
+ wrec.w_offset = offset;
+ wrec.w_nbytes = nbytes;
+
+ wrec.w_pathlen = strlen(file);
+ memcpy(wrec.w_path, file, wrec.w_pathlen);
+ wrec.w_hostlen = strlen(Host);
+ memcpy(wrec.w_host, Host, wrec.w_hostlen);
+ wrec.w_patternlen = Pattern_Length;
+ memcpy(wrec.w_pattern, Pattern, wrec.w_patternlen);
+
+ wrec.w_done = 0;
+
+ if ((woffset = wlog_record_write(&Wlog, &wrec, -1)) == -1) {
+ doio_fprintf(stderr,
+ "Could not append to write-log: %s (%d)\n",
+ SYSERR, errno);
+ } else {
+ logged_write = 1;
+ }
+ }
+
+ switch (req->r_type ) {
+ case WRITE:
+ /*
+ * sync write
+ */
+
+ if (lseek(fd, offset, SEEK_SET) == -1) {
+ doio_fprintf(stderr,
+ "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n",
+ fd, offset, SYSERR, errno);
+ return -1;
+ }
+
+ rval = write(fd, addr, nbytes);
+
+ if (rval == -1) {
+ doio_fprintf(stderr,
+ "write() failed: %s (%d)\n%s\n",
+ SYSERR, errno,
+ format_rw(req, fd, addr, -1, Pattern, NULL));
+#ifdef sgi
+ doio_fprintf(stderr,
+ "write() failed: %s\n\twrite(%d, %#o, %d)\n\toffset %d, nbytes%%miniou(%d)=%d, oflags=%#o memalign=%d, addr%%memalign=%d\n",
+ strerror(errno),
+ fd, addr, nbytes,
+ offset,
+ fdc->c_miniosz, nbytes%fdc->c_miniosz,
+ oflags, fdc->c_memalign, (long)addr%fdc->c_memalign);
+#else
+ doio_fprintf(stderr,
+ "write() failed: %s\n\twrite(%d, %#o, %d)\n\toffset %d, nbytes%%1B=%d, oflags=%#o\n",
+ strerror(errno),
+ fd, addr, nbytes,
+ offset, nbytes%4096, oflags);
+#endif
+ doio_upanic(U_RVAL);
+ } else if (rval != nbytes) {
+ doio_fprintf(stderr,
+ "write() returned wrong # bytes - expected %d, got %d\n%s\n",
+ nbytes, rval,
+ format_rw(req, fd, addr, -1, Pattern, NULL));
+ doio_upanic(U_RVAL);
+ rval = -1;
+ }
+
+ break;
+
+#ifdef CRAY
+ case WRITEA:
+ /*
+ * async write
+ */
+ if (lseek(fd, offset, SEEK_SET) == -1) {
+ doio_fprintf(stderr,
+ "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n",
+ fd, offset, SYSERR, errno);
+ return -1;
+ }
+
+ aio_strat = req->r_data.write.r_aio_strat;
+ signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+ aio_id = aio_register(fd, aio_strat, signo);
+ aiop = aio_slot(aio_id);
+
+ /*
+ * init iosw and do the async write
+ */
+
+ if (writea(fd, addr, nbytes, &aiop->iosw, signo) == -1) {
+ doio_fprintf(stderr,
+ "writea() failed: %s (%d)\n%s\n",
+ SYSERR, errno,
+ format_rw(req, fd, addr, -1, Pattern, NULL));
+ doio_upanic(U_RVAL);
+ aio_unregister(aio_id);
+ rval = -1;
+ } else {
+
+ /*
+ * Wait for io to complete
+ */
+
+ aio_wait(aio_id);
+
+ /*
+ * check that iosw is ok
+ */
+
+ if (aiop->iosw.sw_count != nbytes) {
+ doio_fprintf(stderr,
+ "Bad iosw from writea()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n",
+ 1, 0, nbytes,
+ aiop->iosw.sw_flag,
+ aiop->iosw.sw_error,
+ aiop->iosw.sw_count,
+ format_rw(req, fd, addr, -1, Pattern, &aiop->iosw));
+ aio_unregister(aio_id);
+ doio_upanic(U_IOSW);
+ rval = -1;
+ } else {
+ aio_unregister(aio_id);
+ rval = 0;
+ }
+ }
+ break;
+
+#endif /* CRAY */
+ }
+
+ /*
+ * Verify that the data was written correctly - check_file() returns
+ * a non-null pointer which contains an error message if there are
+ * problems.
+ */
+
+ if (v_opt) {
+ msg = check_file(file, offset, nbytes, Pattern, Pattern_Length,
+ 0, oflags & O_PARALLEL);
+ if (msg != NULL) {
+ doio_fprintf(stderr, "%s%s\n",
+ msg,
+#ifdef CRAY
+ format_rw(req, fd, addr, -1, Pattern, &aiop->iosw)
+#else
+ format_rw(req, fd, addr, -1, Pattern, NULL)
+#endif
+ );
+ doio_upanic(U_CORRUPTION);
+ exit(E_COMPARE);
+
+ }
+ }
+
+ /*
+ * General cleanup ...
+ *
+ * Write extent information to the write-log, so that doio_check can do
+ * corruption detection. Note that w_done is set to 1, indicating that
+ * the write has been verified as complete. We don't need to write the
+ * filename on the second logging.
+ */
+
+ if (w_opt && logged_write) {
+ wrec.w_done = 1;
+ wlog_record_write(&Wlog, &wrec, woffset);
+ }
+
+ /*
+ * Unlock file region if necessary
+ */
+
+ if (got_lock) {
+ if (lock_file_region(file, fd, F_UNLCK, offset, nbytes) < 0) {
+ alloc_mem(-1);
+ exit(E_INTERNAL);
+ }
+ }
+
+ return( (rval == -1) ? -1 : 0);
+}
+
+
+/*
+ * Simple routine to lock/unlock a file using fcntl()
+ */
+
+int
+lock_file_region(fname, fd, type, start, nbytes)
+char *fname;
+int fd;
+int type;
+int start;
+int nbytes;
+{
+ struct flock flk;
+
+ flk.l_type = type;
+ flk.l_whence = 0;
+ flk.l_start = start;
+ flk.l_len = nbytes;
+
+ if (fcntl(fd, F_SETLKW, &flk) < 0) {
+ doio_fprintf(stderr,
+ "fcntl(%d, %d, %#o) failed for file %s, lock type %d, offset %d, length %d: %s (%d), open flags: %#o\n",
+ fd, F_SETLKW, &flk, fname, type,
+ start, nbytes, SYSERR, errno,
+ fcntl(fd, F_GETFL, 0));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Perform a listio request.
+ */
+
+#ifdef CRAY
+char *
+format_listio(
+ struct io_req *ioreq,
+ int lcmd,
+ struct listreq *list,
+ int nent,
+ int fd,
+ char *pattern
+ )
+{
+ static char *errbuf=NULL;
+ struct listio_req *liop = &ioreq->r_data.listio;
+ struct listreq *listreq;
+ char *cp, *cmd, *opcode, *aio_strat;
+ int i;
+
+ switch (lcmd) {
+ case LC_START: cmd = "LC_START"; break;
+ case LC_WAIT: cmd = "LC_WAIT"; break;
+ default: cmd = "???"; break;
+ }
+
+ if(errbuf == NULL)
+ errbuf = (char *)malloc(32768);
+
+ cp = errbuf;
+ cp += sprintf(cp, "Request number %d\n", Reqno);
+
+ cp += sprintf(cp, "syscall: listio(%s, %#o, %d)\n\n",
+ cmd, list, nent);
+
+ aio_strat = format_strat(liop->r_aio_strat);
+
+ for (i = 0; i < nent; i++) {
+ cp += sprintf(cp, "struct lioreq for request element %d\n", i);
+ cp += sprintf(cp, "----------------------------------------\n");
+
+ listreq = list + i;
+
+ switch (listreq->li_opcode) {
+ case LO_READ: opcode = "LO_READ"; break;
+ case LO_WRITE: opcode = "LO_WRITE"; break;
+ default: opcode = "???"; break;
+ }
+
+ cp += sprintf(cp, " li_opcode = %s\n", opcode);
+ cp += sprintf(cp, " li_drvr = %#o\n", listreq->li_drvr);
+ cp += sprintf(cp, " li_flags = %#o\n", listreq->li_flags);
+ cp += sprintf(cp, " li_offset = %d\n", listreq->li_offset);
+ cp += sprintf(cp, " li_fildes = %d\n", listreq->li_fildes);
+ cp += sprintf(cp, " li_buf = %#o\n", listreq->li_buf);
+ cp += sprintf(cp, " li_nbyte = %d\n", listreq->li_nbyte);
+ cp += sprintf(cp, " li_status = %#o (%d, %d, %d)\n", listreq->li_status, listreq->li_status->sw_flag, listreq->li_status->sw_error, listreq->li_status->sw_count);
+ cp += sprintf(cp, " li_signo = %d\n", listreq->li_signo);
+ cp += sprintf(cp, " li_nstride = %d\n", listreq->li_nstride);
+ cp += sprintf(cp, " li_filstride = %d\n", listreq->li_filstride);
+ cp += sprintf(cp, " li_memstride = %d\n", listreq->li_memstride);
+ cp += sprintf(cp, " io completion strategy is %s\n", aio_strat);
+ }
+ return errbuf;
+}
+#endif /* CRAY */
+
+int
+do_listio(req)
+struct io_req *req;
+{
+#ifdef CRAY
+ struct listio_req *lio;
+ int fd, oflags, signo, nb, i;
+ int logged_write, rval, got_lock;
+ int aio_strat, aio_id;
+ int min_byte, max_byte;
+ int mem_needed;
+ int foffset, fstride, mstride, nstrides;
+ char *moffset;
+ long offset, woffset;
+ char *addr, *msg;
+ sigset_t block_mask, omask;
+ struct wlog_rec wrec;
+ struct aio_info *aiop;
+ struct listreq lio_req;
+
+ lio = &req->r_data.listio;
+
+ /*
+ * If bytes per stride is less than the stride size, drop the request
+ * since it will cause overlapping strides, and we cannot predict
+ * the order they will complete in.
+ */
+
+ if (lio->r_filestride && abs(lio->r_filestride) < lio->r_nbytes) {
+ doio_fprintf(stderr, "do_listio(): Bogus listio request - abs(filestride) [%d] < nbytes [%d]\n",
+ abs(lio->r_filestride), lio->r_nbytes);
+ return -1;
+ }
+
+ /*
+ * Allocate core memory. Initialize the data to be written. Make
+ * sure we get enough, based on the memstride.
+ */
+
+ mem_needed =
+ stride_bounds(0, lio->r_memstride, lio->r_nstrides,
+ lio->r_nbytes, NULL, NULL);
+
+ if ((rval = alloc_mem(mem_needed + wtob(1))) < 0) {
+ return rval;
+ }
+
+ /*
+ * Set the memory address pointer. If the io is not raw, adjust
+ * addr by a random amount, so that non-raw io is not necessarily
+ * word aligned.
+ */
+
+ addr = Memptr;
+
+ if (! (lio->r_uflags & F_WORD_ALIGNED)) {
+ addr += random_range(0, wtob(1) - 1, 1, NULL);
+ }
+
+ if (lio->r_opcode == LO_WRITE) {
+ Pattern[0] = lio->r_pattern;
+ (*Data_Fill)(Memptr, mem_needed, Pattern, Pattern_Length, 0);
+ if( addr != Memptr )
+ memmove( addr, Memptr, mem_needed);
+ }
+
+ /*
+ * Get a descriptor to do the io on. No need to do an lseek, as this
+ * is encoded in the listio request.
+ */
+
+ if ((fd = alloc_fd(lio->r_file, lio->r_oflags)) == -1) {
+ return -1;
+ }
+
+ rval = -1;
+ got_lock = 0;
+ logged_write = 0;
+
+ /*
+ * If the opcode is LO_WRITE, lock all regions of the file that
+ * are touched by this listio request. Currently, we use
+ * stride_bounds() to figure out the min and max bytes affected, and
+ * lock the entire region, regardless of the file stride.
+ */
+
+ if (lio->r_opcode == LO_WRITE && k_opt) {
+ stride_bounds(lio->r_offset,
+ lio->r_filestride, lio->r_nstrides,
+ lio->r_nbytes, &min_byte, &max_byte);
+
+ if (lock_file_region(lio->r_file, fd, F_WRLCK,
+ min_byte, (max_byte-min_byte+1)) < 0) {
+ doio_fprintf(stderr, "stride_bounds(%d, %d, %d, %d, ..., ...) set min_byte to %d, max_byte to %d\n",
+ lio->r_offset, lio->r_filestride,
+ lio->r_nstrides, lio->r_nbytes, min_byte,
+ max_byte);
+ return -1;
+ } else {
+ got_lock = 1;
+ }
+ }
+
+ /*
+ * async write
+ */
+
+ aio_strat = lio->r_aio_strat;
+ signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+ aio_id = aio_register(fd, aio_strat, signo);
+ aiop = aio_slot(aio_id);
+
+ /*
+ * Form the listio request, and make the call.
+ */
+
+ lio_req.li_opcode = lio->r_opcode;
+ lio_req.li_drvr = 0;
+ lio_req.li_flags = LF_LSEEK;
+ lio_req.li_offset = lio->r_offset;
+ lio_req.li_fildes = fd;
+
+ if (lio->r_memstride >= 0 || lio->r_nstrides <= 1) {
+ lio_req.li_buf = addr;
+ } else {
+ lio_req.li_buf = addr + mem_needed - lio->r_nbytes;
+ }
+
+ lio_req.li_nbyte = lio->r_nbytes;
+ lio_req.li_status = &aiop->iosw;
+ lio_req.li_signo = signo;
+ lio_req.li_nstride = lio->r_nstrides;
+ lio_req.li_filstride = lio->r_filestride;
+ lio_req.li_memstride = lio->r_memstride;
+
+ /*
+ * If signo != 0, block signo while we're in the system call, so that
+ * we don't get interrupted syscall failures.
+ */
+
+ if (signo) {
+ sigemptyset(&block_mask);
+ sigaddset(&block_mask, signo);
+ sigprocmask(SIG_BLOCK, &block_mask, &omask);
+ }
+
+ if (listio(lio->r_cmd, &lio_req, 1) < 0) {
+ doio_fprintf(stderr,
+ "listio() failed: %s (%d)\n%s\n",
+ SYSERR, errno,
+ format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern));
+ aio_unregister(aio_id);
+ doio_upanic(U_RVAL);
+ goto lio_done;
+ }
+
+ if (signo) {
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+
+ /*
+ * Wait for io to complete
+ */
+
+ aio_wait(aio_id);
+
+ nstrides = lio->r_nstrides ? lio->r_nstrides : 1;
+ if (aiop->iosw.sw_count != lio->r_nbytes * nstrides) {
+ doio_fprintf(stderr,
+ "Bad iosw from listio()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n",
+ 1, 0, lio->r_nbytes * lio->r_nstrides,
+ aiop->iosw.sw_flag,
+ aiop->iosw.sw_error, aiop->iosw.sw_count,
+ format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern));
+ aio_unregister(aio_id);
+ doio_upanic(U_IOSW);
+ goto lio_done;
+ }
+
+ aio_unregister(aio_id);
+
+ /*
+ * Verify that the data was written correctly - check_file() returns
+ * a non-null pointer which contains an error message if there are
+ * problems.
+ *
+ * For listio, we basically have to make 1 call to check_file for each
+ * stride.
+ */
+
+ if (v_opt && lio_req.li_opcode == LO_WRITE) {
+ fstride = lio->r_filestride ? lio->r_filestride : lio->r_nbytes;
+ mstride = lio->r_memstride ? lio->r_memstride : lio->r_nbytes;
+ foffset = lio->r_offset;
+
+ if (mstride> 0 || lio->r_nstrides <= 1) {
+ moffset = addr;
+ } else {
+ moffset = addr + mem_needed - lio->r_nbytes;
+ }
+
+ for (i = 0; i < lio_req.li_nstride; i++) {
+ msg = check_file(lio->r_file,
+ foffset, lio->r_nbytes,
+ Pattern, Pattern_Length,
+ moffset - addr,
+ lio->r_oflags & O_PARALLEL);
+
+ if (msg != NULL) {
+ doio_fprintf(stderr, "%s\n%s\n",
+ msg,
+ format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern));
+ doio_upanic(U_CORRUPTION);
+ exit(E_COMPARE);
+ }
+
+ moffset += mstride;
+ foffset += fstride;
+ }
+
+ }
+
+ rval = 0;
+
+ lio_done:
+
+ /*
+ * General cleanup ...
+ *
+ */
+
+ /*
+ * Release file locks if necessary
+ */
+
+ if (got_lock) {
+ if (lock_file_region(lio->r_file, fd, F_UNLCK,
+ min_byte, (max_byte-min_byte+1)) < 0) {
+ return -1;
+ }
+ }
+
+ return rval;
+#else
+ return -1;
+#endif
+}
+
+/*
+ * perform ssread/sswrite operations
+ */
+
+#ifdef _CRAY1
+
+int
+do_ssdio(req)
+struct io_req *req;
+{
+ int nbytes, nb;
+ char errbuf[BSIZE];
+
+ nbytes = req->r_data.ssread.r_nbytes;
+
+ /*
+ * Grab core and sds space
+ */
+
+ if ((nb = alloc_mem(nbytes)) < 0)
+ return nb;
+
+ if (alloc_sds(nbytes) == -1)
+ return -1;
+
+ if (req->r_type == SSWRITE) {
+
+ /*
+ * Init data and ship it to the ssd
+ */
+
+ Pattern[0] = req->r_data.sswrite.r_pattern;
+ /*pattern_fill(Memptr, nbytes, Pattern, Pattern_Length, 0);*/
+ (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+
+ if (sswrite((long)Memptr, (long)Sdsptr, btoc(nbytes)) == -1) {
+ doio_fprintf(stderr, "sswrite() failed: %s (%d)\n%s\n",
+ SYSERR, errno,
+ format_sds(req, Memptr, Sdsptr, Pattern));
+ doio_upanic(U_RVAL);
+ return -1;
+ }
+ } else {
+ /*
+ * read from sds
+ */
+
+ if (ssread((long)Memptr, (long)Sdsptr, btoc(nbytes)) == -1) {
+ doio_fprintf(stderr, "ssread() failed: %s (%d)\n%s\n",
+ SYSERR, errno,
+ format_sds(req, Memptr, Sdsptr, Pattern));
+
+ doio_upanic(U_RVAL);
+ return -1;
+ }
+ }
+
+ /*
+ * Verify data if SSWRITE and v_opt
+ */
+
+ if (v_opt && req->r_type == SSWRITE) {
+ ssread((long)Memptr, (long)Sdsptr, btoc(nbytes));
+
+ if (pattern_check(Memptr, nbytes, Pattern, Pattern_Length, 0) == -1) {
+ doio_fprintf(stderr,
+ "sds DATA COMPARE ERROR - ABORTING\n%s\n",
+ format_sds(req, Memptr, Sdsptr, Pattern));
+
+ doio_upanic(U_CORRUPTION);
+ exit(E_COMPARE);
+ }
+ }
+}
+
+#else
+
+#ifdef CRAY
+
+int
+do_ssdio(req)
+struct io_req *req;
+{
+ doio_fprintf(stderr,
+ "Internal Error - do_ssdio() called on a non-cray1 system\n");
+ alloc_mem(-1);
+ exit(E_INTERNAL);
+}
+
+#endif
+
+#endif /* _CRAY1 */
+
+
+/* ---------------------------------------------------------------------------
+ *
+ * A new paradigm of doing the r/w system call where there is a "stub"
+ * function that builds the info for the system call, then does the system
+ * call; this is called by code that is common to all system calls and does
+ * the syscall return checking, async I/O wait, iosw check, etc.
+ *
+ * Flags:
+ * WRITE, ASYNC, SSD/SDS,
+ * FILE_LOCK, WRITE_LOG, VERIFY_DATA,
+ */
+
+struct status {
+ int rval; /* syscall return */
+ int err; /* errno */
+ int *aioid; /* list of async I/O structures */
+};
+
+struct syscall_info {
+ char *sy_name;
+ int sy_type;
+ struct status *(*sy_syscall)();
+ int (*sy_buffer)();
+ char *(*sy_format)();
+ int sy_flags;
+ int sy_bits;
+};
+
+#define SY_WRITE 00001
+#define SY_ASYNC 00010
+#define SY_IOSW 00020
+#define SY_SDS 00100
+
+char *
+fmt_ioreq(struct io_req *ioreq, struct syscall_info *sy, int fd)
+{
+ static char *errbuf=NULL;
+ char *cp;
+ struct rw_req *io;
+ struct smap *aname;
+ struct stat sbuf;
+#ifdef sgi
+ struct dioattr finfo;
+#endif
+
+ if(errbuf == NULL)
+ errbuf = (char *)malloc(32768);
+
+ io = &ioreq->r_data.io;
+
+ /*
+ * Look up async I/O completion strategy
+ */
+ for(aname=aionames;
+ aname->value != -1 && aname->value != io->r_aio_strat;
+ aname++)
+ ;
+
+ cp = errbuf;
+ cp += sprintf(cp, "Request number %d\n", Reqno);
+
+ cp += sprintf(cp, " fd %d is file %s - open flags are %#o %s\n",
+ fd, io->r_file, io->r_oflags, format_oflags(io->r_oflags));
+
+ if(sy->sy_flags & SY_WRITE) {
+ cp += sprintf(cp, " write done at file offset %d - pattern is %c (%#o)\n",
+ io->r_offset,
+ (io->r_pattern == '\0') ? '?' : io->r_pattern,
+ io->r_pattern);
+ } else {
+ cp += sprintf(cp, " read done at file offset %d\n",
+ io->r_offset);
+ }
+
+ if(sy->sy_flags & SY_ASYNC) {
+ cp += sprintf(cp, " async io completion strategy is %s\n",
+ aname->string);
+ }
+
+ cp += sprintf(cp, " number of requests is %d, strides per request is %d\n",
+ io->r_nent, io->r_nstrides);
+
+ cp += sprintf(cp, " i/o byte count = %d\n",
+ io->r_nbytes);
+
+ cp += sprintf(cp, " memory alignment is %s\n",
+ (io->r_uflags & F_WORD_ALIGNED) ? "aligned" : "unaligned");
+
+#ifdef CRAY
+ if(io->r_oflags & O_RAW) {
+ cp += sprintf(cp, " RAW I/O: offset %% 4096 = %d length %% 4096 = %d\n",
+ io->r_offset % 4096, io->r_nbytes % 4096);
+ fstat(fd, &sbuf);
+ cp += sprintf(cp, " optimal file xfer size: small: %d large: %d\n",
+ sbuf.st_blksize, sbuf.st_oblksize);
+ cp += sprintf(cp, " cblks %d cbits %#o\n",
+ sbuf.st_cblks, sbuf.st_cbits);
+ }
+#endif
+#ifdef sgi
+ if(io->r_oflags & O_DIRECT) {
+
+ if(fcntl(fd, F_DIOINFO, &finfo) == -1) {
+ cp += sprintf(cp, " Error %s (%d) getting direct I/O info\n",
+ strerror(errno), errno);
+ finfo.d_mem = 1;
+ finfo.d_miniosz = 1;
+ finfo.d_maxiosz = 1;
+ }
+
+ cp += sprintf(cp, " DIRECT I/O: offset %% %d = %d length %% %d = %d\n",
+ finfo.d_miniosz,
+ io->r_offset % finfo.d_miniosz,
+ io->r_nbytes,
+ io->r_nbytes % finfo.d_miniosz);
+ cp += sprintf(cp, " mem alignment 0x%x xfer size: small: %d large: %d\n",
+ finfo.d_mem, finfo.d_miniosz, finfo.d_maxiosz);
+ }
+#endif
+
+ return(errbuf);
+}
+
+/*
+ * Issue listio requests
+ */
+#ifdef CRAY
+struct status *
+sy_listio(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+ int offset, nbytes, nstrides, nents, aio_strat;
+ int aio_id, signo, o, i, lc;
+ char *a;
+ struct listreq *lio_req, *l;
+ struct aio_info *aiop;
+ struct status *status;
+
+ /*
+ * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+ * r_nbytes are at the same offset in the read_req and reada_req
+ * structures.
+ */
+ offset = req->r_data.io.r_offset;
+ nbytes = req->r_data.io.r_nbytes;
+ nstrides = req->r_data.io.r_nstrides;
+ nents = req->r_data.io.r_nent;
+ aio_strat = req->r_data.io.r_aio_strat;
+
+ lc = (sysc->sy_flags & SY_ASYNC) ? LC_START : LC_WAIT;
+
+ status = (struct status *)malloc(sizeof(struct status));
+ if( status == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ status->aioid = (int *)malloc( (nents+1) * sizeof(int) );
+ if( status->aioid == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+
+ signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+ lio_req = (struct listreq *)malloc(nents * sizeof(struct listreq));
+ if( lio_req == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ for(l=lio_req,a=addr,o=offset,i=0;
+ i < nents;
+ l++, a+=nbytes, o+=nbytes, i++) {
+
+ aio_id = aio_register(fd, aio_strat, signo);
+ aiop = aio_slot(aio_id);
+ status->aioid[i] = aio_id;
+
+ l->li_opcode = (sysc->sy_flags & SY_WRITE) ? LO_WRITE : LO_READ;
+ l->li_offset = o;
+ l->li_fildes = fd;
+ l->li_buf = a;
+ l->li_nbyte = nbytes;
+ l->li_status = &aiop->iosw;
+ l->li_signo = signo;
+ l->li_nstride = nstrides;
+ l->li_filstride = 0;
+ l->li_memstride = 0;
+ l->li_drvr = 0;
+ l->li_flags = LF_LSEEK;
+ }
+
+ status->aioid[nents] = -1; /* end sentinel */
+
+ if( (status->rval = listio(lc, lio_req, nents)) == -1) {
+ status->err = errno;
+ }
+
+ free(lio_req);
+ return(status);
+}
+
+/*
+ * Calculate the size of a request in bytes and min/max boundaries
+ *
+ * This assumes filestride & memstride = 0.
+ */
+int
+listio_mem(struct io_req *req, int offset, int fmstride,
+ int *min, int *max)
+{
+ int i, size;
+
+ size = stride_bounds(offset, fmstride,
+ req->r_data.io.r_nstrides*req->r_data.io.r_nent,
+ req->r_data.io.r_nbytes, min, max);
+ return(size);
+}
+
+char *
+fmt_listio(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+ static char *errbuf = NULL;
+ char *cp;
+ char *c, *opcode;
+ int i;
+
+ if(errbuf == NULL){
+ errbuf = (char *)malloc(32768);
+ if( errbuf == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ }
+
+ c = (sy->sy_flags & SY_ASYNC) ? "lc_wait" : "lc_start";
+
+ cp = errbuf;
+ cp += sprintf(cp, "syscall: listio(%s, (?), %d)\n",
+ c, req->r_data.io.r_nent);
+
+ cp += sprintf(cp, " data buffer at %#o\n", addr);
+
+ return(errbuf);
+}
+#endif /* CRAY */
+
+#ifdef sgi
+struct status *
+sy_pread(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+ int rc;
+ struct status *status;
+
+ rc = pread(fd, addr, req->r_data.io.r_nbytes,
+ req->r_data.io.r_offset);
+
+ status = (struct status *)malloc(sizeof(struct status));
+ if( status == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ status->aioid = NULL;
+ status->rval = rc;
+ status->err = errno;
+
+ return(status);
+}
+
+struct status *
+sy_pwrite(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+ int rc;
+ struct status *status;
+
+ rc = pwrite(fd, addr, req->r_data.io.r_nbytes,
+ req->r_data.io.r_offset);
+
+ status = (struct status *)malloc(sizeof(struct status));
+ if( status == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ status->aioid = NULL;
+ status->rval = rc;
+ status->err = errno;
+
+ return(status);
+}
+
+char *
+fmt_pread(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+ static char *errbuf = NULL;
+ char *cp;
+
+ if(errbuf == NULL){
+ errbuf = (char *)malloc(32768);
+ if( errbuf == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ }
+
+ cp = errbuf;
+ cp += sprintf(cp, "syscall: %s(%d, 0x%lx, %d)\n",
+ sy->sy_name, fd, addr, req->r_data.io.r_nbytes);
+ return(errbuf);
+}
+#endif /* sgi */
+
+#ifndef CRAY
+struct status *
+sy_readv(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+ struct status *sy_rwv();
+ return sy_rwv(req, sysc, fd, addr, 0);
+}
+
+struct status *
+sy_writev(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+ struct status *sy_rwv();
+ return sy_rwv(req, sysc, fd, addr, 1);
+}
+
+struct status *
+sy_rwv(req, sysc, fd, addr, rw)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+int rw;
+{
+ int rc;
+ struct status *status;
+ struct iovec iov[2];
+
+ status = (struct status *)malloc(sizeof(struct status));
+ if( status == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ status->aioid = NULL;
+
+ /* move to the desired file position. */
+ if ((rc=lseek(fd, req->r_data.io.r_offset, SEEK_SET)) == -1) {
+ status->rval = rc;
+ status->err = errno;
+ return(status);
+ }
+
+ iov[0].iov_base = addr;
+ iov[0].iov_len = req->r_data.io.r_nbytes;
+
+ if(rw)
+ rc = writev(fd, iov, 1);
+ else
+ rc = readv(fd, iov, 1);
+ status->aioid = NULL;
+ status->rval = rc;
+ status->err = errno;
+ return(status);
+}
+
+char *
+fmt_readv(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+ static char errbuf[32768];
+ char *cp;
+
+ cp = errbuf;
+ cp += sprintf(cp, "syscall: %s(%d, (iov on stack), 1)\n",
+ sy->sy_name, fd);
+ return(errbuf);
+}
+#endif /* !CRAY */
+
+#ifdef sgi
+struct status *
+sy_aread(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+ struct status *sy_arw();
+ return sy_arw(req, sysc, fd, addr, 0);
+}
+
+struct status *
+sy_awrite(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+ struct status *sy_arw();
+ return sy_arw(req, sysc, fd, addr, 1);
+}
+
+/*
+ #define sy_aread(A, B, C, D) sy_arw(A, B, C, D, 0)
+ #define sy_awrite(A, B, C, D) sy_arw(A, B, C, D, 1)
+ */
+
+struct status *
+sy_arw(req, sysc, fd, addr, rw)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+int rw;
+{
+ /* POSIX 1003.1b-1993 Async read */
+ struct status *status;
+ int rc;
+ int aio_id, aio_strat, signo;
+ struct aio_info *aiop;
+
+ status = (struct status *)malloc(sizeof(struct status));
+ if( status == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ aio_strat = req->r_data.io.r_aio_strat;
+ signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0;
+
+ aio_id = aio_register(fd, aio_strat, signo);
+ aiop = aio_slot(aio_id);
+
+ memset( (void *)&aiop->aiocb, 0, sizeof(aiocb_t));
+
+ aiop->aiocb.aio_fildes = fd;
+ aiop->aiocb.aio_nbytes = req->r_data.io.r_nbytes;
+ aiop->aiocb.aio_offset = req->r_data.io.r_offset;
+ aiop->aiocb.aio_buf = addr;
+ aiop->aiocb.aio_reqprio = 0; /* must be 0 */
+ aiop->aiocb.aio_lio_opcode = 0;
+
+ if(aio_strat == A_SIGNAL) { /* siginfo(2) stuff */
+ aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+ aiop->aiocb.aio_sigevent.sigev_signo = signo;
+ } else if(aio_strat == A_CALLBACK) {
+ aiop->aiocb.aio_sigevent.sigev_signo = 0;
+ aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_CALLBACK;
+ aiop->aiocb.aio_sigevent.sigev_func = cb_handler;
+ aiop->aiocb.aio_sigevent.sigev_value.sival_int = aio_id;
+ } else {
+ aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_NONE;
+ aiop->aiocb.aio_sigevent.sigev_signo = 0;
+ }
+
+ if(rw)
+ rc = aio_write(&aiop->aiocb);
+ else
+ rc = aio_read(&aiop->aiocb);
+
+ status->aioid = (int *)malloc( 2 * sizeof(int) );
+ if( status->aioid == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ status->aioid[0] = aio_id;
+ status->aioid[1] = -1;
+ status->rval = rc;
+ status->err = errno;
+ return(status);
+}
+
+char *
+fmt_aread(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+ static char errbuf[32768];
+ char *cp;
+
+ cp = errbuf;
+ cp += sprintf(cp, "syscall: %s(&aiop->aiocb)\n",
+ sy->sy_name);
+ return(errbuf);
+}
+#endif /* sgi */
+
+#ifndef CRAY
+
+struct status *
+sy_mmread(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+ struct status *sy_mmrw();
+ return sy_mmrw(req, sysc, fd, addr, 0);
+}
+
+struct status *
+sy_mmwrite(req, sysc, fd, addr)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+{
+ struct status *sy_mmrw();
+ return sy_mmrw(req, sysc, fd, addr, 1);
+}
+
+struct status *
+sy_mmrw(req, sysc, fd, addr, rw)
+struct io_req *req;
+struct syscall_info *sysc;
+int fd;
+char *addr;
+int rw;
+{
+ /*
+ * mmap read/write
+ * This version is oriented towards mmaping the file to memory
+ * ONCE and keeping it mapped.
+ */
+ struct status *status;
+ void *mrc, *memaddr;
+ struct fd_cache *fdc;
+ struct stat sbuf;
+
+ status = (struct status *)malloc(sizeof(struct status));
+ if( status == NULL ){
+ doio_fprintf(stderr, "malloc failed, %s/%d\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ status->aioid = NULL;
+ status->rval = -1;
+
+ fdc = alloc_fdcache(req->r_data.io.r_file, req->r_data.io.r_oflags);
+
+ if( fdc->c_memaddr == NULL ) {
+ if( fstat(fd, &sbuf) < 0 ){
+ doio_fprintf(stderr, "fstat failed, errno=%d\n",
+ errno);
+ status->err = errno;
+ return(status);
+ }
+
+ fdc->c_memlen = (int)sbuf.st_size;
+ mrc = mmap(NULL, (int)sbuf.st_size,
+ rw ? PROT_WRITE|PROT_READ : PROT_READ,
+ MAP_SHARED, fd, 0);
+
+ if( mrc == MAP_FAILED ) {
+ doio_fprintf(stderr, "mmap() failed - 0x%lx %d\n",
+ mrc, errno);
+ status->err = errno;
+ return(status);
+ }
+
+ fdc->c_memaddr = mrc;
+ }
+
+ memaddr = (void *)((char *)fdc->c_memaddr + req->r_data.io.r_offset);
+
+ active_mmap_rw = 1;
+ if(rw)
+ memcpy(memaddr, addr, req->r_data.io.r_nbytes);
+ else
+ memcpy(addr, memaddr, req->r_data.io.r_nbytes);
+ active_mmap_rw = 0;
+
+ status->rval = req->r_data.io.r_nbytes;
+ status->err = 0;
+ return(status);
+}
+
+char *
+fmt_mmrw(struct io_req *req, struct syscall_info *sy, int fd, char *addr)
+{
+ static char errbuf[32768];
+ char *cp;
+ struct fd_cache *fdc;
+ void *memaddr;
+
+ fdc = alloc_fdcache(req->r_data.io.r_file, req->r_data.io.r_oflags);
+
+ cp = errbuf;
+ cp += sprintf(cp, "syscall: %s(NULL, %d, %s, MAP_SHARED, %d, 0)\n",
+ sy->sy_name,
+ fdc->c_memlen,
+ (sy->sy_flags & SY_WRITE) ? "PROT_WRITE" : "PROT_READ",
+ fd);
+
+ cp += sprintf(cp, "\tfile is mmaped to: 0x%lx\n",
+ fdc->c_memaddr);
+
+ memaddr = (void *)((char *)fdc->c_memaddr + req->r_data.io.r_offset);
+
+ cp += sprintf(cp, "\tfile-mem=0x%lx, length=%d, buffer=0x%lx\n",
+ memaddr, req->r_data.io.r_nbytes, addr);
+
+ return(errbuf);
+}
+#endif /* !CRAY */
+
+struct syscall_info syscalls[] = {
+#ifdef CRAY
+ { "listio-read-sync", LREAD,
+ sy_listio, NULL, fmt_listio,
+ SY_IOSW
+ },
+ { "listio-read-strides-sync", LSREAD,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW
+ },
+ { "listio-read-reqs-sync", LEREAD,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW
+ },
+ { "listio-read-async", LREADA,
+ sy_listio, NULL, fmt_listio,
+ SY_IOSW | SY_ASYNC
+ },
+ { "listio-read-strides-async", LSREADA,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW | SY_ASYNC
+ },
+ { "listio-read-reqs-async", LEREADA,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW | SY_ASYNC
+ },
+ { "listio-write-sync", LWRITE,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW | SY_WRITE
+ },
+ { "listio-write-strides-sync", LSWRITE,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW | SY_WRITE
+ },
+ { "listio-write-reqs-sync", LEWRITE,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW | SY_WRITE
+ },
+ { "listio-write-async", LWRITEA,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW | SY_WRITE | SY_ASYNC
+ },
+ { "listio-write-strides-async", LSWRITEA,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW | SY_WRITE | SY_ASYNC
+ },
+ { "listio-write-reqs-async", LEWRITEA,
+ sy_listio, listio_mem, fmt_listio,
+ SY_IOSW | SY_WRITE | SY_ASYNC
+ },
+#endif
+
+#ifdef sgi
+ { "aread", AREAD,
+ sy_aread, NULL, fmt_aread,
+ SY_IOSW | SY_ASYNC
+ },
+ { "awrite", AWRITE,
+ sy_awrite, NULL, fmt_aread,
+ SY_IOSW | SY_WRITE | SY_ASYNC
+ },
+ { "pread", PREAD,
+ sy_pread, NULL, fmt_pread,
+ 0
+ },
+ { "pwrite", PWRITE,
+ sy_pwrite, NULL, fmt_pread,
+ SY_WRITE
+ },
+#endif
+
+#ifndef CRAY
+ { "readv", READV,
+ sy_readv, NULL, fmt_readv,
+ 0
+ },
+ { "writev", WRITEV,
+ sy_writev, NULL, fmt_readv,
+ SY_WRITE
+ },
+ { "mmap-read", MMAPR,
+ sy_mmread, NULL, fmt_mmrw,
+ 0
+ },
+ { "mmap-write", MMAPW,
+ sy_mmwrite, NULL, fmt_mmrw,
+ SY_WRITE
+ },
+#endif
+
+ { NULL, 0,
+ 0, 0, 0,
+ 0
+ },
+};
+
+int
+do_rw(req)
+ struct io_req *req;
+{
+ static int pid = -1;
+ int fd, offset, nbytes, nstrides, nents, oflags;
+ int rval, mem_needed, i;
+ int logged_write, got_lock, woffset, pattern;
+ int min_byte, max_byte;
+ char *addr, *file, *msg;
+ struct status *s;
+ struct wlog_rec wrec;
+ struct syscall_info *sy;
+ struct aio_info *aiop;
+#ifdef CRAY
+ struct iosw *iosw;
+#endif
+#ifdef sgi
+ struct fd_cache *fdc;
+#endif
+
+ /*
+ * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+ * r_nbytes are at the same offset in the read_req and reada_req
+ * structures.
+ */
+ file = req->r_data.io.r_file;
+ oflags = req->r_data.io.r_oflags;
+ offset = req->r_data.io.r_offset;
+ nbytes = req->r_data.io.r_nbytes;
+ nstrides= req->r_data.io.r_nstrides;
+ nents = req->r_data.io.r_nent;
+ pattern = req->r_data.io.r_pattern;
+
+ if( nents >= MAX_AIO ) {
+ doio_fprintf(stderr, "do_rw: too many list requests, %d. Maximum is %d\n",
+ nents, MAX_AIO);
+ return(-1);
+ }
+
+ /*
+ * look up system call info
+ */
+ for(sy=syscalls; sy->sy_name != NULL && sy->sy_type != req->r_type; sy++)
+ ;
+
+ if(sy->sy_name == NULL) {
+ doio_fprintf(stderr, "do_rw: unknown r_type %d.\n",
+ req->r_type);
+ return(-1);
+ }
+
+ /*
+ * Get an open file descriptor
+ * Note: must be done before memory allocation so that the direct i/o
+ * information is available in mem. allocate
+ */
+
+ if ((fd = alloc_fd(file, oflags)) == -1)
+ return -1;
+
+ /*
+ * Allocate core memory and possibly sds space. Initialize the
+ * data to be written. Make sure we get enough, based on the
+ * memstride.
+ *
+ * need:
+ * 1 extra word for possible partial-word address "bump"
+ * 1 extra word for dynamic pattern overrun
+ * MPP_BUMP extra words for T3E non-hw-aligned memory address.
+ */
+
+ if( sy->sy_buffer != NULL ) {
+ mem_needed = (*sy->sy_buffer)(req, 0, 0, NULL, NULL);
+ } else {
+ mem_needed = nbytes;
+ }
+
+#ifdef CRAY
+ if ((rval = alloc_mem(mem_needed + wtob(1) * 2 + MPP_BUMP * sizeof(UINT64_T))) < 0) {
+ return rval;
+ }
+#else
+#ifdef sgi
+ /* get memory alignment for using DIRECT I/O */
+ fdc = alloc_fdcache(file, oflags);
+
+ if ((rval = alloc_mem(mem_needed + wtob(1) * 2 + fdc->c_memalign)) < 0) {
+ return rval;
+ }
+#else
+ /* what is !CRAY && !sgi ? */
+ if ((rval = alloc_mem(mem_needed + wtob(1) * 2)) < 0) {
+ return rval;
+ }
+#endif /* sgi */
+#endif /* CRAY */
+
+ Pattern[0] = pattern;
+
+ /*
+ * Allocate SDS space for backdoor write if desired
+ */
+
+ if (oflags & O_SSD) {
+#ifdef CRAY
+#ifndef _CRAYMPP
+ if (alloc_sds(nbytes) == -1)
+ return -1;
+
+ if( sy->sy_flags & SY_WRITE ) {
+ /*pattern_fill(Memptr, mem_needed, Pattern, Pattern_Length, 0);*/
+ (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0);
+
+ if (sswrite((long)Memptr, Sdsptr, btoc(mem_needed)) == -1) {
+ doio_fprintf(stderr, "sswrite(%d, %d, %d) failed: %s (%d)\n",
+ (long)Memptr, Sdsptr,
+ btoc(mem_needed), SYSERR, errno);
+ fflush(stderr);
+ return -1;
+ }
+ }
+
+ addr = (char *)Sdsptr;
+#else
+ doio_fprintf(stderr, "Invalid O_SSD flag was generated for MPP system\n");
+ fflush(stderr);
+ return -1;
+#endif /* _CRAYMPP */
+#else /* CRAY */
+ doio_fprintf(stderr, "Invalid O_SSD flag was generated for non-Cray system\n");
+ fflush(stderr);
+ return -1;
+#endif /* CRAY */
+ } else {
+ addr = Memptr;
+
+ /*
+ * if io is not raw, bump the offset by a random amount
+ * to generate non-word-aligned io.
+ *
+ * On MPP systems, raw I/O must start on an 0x80 byte boundary.
+ * For non-aligned I/O, bump the address from 1 to 8 words.
+ */
+
+ if (! (req->r_data.io.r_uflags & F_WORD_ALIGNED)) {
+#ifdef _CRAYMPP
+ addr += random_range(0, MPP_BUMP, 1, NULL) * sizeof(int);
+#endif
+ addr += random_range(0, wtob(1) - 1, 1, NULL);
+ }
+
+#ifdef sgi
+ /*
+ * Force memory alignment for Direct I/O
+ */
+ if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) {
+ addr += fdc->c_memalign - ((long)addr % fdc->c_memalign);
+ }
+#endif
+
+ /*
+ * FILL must be done on a word-aligned buffer.
+ * Call the fill function with Memptr which is aligned,
+ * then memmove it to the right place.
+ */
+ if (sy->sy_flags & SY_WRITE) {
+ (*Data_Fill)(Memptr, mem_needed, Pattern, Pattern_Length, 0);
+ if( addr != Memptr )
+ memmove( addr, Memptr, mem_needed);
+ }
+ }
+
+ rval = 0;
+ got_lock = 0;
+ logged_write = 0;
+
+ /*
+ * Lock data if this is a write and locking option is set
+ */
+ if (sy->sy_flags & SY_WRITE && k_opt) {
+ if( sy->sy_buffer != NULL ) {
+ (*sy->sy_buffer)(req, offset, 0, &min_byte, &max_byte);
+ } else {
+ min_byte = offset;
+ max_byte = offset + (nbytes * nstrides * nents);
+ }
+
+ if (lock_file_region(file, fd, F_WRLCK,
+ min_byte, (max_byte-min_byte+1)) < 0) {
+ doio_fprintf(stderr,
+ "file lock failed:\n%s\n",
+ fmt_ioreq(req, sy, fd));
+ doio_fprintf(stderr,
+ " buffer(req, %d, 0, 0x%x, 0x%x)\n",
+ offset, min_byte, max_byte);
+ alloc_mem(-1);
+ exit(E_INTERNAL);
+ }
+
+ got_lock = 1;
+ }
+
+ /*
+ * Write a preliminary write-log entry. This is done so that
+ * doio_check can do corruption detection across an interrupt/crash.
+ * Note that w_done is set to 0. If doio_check sees this, it
+ * re-creates the file extents as if the write completed, but does not
+ * do any checking - see comments in doio_check for more details.
+ */
+
+ if (sy->sy_flags & SY_WRITE && w_opt) {
+ if (pid == -1) {
+ pid = getpid();
+ }
+
+ wrec.w_async = (sy->sy_flags & SY_ASYNC) ? 1 : 0;
+ wrec.w_oflags = oflags;
+ wrec.w_pid = pid;
+ wrec.w_offset = offset;
+ wrec.w_nbytes = nbytes; /* mem_needed -- total length */
+
+ wrec.w_pathlen = strlen(file);
+ memcpy(wrec.w_path, file, wrec.w_pathlen);
+ wrec.w_hostlen = strlen(Host);
+ memcpy(wrec.w_host, Host, wrec.w_hostlen);
+ wrec.w_patternlen = Pattern_Length;
+ memcpy(wrec.w_pattern, Pattern, wrec.w_patternlen);
+
+ wrec.w_done = 0;
+
+ if ((woffset = wlog_record_write(&Wlog, &wrec, -1)) == -1) {
+ doio_fprintf(stderr,
+ "Could not append to write-log: %s (%d)\n",
+ SYSERR, errno);
+ } else {
+ logged_write = 1;
+ }
+ }
+
+ s = (*sy->sy_syscall)(req, sy, fd, addr);
+
+ if( s->rval == -1 ) {
+ doio_fprintf(stderr,
+ "%s() request failed: %s (%d)\n%s\n%s\n",
+ sy->sy_name, SYSERR, errno,
+ fmt_ioreq(req, sy, fd),
+ (*sy->sy_format)(req, sy, fd, addr));
+
+ doio_upanic(U_RVAL);
+
+ for(i=0; i < nents; i++) {
+ if(s->aioid == NULL)
+ break;
+ aio_unregister(s->aioid[i]);
+ }
+ rval = -1;
+ } else {
+ /*
+ * If the syscall was async, wait for I/O to complete
+ */
+#ifndef linux
+ if(sy->sy_flags & SY_ASYNC) {
+ for(i=0; i < nents; i++) {
+ aio_wait(s->aioid[i]);
+ }
+ }
+#endif
+
+ /*
+ * Check the syscall how-much-data-written return. Look
+ * for this in either the return value or the 'iosw'
+ * structure.
+ */
+
+ if( sy->sy_flags & SY_IOSW ) {
+#ifdef CRAY
+ for( i=0; i < nents; i++ ) {
+ if(s->aioid == NULL)
+ break; /* >>> error condition? */
+ aiop = aio_slot(s->aioid[i]);
+ iosw = &aiop->iosw;
+ if(iosw->sw_error != 0) {
+ doio_fprintf(stderr,
+ "%s() iosw error set: %s\n%s\n%s\n",
+ sy->sy_name,
+ sys_errlist[iosw->sw_error],
+ fmt_ioreq(req, sy, fd),
+ (*sy->sy_format)(req, sy, fd, addr));
+ doio_upanic(U_IOSW);
+ rval = -1;
+ } else if(iosw->sw_count != nbytes*nstrides) {
+ doio_fprintf(stderr,
+ "Bad iosw from %s() #%d\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n%s\n",
+ sy->sy_name, i,
+ 1, 0, nbytes*nstrides,
+ iosw->sw_flag,
+ iosw->sw_error,
+ iosw->sw_count,
+ fmt_ioreq(req, sy, fd),
+ (*sy->sy_format)(req, sy, fd, addr));
+ doio_upanic(U_IOSW);
+ rval = -1;
+ }
+
+ aio_unregister(s->aioid[i]);
+ }
+#endif /* CRAY */
+#ifdef sgi
+ for( i=0; s->aioid[i] != -1; i++ ) {
+ if(s->aioid == NULL) {
+ doio_fprintf(stderr,
+ "aioid == NULL!\n");
+ break;
+ }
+ aiop = aio_slot(s->aioid[i]);
+
+ /*
+ * make sure the io completed without error
+ */
+ if (aiop->aio_errno != 0) {
+ doio_fprintf(stderr,
+ "%s() aio error set: %s (%d)\n%s\n%s\n",
+ sy->sy_name,
+ sys_errlist[aiop->aio_errno],
+ aiop->aio_errno,
+ fmt_ioreq(req, sy, fd),
+ (*sy->sy_format)(req, sy, fd, addr));
+ doio_upanic(U_IOSW);
+ rval = -1;
+ } else if (aiop->aio_ret != nbytes) {
+ doio_fprintf(stderr,
+ "Bad aio return from %s() #%d\nExpected (%d,%d), got (%d,%d)\n%s\n%s\n",
+ sy->sy_name, i,
+ 0, nbytes,
+ aiop->aio_errno,
+ aiop->aio_ret,
+ fmt_ioreq(req, sy, fd),
+ (*sy->sy_format)(req, sy, fd, addr));
+ aio_unregister(s->aioid[i]);
+ doio_upanic(U_IOSW);
+ return -1;
+ } else {
+ aio_unregister(s->aioid[i]);
+ rval = 0;
+ }
+ }
+#endif /* sgi */
+ } else {
+
+ if(s->rval != mem_needed) {
+ doio_fprintf(stderr,
+ "%s() request returned wrong # of bytes - expected %d, got %d\n%s\n%s\n",
+ sy->sy_name, nbytes, s->rval,
+ fmt_ioreq(req, sy, fd),
+ (*sy->sy_format)(req, sy, fd, addr));
+ rval = -1;
+ doio_upanic(U_RVAL);
+ }
+ }
+ }
+
+
+ /*
+ * Verify that the data was written correctly - check_file() returns
+ * a non-null pointer which contains an error message if there are
+ * problems.
+ */
+
+ if ( rval == 0 && sy->sy_flags & SY_WRITE && v_opt) {
+ msg = check_file(file, offset, nbytes*nstrides*nents,
+ Pattern, Pattern_Length, 0,
+ oflags & O_PARALLEL);
+ if (msg != NULL) {
+ doio_fprintf(stderr, "%s\n%s\n%s\n",
+ msg,
+ fmt_ioreq(req, sy, fd),
+ (*sy->sy_format)(req, sy, fd, addr));
+ doio_upanic(U_CORRUPTION);
+ exit(E_COMPARE);
+ }
+ }
+
+ /*
+ * General cleanup ...
+ *
+ * Write extent information to the write-log, so that doio_check can do
+ * corruption detection. Note that w_done is set to 1, indicating that
+ * the write has been verified as complete. We don't need to write the
+ * filename on the second logging.
+ */
+
+ if (w_opt && logged_write) {
+ wrec.w_done = 1;
+ wlog_record_write(&Wlog, &wrec, woffset);
+ }
+
+ /*
+ * Unlock file region if necessary
+ */
+
+ if (got_lock) {
+ if (lock_file_region(file, fd, F_UNLCK,
+ min_byte, (max_byte-min_byte+1)) < 0) {
+ alloc_mem(-1);
+ exit(E_INTERNAL);
+ }
+ }
+
+ if(s->aioid != NULL)
+ free(s->aioid);
+ free(s);
+ return (rval == -1) ? -1 : 0;
+}
+
+
+/*
+ * fcntl-based requests
+ * - F_FRESVSP
+ * - F_UNRESVSP
+ * - F_FSYNC
+ */
+#ifdef sgi
+int
+do_fcntl(req)
+ struct io_req *req;
+{
+ int fd, oflags, offset, nbytes;
+ int rval, op;
+ int got_lock;
+ int min_byte, max_byte;
+ char *file, *msg;
+ struct flock flk;
+
+ /*
+ * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+ * r_nbytes are at the same offset in the read_req and reada_req
+ * structures.
+ */
+ file = req->r_data.io.r_file;
+ oflags = req->r_data.io.r_oflags;
+ offset = req->r_data.io.r_offset;
+ nbytes = req->r_data.io.r_nbytes;
+
+ flk.l_type=0;
+ flk.l_whence=SEEK_SET;
+ flk.l_start=offset;
+ flk.l_len=nbytes;
+
+ /*
+ * Get an open file descriptor
+ */
+
+ if ((fd = alloc_fd(file, oflags)) == -1)
+ return -1;
+
+ rval = 0;
+ got_lock = 0;
+
+ /*
+ * Lock data if this is locking option is set
+ */
+ if (k_opt) {
+ min_byte = offset;
+ max_byte = offset + nbytes;
+
+ if (lock_file_region(file, fd, F_WRLCK,
+ min_byte, (nbytes+1)) < 0) {
+ doio_fprintf(stderr,
+ "file lock failed:\n");
+ doio_fprintf(stderr,
+ " buffer(req, %d, 0, 0x%x, 0x%x)\n",
+ offset, min_byte, max_byte);
+ alloc_mem(-1);
+ exit(E_INTERNAL);
+ }
+
+ got_lock = 1;
+ }
+
+ switch (req->r_type) {
+ case RESVSP: op=F_RESVSP; msg="f_resvsp"; break;
+ case UNRESVSP: op=F_UNRESVSP; msg="f_unresvsp"; break;
+#ifdef F_FSYNC
+ case DFFSYNC: op=F_FSYNC; msg="f_fsync"; break;
+#endif
+ }
+
+ rval = fcntl(fd, op, &flk);
+
+ if( rval == -1 ) {
+ doio_fprintf(stderr,
+ "fcntl %s request failed: %s (%d)\n\tfcntl(%d, %s %d, {%d %lld ==> %lld}\n",
+ msg, SYSERR, errno,
+ fd, msg, op, flk.l_whence,
+ (long long)flk.l_start,
+ (long long)flk.l_len);
+
+ doio_upanic(U_RVAL);
+ rval = -1;
+ }
+
+ /*
+ * Unlock file region if necessary
+ */
+
+ if (got_lock) {
+ if (lock_file_region(file, fd, F_UNLCK,
+ min_byte, (max_byte-min_byte+1)) < 0) {
+ alloc_mem(-1);
+ exit(E_INTERNAL);
+ }
+ }
+
+ return (rval == -1) ? -1 : 0;
+}
+#endif
+
+/*
+ * fsync(2) and fdatasync(2)
+ */
+#ifndef CRAY
+int
+do_sync(req)
+ struct io_req *req;
+{
+ int fd, oflags;
+ int rval;
+ char *file;
+
+ /*
+ * Initialize common fields - assumes r_oflags, r_file, r_offset, and
+ * r_nbytes are at the same offset in the read_req and reada_req
+ * structures.
+ */
+ file = req->r_data.io.r_file;
+ oflags = req->r_data.io.r_oflags;
+
+ /*
+ * Get an open file descriptor
+ */
+
+ if ((fd = alloc_fd(file, oflags)) == -1)
+ return -1;
+
+ rval = 0;
+ switch(req->r_type) {
+ case FSYNC2:
+ rval = fsync(fd);
+ break;
+ case FDATASYNC:
+ rval = fdatasync(fd);
+ break;
+ default:
+ rval = -1;
+ }
+ return (rval == -1) ? -1 : 0;
+}
+#endif
+
+
+int
+doio_pat_fill(char *addr, int mem_needed, char *Pattern, int Pattern_Length,
+ int shift)
+{
+ return pattern_fill(addr, mem_needed, Pattern, Pattern_Length, 0);
+}
+
+char *
+doio_pat_check(buf, offset, length, pattern, pattern_length, patshift)
+char *buf;
+int offset;
+int length;
+char *pattern;
+int pattern_length;
+int patshift;
+{
+ static char errbuf[4096];
+ int nb, i, pattern_index;
+ char *cp, *bufend, *ep;
+ char actual[33], expected[33];
+
+ if (pattern_check(buf, length, pattern, pattern_length, patshift) != 0) {
+ ep = errbuf;
+ ep += sprintf(ep, "Corrupt regions follow - unprintable chars are represented as '.'\n");
+ ep += sprintf(ep, "-----------------------------------------------------------------\n");
+
+ pattern_index = patshift % pattern_length;;
+ cp = buf;
+ bufend = buf + length;
+
+ while (cp < bufend) {
+ if (*cp != pattern[pattern_index]) {
+ nb = bufend - cp;
+ if (nb > sizeof(expected)-1) {
+ nb = sizeof(expected)-1;
+ }
+
+ ep += sprintf(ep, "corrupt bytes starting at file offset %d\n", offset + (int)(cp-buf));
+
+ /*
+ * Fill in the expected and actual patterns
+ */
+ bzero(expected, sizeof(expected));
+ bzero(actual, sizeof(actual));
+
+ for (i = 0; i < nb; i++) {
+ expected[i] = pattern[(pattern_index + i) % pattern_length];
+ if (! isprint(expected[i])) {
+ expected[i] = '.';
+ }
+
+ actual[i] = cp[i];
+ if (! isprint(actual[i])) {
+ actual[i] = '.';
+ }
+ }
+
+ ep += sprintf(ep, " 1st %2d expected bytes: %s\n", nb, expected);
+ ep += sprintf(ep, " 1st %2d actual bytes: %s\n", nb, actual);
+ fflush(stderr);
+ return errbuf;
+ } else {
+ cp++;
+ pattern_index++;
+
+ if (pattern_index == pattern_length) {
+ pattern_index = 0;
+ }
+ }
+ }
+ return errbuf;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Check the contents of a file beginning at offset, for length bytes. It
+ * is assumed that there is a string of pattern bytes in this area of the
+ * file. Use normal buffered reads to do the verification.
+ *
+ * If there is a data mismatch, write a detailed message into a static buffer
+ * suitable for the caller to print. Otherwise print NULL.
+ *
+ * The fsa flag is set to non-zero if the buffer should be read back through
+ * the FSA (unicos/mk). This implies the file will be opened
+ * O_PARALLEL|O_RAW|O_WELLFORMED to do the validation. We must do this because
+ * FSA will not allow the file to be opened for buffered io if it was
+ * previously opened for O_PARALLEL io.
+ */
+
+char *
+check_file(file, offset, length, pattern, pattern_length, patshift, fsa)
+char *file;
+int offset;
+int length;
+char *pattern;
+int pattern_length;
+int patshift;
+int fsa;
+{
+ static char errbuf[4096];
+ int fd, nb, flags;
+ char *buf, *em, *ep;
+#ifdef sgi
+ struct fd_cache *fdc;
+#endif
+
+ buf = Memptr;
+
+ if (V_opt) {
+ flags = Validation_Flags | O_RDONLY;
+ } else {
+ flags = O_RDONLY;
+ if (fsa) {
+#ifdef CRAY
+ flags |= O_PARALLEL | O_RAW | O_WELLFORMED;
+#endif
+ }
+ }
+
+ if ((fd = alloc_fd(file, flags)) == -1) {
+ sprintf(errbuf,
+ "Could not open file %s with flags %#o (%s) for data comparison: %s (%d)\n",
+ file, flags, format_oflags(flags),
+ SYSERR, errno);
+ return errbuf;
+ }
+
+ if (lseek(fd, offset, SEEK_SET) == -1) {
+ sprintf(errbuf,
+ "Could not lseek to offset %d in %s for verification: %s (%d)\n",
+ offset, file, SYSERR, errno);
+ return errbuf;
+ }
+
+#ifdef sgi
+ /* Irix: Guarantee a properly aligned address on Direct I/O */
+ fdc = alloc_fdcache(file, flags);
+ if( (flags & O_DIRECT) && ((long)buf % fdc->c_memalign != 0) ) {
+ buf += fdc->c_memalign - ((long)buf % fdc->c_memalign);
+ }
+#endif
+
+ if ((nb = read(fd, buf, length)) == -1) {
+#ifdef sgi
+ sprintf(errbuf,
+ "Could not read %d bytes from %s for verification: %s (%d)\n\tread(%d, 0x%lx, %d)\n\tbuf %% alignment(%d) = %ld\n",
+ length, file, SYSERR, errno,
+ fd, buf, length,
+ fdc->c_memalign, (long)buf % fdc->c_memalign);
+#else
+ sprintf(errbuf,
+ "Could not read %d bytes from %s for verification: %s (%d)\n",
+ length, file, SYSERR, errno);
+
+#endif
+ return errbuf;
+ }
+
+ if (nb != length) {
+ sprintf(errbuf,
+ "Read wrong # bytes from %s. Expected %d, got %d\n",
+ file, length, nb);
+ return errbuf;
+ }
+
+ if( (em = (*Data_Check)(buf, offset, length, pattern, pattern_length, patshift)) != NULL ) {
+ ep = errbuf;
+ ep += sprintf(ep, "*** DATA COMPARISON ERROR ***\n");
+ ep += sprintf(ep, "check_file(%s, %d, %d, %s, %d, %d) failed\n\n",
+ file, offset, length, pattern, pattern_length, patshift);
+ ep += sprintf(ep, "Comparison fd is %d, with open flags %#o\n",
+ fd, flags);
+ strcpy(ep, em);
+ return(errbuf);
+ }
+ return NULL;
+}
+
+/*
+ * Function to single-thread stdio output.
+ */
+
+int
+doio_fprintf(FILE *stream, char *format, ...)
+{
+ static int pid = -1;
+ char *date;
+ int rval;
+ struct flock flk;
+ va_list arglist;
+
+ date = hms(time(0));
+
+ if (pid == -1) {
+ pid = getpid();
+ }
+
+ flk.l_whence = flk.l_start = flk.l_len = 0;
+ flk.l_type = F_WRLCK;
+ fcntl(fileno(stream), F_SETLKW, &flk);
+
+ va_start(arglist, format);
+ rval = fprintf(stream, "\n%s%s (%5d) %s\n", Prog, TagName, pid, date);
+ rval += fprintf(stream, "---------------------\n");
+ vfprintf(stream, format, arglist);
+ va_end(arglist);
+
+ fflush(stream);
+
+ flk.l_type = F_UNLCK;
+ fcntl(fileno(stream), F_SETLKW, &flk);
+
+ return rval;
+}
+
+/*
+ * Simple function for allocating core memory. Uses Memsize and Memptr to
+ * keep track of the current amount allocated.
+ */
+#ifndef CRAY
+int
+alloc_mem(nbytes)
+int nbytes;
+{
+ char *cp;
+ void *addr;
+ int me, flags, key, shmid;
+ static int mturn = 0; /* which memory type to use */
+ struct memalloc *M;
+ char filename[255];
+#ifdef linux
+ struct shmid_ds shm_ds;
+#endif
+
+#ifdef linux
+ bzero( &shm_ds, sizeof(struct shmid_ds) );
+#endif
+
+ /* nbytes = -1 means "free all allocated memory" */
+ if( nbytes == -1 ) {
+
+ for(me=0; me < Nmemalloc; me++) {
+ if(Memalloc[me].space == NULL)
+ continue;
+
+ switch(Memalloc[me].memtype) {
+ case MEM_DATA:
+#ifdef sgi
+ if(Memalloc[me].flags & MEMF_MPIN)
+ munpin(Memalloc[me].space,
+ Memalloc[me].size);
+#endif
+ free(Memalloc[me].space);
+ Memalloc[me].space = NULL;
+ Memptr = NULL;
+ Memsize = 0;
+ break;
+ case MEM_SHMEM:
+#ifdef sgi
+ if(Memalloc[me].flags & MEMF_MPIN)
+ munpin(Memalloc[me].space,
+ Memalloc[me].size);
+#endif
+ shmdt(Memalloc[me].space);
+ Memalloc[me].space = NULL;
+#ifdef sgi
+ shmctl(Memalloc[me].fd, IPC_RMID);
+#else
+ shmctl(Memalloc[me].fd, IPC_RMID, &shm_ds);
+#endif
+ break;
+ case MEM_MMAP:
+#ifdef sgi
+ if(Memalloc[me].flags & MEMF_MPIN)
+ munpin(Memalloc[me].space,
+ Memalloc[me].size);
+#endif
+ munmap(Memalloc[me].space,
+ Memalloc[me].size);
+ close(Memalloc[me].fd);
+ if(Memalloc[me].flags & MEMF_FILE) {
+ unlink(Memalloc[me].name);
+ }
+ Memalloc[me].space = NULL;
+ break;
+ default:
+ doio_fprintf(stderr, "alloc_mem: HELP! Unknown memory space type %d index %d\n",
+ Memalloc[me].memtype, me);
+ break;
+ }
+ }
+ return 0;
+ }
+
+ /*
+ * Select a memory area (currently round-robbin)
+ */
+
+ if(mturn >= Nmemalloc)
+ mturn=0;
+
+ M = &Memalloc[mturn];
+
+ switch(M->memtype) {
+ case MEM_DATA:
+ if( nbytes > M->size ) {
+ if( M->space != NULL ){
+#ifdef sgi
+ if( M->flags & MEMF_MPIN )
+ munpin( M->space, M->size );
+#endif
+ free(M->space);
+ }
+ M->space = NULL;
+ M->size = 0;
+ }
+
+ if( M->space == NULL ) {
+ if( (cp = malloc( nbytes )) == NULL ) {
+ doio_fprintf(stderr, "malloc(%d) failed: %s (%d)\n",
+ nbytes, SYSERR, errno);
+ return -1;
+ }
+#ifdef sgi
+ if(M->flags & MEMF_MPIN) {
+ if( mpin(cp, nbytes) == -1 ) {
+ doio_fprintf(stderr, "mpin(0x%lx, %d) failed: %s (%d)\n",
+ cp, nbytes, SYSERR, errno);
+ }
+ }
+#endif
+ M->space = (void *)cp;
+ M->size = nbytes;
+ }
+ break;
+
+ case MEM_MMAP:
+ if( nbytes > M->size ) {
+ if( M->space != NULL ) {
+#ifdef sgi
+ if( M->flags & MEMF_MPIN )
+ munpin(M->space, M->size);
+#endif
+ munmap(M->space, M->size);
+ close(M->fd);
+ if( M->flags & MEMF_FILE )
+ unlink( M->name );
+ }
+ M->space = NULL;
+ M->size = 0;
+ }
+
+ if( M->space == NULL ) {
+ if(strchr(M->name, '%')) {
+ sprintf(filename, M->name, getpid());
+ M->name = strdup(filename);
+ }
+
+ if( (M->fd = open(M->name, O_CREAT|O_RDWR, 0666)) == -1) {
+ doio_fprintf(stderr, "alloc_mmap: error %d (%s) opening '%s'\n",
+ errno, SYSERR,
+ M->name);
+ return(-1);
+ }
+
+ addr = NULL;
+ flags = 0;
+ M->size = nbytes * 4;
+
+ /* bias addr if MEMF_ADDR | MEMF_FIXADDR */
+ /* >>> how to pick a memory address? */
+
+ /* bias flags on MEMF_PRIVATE etc */
+ if(M->flags & MEMF_PRIVATE)
+ flags |= MAP_PRIVATE;
+#ifdef sgi
+ if(M->flags & MEMF_LOCAL)
+ flags |= MAP_LOCAL;
+ if(M->flags & MEMF_AUTORESRV)
+ flags |= MAP_AUTORESRV;
+ if(M->flags & MEMF_AUTOGROW)
+ flags |= MAP_AUTOGROW;
+#endif
+ if(M->flags & MEMF_SHARED)
+ flags |= MAP_SHARED;
+
+/*printf("alloc_mem, about to mmap, fd=%d, name=(%s)\n", M->fd, M->name);*/
+ if( (M->space = mmap(addr, M->size,
+ PROT_READ|PROT_WRITE,
+ flags, M->fd, 0))
+ == MAP_FAILED) {
+ doio_fprintf(stderr, "alloc_mem: mmap error. errno %d (%s)\n\tmmap(addr 0x%x, size %d, read|write 0x%x, mmap flags 0x%x [%#o], fd %d, 0)\n\tfile %s\n",
+ errno, SYSERR,
+ addr, M->size,
+ PROT_READ|PROT_WRITE,
+ flags, M->flags, M->fd,
+ M->name);
+ doio_fprintf(stderr, "\t%s%s%s%s%s",
+ (flags & MAP_PRIVATE) ? "private " : "",
+#ifdef sgi
+ (flags & MAP_LOCAL) ? "local " : "",
+ (flags & MAP_AUTORESRV) ? "autoresrv " : "",
+ (flags & MAP_AUTOGROW) ? "autogrow " : "",
+#endif
+ (flags & MAP_SHARED) ? "shared" : "");
+ return(-1);
+ }
+ }
+ break;
+
+ case MEM_SHMEM:
+ if( nbytes > M->size ) {
+ if( M->space != NULL ) {
+#ifdef sgi
+ if( M->flags & MEMF_MPIN )
+ munpin(M->space, M->size);
+#endif
+ shmdt( M->space );
+#ifdef sgi
+ shmctl( M->fd, IPC_RMID );
+#else
+ shmctl( M->fd, IPC_RMID, &shm_ds );
+#endif
+ }
+ M->space = NULL;
+ M->size = 0;
+ }
+
+ if(M->space == NULL) {
+ if(!strcmp(M->name, "private")) {
+ key = IPC_PRIVATE;
+ } else {
+ sscanf(M->name, "%i", &key);
+ }
+
+ M->size = M->nblks ? M->nblks * 512 : nbytes;
+
+ if( nbytes > M->size ){
+#ifdef DEBUG
+ doio_fprintf(stderr, "MEM_SHMEM: nblks(%d) too small: nbytes=%d Msize=%d, skipping this req.\n",
+ M->nblks, nbytes, M->size );
+#endif
+ return SKIP_REQ;
+ }
+
+ shmid = shmget(key, M->size, IPC_CREAT|0666);
+ if( shmid == -1 ) {
+ doio_fprintf(stderr, "shmget(0x%x, %d, CREAT) failed: %s (%d)\n",
+ key, M->size, SYSERR, errno);
+ return(-1);
+ }
+ M->fd = shmid;
+ M->space = shmat(shmid, NULL, SHM_RND);
+ if( M->space == (void *)-1 ) {
+ doio_fprintf(stderr, "shmat(0x%x, NULL, SHM_RND) failed: %s (%d)\n",
+ shmid, SYSERR, errno);
+ return(-1);
+ }
+#ifdef sgi
+ if(M->flags & MEMF_MPIN) {
+ if( mpin(M->space, M->size) == -1 ) {
+ doio_fprintf(stderr, "mpin(0x%lx, %d) failed: %s (%d)\n",
+ M->space, M->size, SYSERR, errno);
+ }
+ }
+#endif
+ }
+ break;
+
+ default:
+ doio_fprintf(stderr, "alloc_mem: HELP! Unknown memory space type %d index %d\n",
+ Memalloc[me].memtype, mturn);
+ break;
+ }
+
+ Memptr = M->space;
+ Memsize = M->size;
+
+ mturn++;
+ return 0;
+}
+#endif /* !CRAY */
+
+#ifdef CRAY
+int
+alloc_mem(nbytes)
+int nbytes;
+{
+ char *cp;
+ int ip;
+ static char *malloc_space;
+
+ /*
+ * The "unicos" version of this did some stuff with sbrk;
+ * this caused problems with async I/O on irix, and now appears
+ * to be causing problems with FSA I/O on unicos/mk.
+ */
+#ifdef NOTDEF
+ if (nbytes > Memsize) {
+ if ((cp = (char *)sbrk(nbytes - Memsize)) == (char *)-1) {
+ doio_fprintf(stderr, "sbrk(%d) failed: %s (%d)\n",
+ nbytes - Memsize, SYSERR, errno);
+ return -1;
+ }
+
+ if (Memsize == 0)
+ Memptr = cp;
+ Memsize += nbytes - Memsize;
+ }
+#else
+
+ /* nbytes = -1 means "free all allocated memory" */
+ if( nbytes == -1 ) {
+ free( malloc_space );
+ Memptr = NULL;
+ Memsize = 0;
+ return 0;
+ }
+
+ if( nbytes > Memsize ) {
+ if( Memsize != 0 )
+ free( malloc_space );
+
+ if( (cp = malloc_space = malloc( nbytes )) == NULL ) {
+ doio_fprintf(stderr, "malloc(%d) failed: %s (%d)\n",
+ nbytes, SYSERR, errno);
+ return -1;
+ }
+
+#ifdef _CRAYT3E
+ /* T3E requires memory to be aligned on 0x40 word boundaries */
+ ip = (int)cp;
+ if( ip & 0x3F != 0 ) {
+ doio_fprintf(stderr, "malloc(%d) = 0x%x(0x%x) not aligned by 0x%x\n",
+ nbytes, cp, ip, ip & 0x3f);
+
+ free(cp);
+ if( (cp = malloc_space = malloc( nbytes + 0x40 )) == NULL ) {
+ doio_fprintf(stderr, "malloc(%d) failed: %s (%d)\n",
+ nbytes, SYSERR, errno);
+ return -1;
+ }
+ ip = (int)cp;
+ cp += (0x40 - (ip & 0x3F));
+ }
+#endif /* _CRAYT3E */
+ Memptr = cp;
+ Memsize = nbytes;
+ }
+#endif /* NOTDEF */
+ return 0;
+}
+#endif /* CRAY */
+
+/*
+ * Simple function for allocating sds space. Uses Sdssize and Sdsptr to
+ * keep track of location and size of currently allocated chunk.
+ */
+
+#ifdef _CRAY1
+
+int
+alloc_sds(nbytes)
+int nbytes;
+{
+ int nblks;
+
+ if (nbytes > Sdssize) {
+ if ((nblks = ssbreak(btoc(nbytes - Sdssize))) == -1) {
+ doio_fprintf(stderr, "ssbreak(%d) failed: %s (%d)\n",
+ btoc(nbytes - Sdssize), SYSERR, errno);
+ return -1;
+ }
+
+ Sdssize = ctob(nblks);
+ Sdsptr = 0;
+ }
+
+ return 0;
+}
+
+#else
+
+#ifdef CRAY
+
+int
+alloc_sds(nbytes)
+int nbytes;
+{
+ doio_fprintf(stderr,
+ "Internal Error - alloc_sds() called on a CRAY2 system\n");
+ alloc_mem(-1);
+ exit(E_INTERNAL);
+}
+
+#endif
+
+#endif /* _CRAY1 */
+
+/*
+ * Function to maintain a file descriptor cache, so that doio does not have
+ * to do so many open() and close() calls. Descriptors are stored in the
+ * cache by file name, and open flags. Each entry also has a _rtc value
+ * associated with it which is used in aging. If doio cannot open a file
+ * because it already has too many open (ie. system limit hit) it will close
+ * the one in the cache that has the oldest _rtc value.
+ *
+ * If alloc_fd() is called with a file of NULL, it will close all descriptors
+ * in the cache, and free the memory in the cache.
+ */
+
+int
+alloc_fd(file, oflags)
+char *file;
+int oflags;
+{
+ struct fd_cache *fdc;
+ struct fd_cache *alloc_fdcache(char *file, int oflags);
+
+ fdc = alloc_fdcache(file, oflags);
+ if(fdc != NULL)
+ return(fdc->c_fd);
+ else
+ return(-1);
+}
+
+struct fd_cache *
+alloc_fdcache(file, oflags)
+char *file;
+int oflags;
+{
+ int fd;
+ struct fd_cache *free_slot, *oldest_slot, *cp;
+ static int cache_size = 0;
+ static struct fd_cache *cache = NULL;
+#ifdef sgi
+ struct dioattr finfo;
+#endif
+
+ /*
+ * If file is NULL, it means to free up the fd cache.
+ */
+
+ if (file == NULL && cache != NULL) {
+ for (cp = cache; cp < &cache[cache_size]; cp++) {
+ if (cp->c_fd != -1) {
+ close(cp->c_fd);
+ }
+#ifndef CRAY
+ if (cp->c_memaddr != NULL) {
+ munmap(cp->c_memaddr, cp->c_memlen);
+ }
+#endif
+ }
+
+ free(cache);
+ cache = NULL;
+ cache_size = 0;
+ return 0;
+ }
+
+ free_slot = NULL;
+ oldest_slot = NULL;
+
+ /*
+ * Look for a fd in the cache. If one is found, return it directly.
+ * Otherwise, when this loop exits, oldest_slot will point to the
+ * oldest fd slot in the cache, and free_slot will point to an
+ * unoccupied slot if there are any.
+ */
+
+ for (cp = cache; cp != NULL && cp < &cache[cache_size]; cp++) {
+ if (cp->c_fd != -1 &&
+ cp->c_oflags == oflags &&
+ strcmp(cp->c_file, file) == 0) {
+#ifdef CRAY
+ cp->c_rtc = _rtc();
+#else
+ cp->c_rtc = Reqno;
+#endif
+ return cp;
+ }
+
+ if (cp->c_fd == -1) {
+ if (free_slot == NULL) {
+ free_slot = cp;
+ }
+ } else {
+ if (oldest_slot == NULL ||
+ cp->c_rtc < oldest_slot->c_rtc) {
+ oldest_slot = cp;
+ }
+ }
+ }
+
+ /*
+ * No matching file/oflags pair was found in the cache. Attempt to
+ * open a new fd.
+ */
+
+ if ((fd = open(file, oflags, 0666)) < 0) {
+ if (errno != EMFILE) {
+ doio_fprintf(stderr,
+ "Could not open file %s with flags %#o (%s): %s (%d)\n",
+ file, oflags, format_oflags(oflags),
+ SYSERR, errno);
+ alloc_mem(-1);
+ exit(E_SETUP);
+ }
+
+ /*
+ * If we get here, we have as many open fd's as we can have.
+ * Close the oldest one in the cache (pointed to by
+ * oldest_slot), and attempt to re-open.
+ */
+
+ close(oldest_slot->c_fd);
+ oldest_slot->c_fd = -1;
+ free_slot = oldest_slot;
+
+ if ((fd = open(file, oflags, 0666)) < 0) {
+ doio_fprintf(stderr,
+ "Could not open file %s with flags %#o (%s): %s (%d)\n",
+ file, oflags, format_oflags(oflags),
+ SYSERR, errno);
+ alloc_mem(-1);
+ exit(E_SETUP);
+ }
+ }
+
+/*printf("alloc_fd: new file %s flags %#o fd %d\n", file, oflags, fd);*/
+
+ /*
+ * If we get here, fd is our open descriptor. If free_slot is NULL,
+ * we need to grow the cache, otherwise free_slot is the slot that
+ * should hold the fd info.
+ */
+
+ if (free_slot == NULL) {
+ cache = (struct fd_cache *)realloc(cache, sizeof(struct fd_cache) * (FD_ALLOC_INCR + cache_size));
+ if (cache == NULL) {
+ doio_fprintf(stderr, "Could not malloc() space for fd chace");
+ alloc_mem(-1);
+ exit(E_SETUP);
+ }
+
+ cache_size += FD_ALLOC_INCR;
+
+ for (cp = &cache[cache_size-FD_ALLOC_INCR];
+ cp < &cache[cache_size]; cp++) {
+ cp->c_fd = -1;
+ }
+
+ free_slot = &cache[cache_size - FD_ALLOC_INCR];
+ }
+
+ /*
+ * finally, fill in the cache slot info
+ */
+
+ free_slot->c_fd = fd;
+ free_slot->c_oflags = oflags;
+ strcpy(free_slot->c_file, file);
+#ifdef CRAY
+ free_slot->c_rtc = _rtc();
+#else
+ free_slot->c_rtc = Reqno;
+#endif
+
+#ifdef sgi
+ if(oflags & O_DIRECT) {
+ if(fcntl(fd, F_DIOINFO, &finfo) == -1) {
+ finfo.d_mem = 1;
+ finfo.d_miniosz = 1;
+ finfo.d_maxiosz = 1;
+ }
+ } else {
+ finfo.d_mem = 1;
+ finfo.d_miniosz = 1;
+ finfo.d_maxiosz = 1;
+ }
+
+ free_slot->c_memalign = finfo.d_mem;
+ free_slot->c_miniosz = finfo.d_miniosz;
+ free_slot->c_maxiosz = finfo.d_maxiosz;
+#endif /* sgi */
+#ifndef CRAY
+ free_slot->c_memaddr = NULL;
+ free_slot->c_memlen = 0;
+#endif
+
+ return free_slot;
+}
+
+/*
+ *
+ * Signal Handling Section
+ *
+ *
+ */
+
+#ifdef sgi
+/*
+ * "caller-id" for signals
+ */
+void
+signal_info(int sig, siginfo_t *info, void *v)
+{
+ int haveit = 0;
+
+ if(info != NULL) {
+ switch(info->si_code) {
+ case SI_USER:
+ doio_fprintf(stderr,
+ "signal_info: si_signo %d si_errno %d si_code SI_USER pid %d uid %d\n",
+ info->si_signo, info->si_errno,
+ info->si_pid, info->si_uid);
+ haveit = 1;
+ break;
+
+ case SI_QUEUE:
+ doio_fprintf(stderr, "signal_info si_signo %d si_code = SI_QUEUE\n",
+ info->si_signo);
+ haveit = 1;
+ break;
+ }
+
+ if( ! haveit ){
+ if( (info->si_signo == SIGSEGV) ||
+ (info->si_signo == SIGBUS) ){
+ doio_fprintf(stderr, "signal_info si_signo %d si_errno %d si_code = %d si_addr=%p active_mmap_rw=%d havesigint=%d\n",
+ info->si_signo, info->si_errno,
+ info->si_code, info->si_addr,
+ active_mmap_rw,
+ havesigint);
+ haveit = 1;
+ }
+ }
+
+ if( !haveit ){
+ doio_fprintf(stderr, "signal_info: si_signo %d si_errno %d unknown code %d\n",
+ info->si_signo, info->si_errno,
+ info->si_code);
+ }
+ } else {
+ doio_fprintf(stderr, "signal_info: sig %d\n", sig);
+ }
+}
+#endif
+
+#ifdef sgi
+void
+cleanup_handler(int sig, siginfo_t *info, void *v)
+{
+ havesigint=1; /* in case there's a followup signal */
+ /*signal_info(sig, info, v);*/ /* be quiet on "normal" kill */
+ alloc_mem(-1);
+ exit(0);
+}
+
+
+void
+die_handler(int sig, siginfo_t *info, void *v)
+{
+ doio_fprintf(stderr, "terminating on signal %d\n", sig);
+ signal_info(sig, info, v);
+ alloc_mem(-1);
+ exit(1);
+}
+
+void
+sigbus_handler(int sig, siginfo_t *info, void *v)
+{
+ /* While we are doing a memcpy to/from an mmapped region we can
+ get a SIGBUS for a variety of reasons--and not all of them
+ should be considered failures.
+
+ Under normal conditions if we get a SIGINT it means we've been
+ told to shutdown. However, if we're currently doing the above-
+ mentioned memcopy then the kernel will follow that SIGINT with
+ a SIGBUS. We can guess that we're in this situation by seeing
+ that the si_errno field in the siginfo structure has EINTR as
+ an errno. (We might make the guess stronger by looking at the
+ si_addr field to see that it's not faulting off the end of the
+ mmapped region, but it seems that in such a case havesigint
+ would not have been set so maybe that doesn't make the guess
+ stronger.)
+ */
+
+
+ if( active_mmap_rw && havesigint && (info->si_errno == EINTR) ){
+ cleanup_handler( sig, info, v );
+ }
+ else{
+ die_handler( sig, info, v );
+ }
+}
+#else
+
+void
+cleanup_handler()
+{
+ havesigint=1; /* in case there's a followup signal */
+ alloc_mem(-1);
+ exit(0);
+}
+
+void
+die_handler(sig)
+int sig;
+{
+ doio_fprintf(stderr, "terminating on signal %d\n", sig);
+ alloc_mem(-1);
+ exit(1);
+}
+
+#ifndef CRAY
+void
+sigbus_handler(sig)
+int sig;
+{
+ /* See sigbus_handler() in the 'ifdef sgi' case for details. Here,
+ we don't have the siginfo stuff so the guess is weaker but we'll
+ do it anyway.
+ */
+
+ if( active_mmap_rw && havesigint )
+ cleanup_handler();
+ else
+ die_handler(sig);
+}
+#endif /* !CRAY */
+#endif /* sgi */
+
+
+void
+noop_handler(sig)
+int sig;
+{
+ return;
+}
+
+
+/*
+ * SIGINT handler for the parent (original doio) process. It simply sends
+ * a SIGINT to all of the doio children. Since they're all in the same
+ * pgrp, this can be done with a single kill().
+ */
+
+void
+sigint_handler()
+{
+ int i;
+
+ for (i = 0; i < Nchildren; i++) {
+ if (Children[i] != -1) {
+ kill(Children[i], SIGINT);
+ }
+ }
+}
+
+/*
+ * Signal handler used to inform a process when async io completes. Referenced
+ * in do_read() and do_write(). Note that the signal handler is not
+ * re-registered.
+ */
+
+void
+aio_handler(sig)
+int sig;
+{
+ int i;
+ struct aio_info *aiop;
+
+ for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) {
+ aiop = &Aio_Info[i];
+
+ if (aiop->strategy == A_SIGNAL && aiop->sig == sig) {
+ aiop->signalled++;
+
+ if (aio_done(aiop)) {
+ aiop->done++;
+ }
+ }
+ }
+}
+
+/*
+ * dump info on all open aio slots
+ */
+void
+dump_aio()
+{
+ int i, count;
+
+ count=0;
+ for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) {
+ if (Aio_Info[i].busy) {
+ count++;
+ fprintf(stderr,
+ "Aio_Info[%03d] id=%d fd=%d signal=%d signaled=%d\n",
+ i, Aio_Info[i].id,
+ Aio_Info[i].fd,
+ Aio_Info[i].sig,
+ Aio_Info[i].signalled);
+ fprintf(stderr, "\tstrategy=%s\n",
+ format_strat(Aio_Info[i].strategy));
+ }
+ }
+ fprintf(stderr, "%d active async i/os\n", count);
+}
+
+
+#ifdef sgi
+/*
+ * Signal handler called as a callback, not as a signal.
+ * 'val' is the value from sigev_value and is assumed to be the
+ * Aio_Info[] index.
+ */
+void
+cb_handler(val)
+sigval_t val;
+{
+ struct aio_info *aiop;
+
+/*printf("cb_handler requesting slot %d\n", val.sival_int);*/
+ aiop = aio_slot( val.sival_int );
+/*printf("cb_handler, aiop=%p\n", aiop);*/
+
+/*printf("%d in cb_handler\n", getpid() );*/
+ if (aiop->strategy == A_CALLBACK) {
+ aiop->signalled++;
+
+ if (aio_done(aiop)) {
+ aiop->done++;
+ }
+ }
+}
+#endif
+
+struct aio_info *
+aio_slot(aio_id)
+int aio_id;
+{
+ int i;
+ static int id = 1;
+ struct aio_info *aiop;
+
+ aiop = NULL;
+
+ for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) {
+ if (aio_id == -1) {
+ if (! Aio_Info[i].busy) {
+ aiop = &Aio_Info[i];
+ aiop->busy = 1;
+ aiop->id = id++;
+ break;
+ }
+ } else {
+ if (Aio_Info[i].busy && Aio_Info[i].id == aio_id) {
+ aiop = &Aio_Info[i];
+ break;
+ }
+ }
+ }
+
+ if( aiop == NULL ){
+ doio_fprintf(stderr,"aio_slot(%d) not found. Request %d\n",
+ aio_id, Reqno);
+ dump_aio();
+ alloc_mem(-1);
+ exit(E_INTERNAL);
+ }
+
+ return aiop;
+}
+
+int
+aio_register(fd, strategy, sig)
+int fd;
+int strategy;
+int sig;
+{
+ struct aio_info *aiop;
+ void aio_handler();
+ struct sigaction sa;
+
+ aiop = aio_slot(-1);
+
+ aiop->fd = fd;
+ aiop->strategy = strategy;
+ aiop->done = 0;
+#ifdef CRAY
+ bzero((char *)&aiop->iosw, sizeof(aiop->iosw));
+#endif
+
+ if (strategy == A_SIGNAL) {
+ aiop->sig = sig;
+ aiop->signalled = 0;
+
+ sa.sa_handler = aio_handler;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+
+ sigaction(sig, &sa, &aiop->osa);
+ } else {
+ aiop->sig = -1;
+ aiop->signalled = 0;
+ }
+
+ return aiop->id;
+}
+
+int
+aio_unregister(aio_id)
+int aio_id;
+{
+ struct aio_info *aiop;
+
+ aiop = aio_slot(aio_id);
+
+ if (aiop->strategy == A_SIGNAL) {
+ sigaction(aiop->sig, &aiop->osa, NULL);
+ }
+
+ aiop->busy = 0;
+ return 0;
+}
+
+#ifndef linux
+int
+aio_wait(aio_id)
+int aio_id;
+{
+#ifdef RECALL_SIZEOF
+ long mask[RECALL_SIZEOF];
+#endif
+ sigset_t sigset;
+ struct aio_info *aiop;
+#ifdef CRAY
+ struct iosw *ioswlist[1];
+#endif
+#ifdef sgi
+ const aiocb_t *aioary[1];
+#endif
+ int r, cnt;
+
+
+ aiop = aio_slot(aio_id);
+/*printf("%d aiop B =%p\n", getpid(), aiop);*/
+
+ switch (aiop->strategy) {
+ case A_POLL:
+ while (! aio_done(aiop))
+ ;
+ break;
+
+ case A_SIGNAL:
+ sigemptyset(&sigset);
+ sighold( aiop->sig );
+
+ while ( !aiop->signalled || !aiop->done ) {
+ sigsuspend(&sigset);
+ sighold( aiop->sig );
+ }
+ break;
+
+#ifdef CRAY
+ case A_RECALL:
+ ioswlist[0] = &aiop->iosw;
+ if (recall(aiop->fd, 1, ioswlist) < 0) {
+ doio_fprintf(stderr, "recall() failed: %s (%d)\n",
+ SYSERR, errno);
+ exit(E_SETUP);
+ }
+ break;
+
+#ifdef RECALL_SIZEOF
+
+ case A_RECALLA:
+ RECALL_INIT(mask);
+ RECALL_SET(mask, aiop->fd);
+ if (recalla(mask) < 0) {
+ doio_fprintf(stderr, "recalla() failed: %s (%d)\n",
+ SYSERR, errno);
+ exit(E_SETUP);
+ }
+
+ RECALL_CLR(mask, aiop->fd);
+ break;
+#endif
+
+ case A_RECALLS:
+ ioswlist[0] = &aiop->iosw;
+ if (recalls(1, ioswlist) < 0) {
+ doio_fprintf(stderr, "recalls failed: %s (%d)\n",
+ SYSERR, errno);
+ exit(E_SETUP);
+ }
+ break;
+#endif /* CRAY */
+
+#ifdef sgi
+ case A_CALLBACK:
+ aioary[0] = &aiop->aiocb;
+ cnt=0;
+ do {
+ r = aio_suspend(aioary, 1, NULL);
+ if( r == -1 ){
+ doio_fprintf(stderr, "aio_suspend failed: %s (%d)\n",
+ SYSERR, errno );
+ exit(E_SETUP);
+ }
+ cnt++;
+ } while(aiop->done == 0);
+
+#if 0
+ /*
+ * after having this set for a while, I've decided that
+ * it's too noisy
+ */
+ if(cnt > 1)
+ doio_fprintf(stderr, "aio_wait: callback wait took %d tries\n", cnt);
+#endif
+
+ /*
+ * Note: cb_handler already calls aio_done
+ */
+ break;
+
+
+ case A_SUSPEND:
+ aioary[0] = &aiop->aiocb;
+ r = aio_suspend(aioary, 1, NULL);
+ if( r == -1 ){
+ doio_fprintf(stderr, "aio_suspend failed: %s (%d)\n",
+ SYSERR, errno );
+ exit(E_SETUP);
+ }
+
+ aio_done(aiop);
+ break;
+#endif
+ }
+
+/*printf("aio_wait: errno %d return %d\n", aiop->aio_errno, aiop->aio_ret);*/
+
+ return 0;
+}
+#endif /* !linux */
+
+/*
+ * Format specified time into HH:MM:SS format. t is the time to format
+ * in seconds (as returned from time(2)).
+ */
+
+char *
+hms(t)
+time_t t;
+{
+ static char ascii_time[9];
+ struct tm *ltime;
+
+ ltime = localtime(&t);
+ strftime(ascii_time, sizeof(ascii_time), "%H:%M:%S", ltime);
+
+ return ascii_time;
+}
+
+/*
+ * Simple routine to check if an async io request has completed.
+ */
+
+int
+aio_done(struct aio_info *ainfo)
+{
+#ifdef CRAY
+ return ainfo->iosw.sw_flag;
+#endif
+
+#ifdef sgi
+ if( (ainfo->aio_errno = aio_error(&ainfo->aiocb)) == -1 ){
+ doio_fprintf(stderr, "aio_done: aio_error failed: %s (%d)\n",
+ SYSERR, errno );
+ exit(E_SETUP);
+ }
+ /*printf("%d aio_done aio_errno=%d\n", getpid(), ainfo->aio_errno);*/
+ if( ainfo->aio_errno != EINPROGRESS ){
+ if( (ainfo->aio_ret = aio_return(&ainfo->aiocb)) == -1 ){
+ doio_fprintf(stderr, "aio_done: aio_return failed: %s (%d)\n",
+ SYSERR, errno );
+ exit(E_SETUP);
+ }
+ }
+
+ return (ainfo->aio_errno != EINPROGRESS);
+#else
+ return -1; /* invalid */
+#endif
+}
+
+/*
+ * Routine to handle upanic() - it first attempts to set the panic flag. If
+ * the flag cannot be set, an error message is issued. A call to upanic
+ * with PA_PANIC is then done unconditionally, in case the panic flag was set
+ * from outside the program (as with the panic(8) program).
+ *
+ * Note - we only execute the upanic code if -U was used, and the passed in
+ * mask is set in the Upanic_Conditions bitmask.
+ */
+
+void
+doio_upanic(mask)
+int mask;
+{
+ if (U_opt == 0 || (mask & Upanic_Conditions) == 0) {
+ return;
+ }
+
+#ifdef CRAY
+ if (upanic(PA_SET) < 0) {
+ doio_fprintf(stderr, "WARNING - Could not set the panic flag - upanic(PA_SET) failed: %s (%d)\n",
+ SYSERR, errno);
+ }
+
+ upanic(PA_PANIC);
+#endif
+#ifdef sgi
+ syssgi(1005); /* syssgi test panic - DEBUG kernels only */
+#endif
+ doio_fprintf(stderr, "WARNING - upanic() failed\n");
+}
+
+/*
+ * Parse cmdline options/arguments and set appropriate global variables.
+ * If the cmdline is valid, return 0 to caller. Otherwise exit with a status
+ * of 1.
+ */
+
+int
+parse_cmdline(argc, argv, opts)
+int argc;
+char **argv;
+char *opts;
+{
+ int c;
+ char cc, *cp, *tok;
+ extern int opterr;
+ extern int optind;
+ extern char *optarg;
+ struct smap *s;
+ char *memargs[NMEMALLOC];
+ int nmemargs, ma;
+ void parse_memalloc(char *arg);
+ void parse_delay(char *arg);
+ void dump_memalloc();
+
+ if (*argv[0] == '-') {
+ argv[0]++;
+ Execd = 1;
+ }
+
+ if ((Prog = strrchr(argv[0], '/')) == NULL) {
+ Prog = argv[0];
+ } else {
+ Prog++;
+ }
+
+ opterr = 0;
+ while ((c = getopt(argc, argv, opts)) != EOF) {
+ switch ((char)c) {
+ case 'a':
+ a_opt++;
+ break;
+
+ case 'C':
+ C_opt++;
+ for(s=checkmap; s->string != NULL; s++)
+ if(!strcmp(s->string, optarg))
+ break;
+ if (s->string == NULL) {
+ fprintf(stderr,
+ "%s%s: Illegal -C arg (%s). Must be one of: ",
+ Prog, TagName, tok);
+
+ for (s = checkmap; s->string != NULL; s++)
+ fprintf(stderr, "%s ", s->string);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+
+ switch(s->value) {
+ case C_DEFAULT:
+ Data_Fill = doio_pat_fill;
+ Data_Check = doio_pat_check;
+ break;
+ default:
+ fprintf(stderr,
+ "%s%s: Unrecognised -C arg '%s' %d",
+ Prog, TagName, s->string, s->value);
+ exit(1);
+ }
+ break;
+
+ case 'd': /* delay between i/o ops */
+ parse_delay(optarg);
+ break;
+
+ case 'e':
+ if (Npes > 1 && Nprocs > 1) {
+ fprintf(stderr, "%s%s: Warning - Program is a multi-pe application - exec option is ignored.\n", Prog, TagName);
+ }
+ e_opt++;
+ break;
+
+ case 'h':
+ help(stdout);
+ exit(0);
+ break;
+
+ case 'k':
+ k_opt++;
+ break;
+
+ case 'm':
+ Message_Interval = strtol(optarg, &cp, 10);
+ if (*cp != '\0' || Message_Interval < 0) {
+ fprintf(stderr, "%s%s: Illegal -m arg (%s): Must be an integer >= 0\n", Prog, TagName, optarg);
+ exit(1);
+ }
+ m_opt++;
+ break;
+
+ case 'M': /* memory allocation types */
+#ifndef CRAY
+ nmemargs = string_to_tokens(optarg, memargs, 32, ",");
+ for(ma=0; ma < nmemargs; ma++) {
+ parse_memalloc(memargs[ma]);
+ }
+ /*dump_memalloc();*/
+#else
+ fprintf(stderr, "%s%s: Error: -M isn't supported on this platform\n", Prog, TagName);
+ exit(1);
+#endif
+ M_opt++;
+ break;
+
+ case 'N':
+ sprintf( TagName, "(%.39s)", optarg );
+ break;
+
+ case 'n':
+ Nprocs = strtol(optarg, &cp, 10);
+ if (*cp != '\0' || Nprocs < 1) {
+ fprintf(stderr,
+ "%s%s: Illegal -n arg (%s): Must be integer > 0\n",
+ Prog, TagName, optarg);
+ exit(E_USAGE);
+ }
+
+ if (Npes > 1 && Nprocs > 1) {
+ fprintf(stderr, "%s%s: Program has been built as a multi-pe app. -n1 is the only nprocs value allowed\n", Prog, TagName);
+ exit(E_SETUP);
+ }
+ n_opt++;
+ break;
+
+ case 'r':
+ Release_Interval = strtol(optarg, &cp, 10);
+ if (*cp != '\0' || Release_Interval < 0) {
+ fprintf(stderr,
+ "%s%s: Illegal -r arg (%s): Must be integer >= 0\n",
+ Prog, TagName, optarg);
+ exit(E_USAGE);
+ }
+
+ r_opt++;
+ break;
+
+ case 'w':
+ Write_Log = optarg;
+ w_opt++;
+ break;
+
+ case 'v':
+ v_opt++;
+ break;
+
+ case 'V':
+ if (strcasecmp(optarg, "sync") == 0) {
+ Validation_Flags = O_SYNC;
+ } else if (strcasecmp(optarg, "buffered") == 0) {
+ Validation_Flags = 0;
+#ifdef CRAY
+ } else if (strcasecmp(optarg, "parallel") == 0) {
+ Validation_Flags = O_PARALLEL;
+ } else if (strcasecmp(optarg, "ldraw") == 0) {
+ Validation_Flags = O_LDRAW;
+ } else if (strcasecmp(optarg, "raw") == 0) {
+ Validation_Flags = O_RAW;
+#endif
+#ifdef sgi
+ } else if (strcasecmp(optarg, "direct") == 0) {
+ Validation_Flags = O_DIRECT;
+#endif
+ } else {
+ if (sscanf(optarg, "%i%c", &Validation_Flags, &cc) != 1) {
+ fprintf(stderr, "%s: Invalid -V argument (%s) - must be a decimal, hex, or octal\n", Prog, optarg);
+ fprintf(stderr, " number, or one of the following strings: 'sync',\n");
+ fprintf(stderr, " 'buffered', 'parallel', 'ldraw', or 'raw'\n");
+ exit(E_USAGE);
+ }
+ }
+ V_opt++;
+ break;
+ case 'U':
+ tok = strtok(optarg, ",");
+ while (tok != NULL) {
+ for (s = Upanic_Args; s->string != NULL; s++)
+ if (strcmp(s->string, tok) == 0)
+ break;
+
+ if (s->string == NULL) {
+ fprintf(stderr,
+ "%s%s: Illegal -U arg (%s). Must be one of: ",
+ Prog, TagName, tok);
+
+ for (s = Upanic_Args; s->string != NULL; s++)
+ fprintf(stderr, "%s ", s->string);
+
+ fprintf(stderr, "\n");
+
+ exit(1);
+ }
+
+ Upanic_Conditions |= s->value;
+ tok = strtok(NULL, ",");
+ }
+
+ U_opt++;
+ break;
+
+ case '?':
+ usage(stderr);
+ exit(E_USAGE);
+ break;
+ }
+ }
+
+ /*
+ * Supply defaults
+ */
+
+ if (! C_opt) {
+ Data_Fill = doio_pat_fill;
+ Data_Check = doio_pat_check;
+ }
+
+ if (! U_opt)
+ Upanic_Conditions = 0;
+
+ if (! n_opt)
+ Nprocs = 1;
+
+ if (! r_opt)
+ Release_Interval = DEF_RELEASE_INTERVAL;
+
+ if (! M_opt) {
+ Memalloc[Nmemalloc].memtype = MEM_DATA;
+ Memalloc[Nmemalloc].flags = 0;
+ Memalloc[Nmemalloc].name = NULL;
+ Memalloc[Nmemalloc].space = NULL;
+ Nmemalloc++;
+ }
+
+ /*
+ * Initialize input stream
+ */
+
+ if (argc == optind) {
+ Infile = NULL;
+ } else {
+ Infile = argv[optind++];
+ }
+
+ if (argc != optind) {
+ usage(stderr);
+ exit(E_USAGE);
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Parse memory allocation types
+ *
+ * Types are:
+ * Data
+ * T3E-shmem:blksize[:nblks]
+ * SysV-shmem:shmid:blksize:nblks
+ * if shmid is "private", use IPC_PRIVATE
+ * and nblks is not required
+ *
+ * mmap:flags:filename:blksize[:nblks]
+ * flags are one of:
+ * p - private (MAP_PRIVATE)
+ * a - private, MAP_AUTORESRV
+ * l - local (MAP_LOCAL)
+ * s - shared (nblks required)
+ *
+ * plus any of:
+ * f - fixed address (MAP_FIXED)
+ * A - use an address without MAP_FIXED
+ * a - autogrow (map once at startup)
+ *
+ * mmap:flags:devzero
+ * mmap /dev/zero (shared not allowd)
+ * maps the first 4096 bytes of /dev/zero
+ *
+ * - put a directory at the beginning of the shared
+ * regions saying what pid has what region.
+ * DIRMAGIC
+ * BLKSIZE
+ * NBLKS
+ * nblks worth of directories - 1 int pids
+ */
+#ifndef CRAY
+void
+parse_memalloc(char *arg)
+{
+ char *allocargs[NMEMALLOC];
+ int nalloc;
+ struct memalloc *M;
+
+ if(Nmemalloc >= NMEMALLOC) {
+ doio_fprintf(stderr, "Error - too many memory types (%d).\n",
+ Nmemalloc);
+ return;
+ }
+
+ M = &Memalloc[Nmemalloc];
+
+ nalloc = string_to_tokens(arg, allocargs, 32, ":");
+ if(!strcmp(allocargs[0], "data")) {
+ M->memtype = MEM_DATA;
+ M->flags = 0;
+ M->name = NULL;
+ M->space = NULL;
+ Nmemalloc++;
+ if(nalloc >= 2) {
+ if(strchr(allocargs[1], 'p'))
+ M->flags |= MEMF_MPIN;
+ }
+ } else if(!strcmp(allocargs[0], "mmap")) {
+ /* mmap:flags:filename[:size] */
+ M->memtype = MEM_MMAP;
+ M->flags = 0;
+ M->space = NULL;
+ if(nalloc >= 1) {
+ if(strchr(allocargs[1], 'p'))
+ M->flags |= MEMF_PRIVATE;
+ if(strchr(allocargs[1], 'a'))
+ M->flags |= MEMF_AUTORESRV;
+ if(strchr(allocargs[1], 'l'))
+ M->flags |= MEMF_LOCAL;
+ if(strchr(allocargs[1], 's'))
+ M->flags |= MEMF_SHARED;
+
+ if(strchr(allocargs[1], 'f'))
+ M->flags |= MEMF_FIXADDR;
+ if(strchr(allocargs[1], 'A'))
+ M->flags |= MEMF_ADDR;
+ if(strchr(allocargs[1], 'G'))
+ M->flags |= MEMF_AUTOGROW;
+
+ if(strchr(allocargs[1], 'U'))
+ M->flags |= MEMF_FILE;
+ } else {
+ M->flags |= MEMF_PRIVATE;
+ }
+
+ if(nalloc > 2) {
+ if(!strcmp(allocargs[2], "devzero")) {
+ M->name = "/dev/zero";
+ if(M->flags &
+ ((MEMF_PRIVATE|MEMF_LOCAL) == 0))
+ M->flags |= MEMF_PRIVATE;
+ } else {
+ M->name = allocargs[2];
+ }
+ } else {
+ M->name = "/dev/zero";
+ if(M->flags &
+ ((MEMF_PRIVATE|MEMF_LOCAL) == 0))
+ M->flags |= MEMF_PRIVATE;
+ }
+ Nmemalloc++;
+
+ } else if(!strcmp(allocargs[0], "shmem")) {
+ /* shmem:shmid:size */
+ M->memtype = MEM_SHMEM;
+ M->flags = 0;
+ M->space = NULL;
+ if(nalloc >= 2) {
+ M->name = allocargs[1];
+ } else {
+ M->name = NULL;
+ }
+ if(nalloc >= 3) {
+ sscanf(allocargs[2], "%i", &M->nblks);
+ } else {
+ M->nblks = 0;
+ }
+ if(nalloc >= 4) {
+ if(strchr(allocargs[3], 'p'))
+ M->flags |= MEMF_MPIN;
+ }
+
+ Nmemalloc++;
+ } else {
+ doio_fprintf(stderr, "Error - unknown memory type '%s'.\n",
+ allocargs[0]);
+ exit(1);
+ }
+}
+
+void
+dump_memalloc()
+{
+ int ma;
+ char *mt;
+
+ if(Nmemalloc == 0) {
+ printf("No memory allocation strategies devined\n");
+ return;
+ }
+
+ for(ma=0; ma < Nmemalloc; ma++) {
+ switch(Memalloc[ma].memtype) {
+ case MEM_DATA: mt = "data"; break;
+ case MEM_SHMEM: mt = "shmem"; break;
+ case MEM_MMAP: mt = "mmap"; break;
+ default: mt = "unknown"; break;
+ }
+ printf("mstrat[%d] = %d %s\n", ma, Memalloc[ma].memtype, mt);
+ printf("\tflags=%#o name='%s' nblks=%d\n",
+ Memalloc[ma].flags,
+ Memalloc[ma].name,
+ Memalloc[ma].nblks);
+ }
+}
+
+#endif /* !CRAY */
+
+/*
+ * -d <op>:<time> - doio inter-operation delay
+ * currently this permits ONE type of delay between operations.
+ */
+
+void
+parse_delay(char *arg)
+{
+ char *delayargs[NMEMALLOC];
+ int ndelay;
+ struct smap *s;
+
+ ndelay = string_to_tokens(arg, delayargs, 32, ":");
+ if(ndelay < 2) {
+ doio_fprintf(stderr,
+ "Illegal delay arg (%s). Must be operation:time\n", arg);
+ exit(1);
+ }
+ for(s=delaymap; s->string != NULL; s++)
+ if(!strcmp(s->string, delayargs[0]))
+ break;
+ if (s->string == NULL) {
+ fprintf(stderr,
+ "Illegal Delay arg (%s). Must be one of: ", arg);
+
+ for (s = delaymap; s->string != NULL; s++)
+ fprintf(stderr, "%s ", s->string);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+
+ delayop = s->value;
+
+ sscanf(delayargs[1], "%i", &delaytime);
+
+ if(ndelay > 2) {
+ fprintf(stderr,
+ "Warning: extra delay arguments ignored.\n");
+ }
+}
+
+
+/*
+ * Usage clause - obvious
+ */
+
+int
+usage(stream)
+FILE *stream;
+{
+ /*
+ * Only do this if we are on vpe 0, to avoid seeing it from every
+ * process in the application.
+ */
+
+ if (Npes > 1 && Vpe != 0) {
+ return 0;
+ }
+
+ fprintf(stream, "usage%s: %s [-aekv] [-m message_interval] [-n nprocs] [-r release_interval] [-w write_log] [-V validation_ftype] [-U upanic_cond] [infile]\n", TagName, Prog);
+ return 0;
+}
+
+void
+help(stream)
+FILE *stream;
+{
+ /*
+ * Only the app running on vpe 0 gets to issue help - this prevents
+ * everybody in the application from doing this.
+ */
+
+ if (Npes > 1 && Vpe != 0) {
+ return;
+ }
+
+ usage(stream);
+ fprintf(stream, "\n");
+ fprintf(stream, "\t-a abort - kill all doio processes on data compare\n");
+ fprintf(stream, "\t errors. Normally only the erroring process exits\n");
+ fprintf(stream, "\t-C data-pattern-type \n");
+ fprintf(stream, "\t Available data patterns are:\n");
+ fprintf(stream, "\t default - repeating pattern\n");
+ fprintf(stream, "\t-d Operation:Time Inter-operation delay.\n");
+ fprintf(stream, "\t Operations are:\n");
+ fprintf(stream, "\t select:time (1 second=1000000)\n");
+ fprintf(stream, "\t sleep:time (1 second=1)\n");
+#ifdef sgi
+ fprintf(stream, "\t sginap:time (1 second=CLK_TCK=100)\n");
+#endif
+ fprintf(stream, "\t alarm:time (1 second=1)\n");
+ fprintf(stream, "\t-e Re-exec children before entering the main\n");
+ fprintf(stream, "\t loop. This is useful for spreading\n");
+ fprintf(stream, "\t procs around on multi-pe systems.\n");
+ fprintf(stream, "\t-k Lock file regions during writes using fcntl()\n");
+ fprintf(stream, "\t-v Verify writes - this is done by doing a buffered\n");
+ fprintf(stream, "\t read() of the data if file io was done, or\n");
+ fprintf(stream, "\t an ssread()of the data if sds io was done\n");
+#ifndef CRAY
+ fprintf(stream, "\t-M Data buffer allocation method\n");
+ fprintf(stream, "\t alloc-type[,type]\n");
+#ifdef sgi
+ fprintf(stream, "\t data:flags\n");
+ fprintf(stream, "\t p - mpin buffer\n");
+ fprintf(stream, "\t shmem:shmid:size:flags\n");
+ fprintf(stream, "\t p - mpin buffer\n");
+#else
+ fprintf(stream, "\t data\n");
+ fprintf(stream, "\t shmem:shmid:size\n");
+#endif /* sgi */
+ fprintf(stream, "\t mmap:flags:filename\n");
+ fprintf(stream, "\t p - private\n");
+#ifdef sgi
+ fprintf(stream, "\t s - shared\n");
+ fprintf(stream, "\t l - local\n");
+ fprintf(stream, "\t a - autoresrv\n");
+ fprintf(stream, "\t G - autogrow\n");
+#else
+ fprintf(stream, "\t s - shared (shared file must exist\n"),
+ fprintf(stream, "\t and have needed length)\n");
+#endif
+ fprintf(stream, "\t f - fixed address (not used)\n");
+ fprintf(stream, "\t a - specify address (not used)\n");
+ fprintf(stream, "\t U - Unlink file when done\n");
+ fprintf(stream, "\t The default flag is private\n");
+ fprintf(stream, "\n");
+#endif /* !CRAY */
+ fprintf(stream, "\t-m message_interval Generate a message every 'message_interval'\n");
+ fprintf(stream, "\t requests. An interval of 0 suppresses\n");
+ fprintf(stream, "\t messages. The default is 0.\n");
+ fprintf(stream, "\t-N tagname Tag name, for Monster.\n");
+ fprintf(stream, "\t-n nprocs # of processes to start up\n");
+ fprintf(stream, "\t-r release_interval Release all memory and close\n");
+ fprintf(stream, "\t files every release_interval operations.\n");
+ fprintf(stream, "\t By default procs never release memory\n");
+ fprintf(stream, "\t or close fds unless they have to.\n");
+ fprintf(stream, "\t-V validation_ftype The type of file descriptor to use for doing data\n");
+ fprintf(stream, "\t validation. validation_ftype may be an octal,\n");
+ fprintf(stream, "\t hex, or decimal number representing the open()\n");
+ fprintf(stream, "\t flags, or may be one of the following strings:\n");
+ fprintf(stream, "\t 'buffered' - validate using bufferd read\n");
+ fprintf(stream, "\t 'sync' - validate using O_SYNC read\n");
+#ifdef sgi
+ fprintf(stream, "\t 'direct - validate using O_DIRECT read'\n");
+#endif
+#ifdef CRAY
+ fprintf(stream, "\t 'ldraw' - validate using O_LDRAW read\n");
+ fprintf(stream, "\t 'parallel' - validate using O_PARALLEL read\n");
+ fprintf(stream, "\t 'raw' - validate using O_RAW read\n");
+#endif
+ fprintf(stream, "\t By default, 'parallel'\n");
+ fprintf(stream, "\t is used if the write was done with O_PARALLEL\n");
+ fprintf(stream, "\t or 'buffered' for all other writes.\n");
+ fprintf(stream, "\t-w write_log File to log file writes to. The doio_check\n");
+ fprintf(stream, "\t program can reconstruct datafiles using the\n");
+ fprintf(stream, "\t write_log, and detect if a file is corrupt\n");
+ fprintf(stream, "\t after all procs have exited.\n");
+ fprintf(stream, "\t-U upanic_cond Comma separated list of conditions that will\n");
+ fprintf(stream, "\t cause a call to upanic(PA_PANIC).\n");
+ fprintf(stream, "\t 'corruption' -> upanic on bad data comparisons\n");
+ fprintf(stream, "\t 'iosw' ---> upanic on unexpected async iosw\n");
+ fprintf(stream, "\t 'rval' ---> upanic on unexpected syscall rvals\n");
+ fprintf(stream, "\t 'all' ---> all of the above\n");
+ fprintf(stream, "\n");
+ fprintf(stream, "\tinfile Input stream - default is stdin - must be a list\n");
+ fprintf(stream, "\t of io_req structures (see doio.h). Currently\n");
+ fprintf(stream, "\t only the iogen program generates the proper\n");
+ fprintf(stream, "\t format\n");
+}
+
diff --git a/doio/doio.h b/doio/doio.h
new file mode 100644
index 0000000..96d4838
--- /dev/null
+++ b/doio/doio.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ * Define io syscalls supported by doio
+ */
+
+#define READ 1
+#define WRITE 2
+#define READA 3
+#define WRITEA 4
+#define SSREAD 5
+#define SSWRITE 6
+#define LISTIO 7
+#define LREAD 10 /* listio - single stride, single entry */
+#define LREADA 11
+#define LWRITE 12
+#define LWRITEA 13
+#define LSREAD 14 /* listio - multi-stride, single entry */
+#define LSREADA 15
+#define LSWRITE 16
+#define LSWRITEA 17
+#define LEREAD 18 /* listio - single stride, multiple entry */
+#define LEREADA 19
+#define LEWRITE 20
+#define LEWRITEA 21
+
+/* Irix System Calls */
+#define PREAD 100
+#define PWRITE 101
+#define READV 102
+#define WRITEV 103
+#define AREAD 104
+#define AWRITE 105
+#define LLREAD 110
+#define LLAREAD 111
+#define LLWRITE 112
+#define LLAWRITE 113
+#define MMAPR 120
+#define MMAPW 121
+#define RESVSP 122 /* fcntl(F_RESVSP) */
+#define UNRESVSP 123 /* fcntl(F_UNRESVSP) */
+#define DFFSYNC 124 /* fcntl(F_FSYNC) */
+#define FSYNC2 125 /* fsync(2) */
+#define FDATASYNC 126 /* fdatasync(2) */
+#define BIOSIZE 127 /* fcntl(F_SETBIOSIZE) */
+
+#ifdef CRAY
+/* used: <<doio>> 1.? <<DOIO>> 1.5 <-DOIO-> 1.7*/
+#define DOIO_MAGIC '<[DOIO]>'
+#else
+#define DOIO_MAGIC 07116601
+#endif
+
+/*
+ * Define various user flags (r_uflag field) that io requests can have
+ * specified.
+ */
+
+#define F_WORD_ALIGNED 0001 /* force request to be word aligned */
+
+/*
+ * define various doio exit status's
+ */
+
+#define E_NORMAL 000 /* normal completion */
+#define E_USAGE 001 /* cmdline usage error */
+#define E_SETUP 002 /* any of a million setup conditions */
+#define E_COMPARE 004 /* data compare error from doio child */
+#define E_INTERNAL 010 /* various internal errors */
+#define E_LOCKD 020 /* lockd startup/timeout errors */
+#define E_SIGNAL 040 /* killed by signal */
+
+/*
+ * Define async io completion strategies supported by doio.
+ */
+
+#define A_POLL 1 /* poll iosw for completion */
+#define A_SIGNAL 2 /* get signal for completion */
+#define A_RECALL 3 /* use recall(2) to wait */
+#define A_RECALLA 4 /* use recalla(2) to wait */
+#define A_RECALLS 5 /* use recalls(2) to wait */
+#define A_SUSPEND 6 /* use aio_suspend(2) to wait */
+#define A_CALLBACK 7 /* use a callback signal op. */
+
+/*
+ * Define individual structures for each syscall type. These will all be
+ * unionized into a single io_req structure which io generators fill in and
+ * pass to doio.
+ *
+ * Note: It is VERY important that the r_file, r_oflags, r_offset, and
+ * r_nbytes fields occupy the same record positions in the
+ * read_req, reada_req, write_req, and writea_req structures and
+ * that they have the same type. It is also that r_pattern
+ * has the same type/offset in the write_req and writea_req
+ * structures.
+ *
+ * Since doio.c accesses all information through the r_data
+ * union in io_req, if the above assumptions hold, the above
+ * fields can be accessed without regard to structure type.
+ * This is an allowed assumption in C.
+ */
+
+#define MAX_FNAME_LENGTH 128
+
+struct read_req {
+ char r_file[MAX_FNAME_LENGTH];
+ int r_oflags; /* open flags */
+ int r_offset;
+ int r_nbytes;
+ int r_uflags; /* user flags: mem alignment */
+ int r_aio_strat; /* asynch read completion strategy */
+ int r_nstrides; /* listio: multiple strides */
+ int r_nent; /* listio: multiple list entries */
+};
+
+struct write_req {
+ char r_file[MAX_FNAME_LENGTH];
+ int r_oflags;
+ int r_offset;
+ int r_nbytes;
+ char r_pattern;
+ int r_uflags; /* user flags: mem alignment */
+ int r_aio_strat; /* asynch write completion strategy */
+ int r_nstrides; /* listio: multiple strides */
+ int r_nent; /* listio: multiple list entries */
+};
+
+struct ssread_req {
+ int r_nbytes;
+};
+
+struct sswrite_req {
+ int r_nbytes;
+ char r_pattern;
+};
+
+struct listio_req {
+ char r_file[MAX_FNAME_LENGTH];
+ int r_cmd; /* LC_START or LC_WAIT */
+ int r_offset; /* file offset */
+ int r_opcode; /* LO_READ, or LO_WRITE */
+ int r_nbytes; /* bytes per stride */
+ int r_nstrides; /* how many strides to make */
+ int r_nent; /* how many listreq entries to make */
+ int r_filestride; /* always 0 for now */
+ int r_memstride; /* always 0 for now */
+ char r_pattern; /* for LO_WRITE operations */
+ int r_oflags; /* open(2) flags */
+ int r_aio_strat; /* async I/O completion strategy */
+ int r_uflags; /* user flags: memory alignment */
+};
+
+#define rw_req listio_req /* listio is superset of everything */
+
+/*
+ * Main structure for sending a request to doio. Any tools which form IO
+ * for doio must present it using one of these structures.
+ */
+
+struct io_req {
+ int r_type; /* must be one of the #defines above */
+ int r_magic; /* must be set to DOIO_MAGIC by requestor */
+ union {
+ struct read_req read;
+ struct write_req write;
+ struct ssread_req ssread;
+ struct sswrite_req sswrite;
+ struct listio_req listio;
+ struct rw_req io;
+ } r_data;
+};
diff --git a/doio/growfiles.c b/doio/growfiles.c
new file mode 100644
index 0000000..1911fe7
--- /dev/null
+++ b/doio/growfiles.c
@@ -0,0 +1,2813 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ * This program will grow a list of files.
+ * Each file will grow by grow_incr before the same
+ * file grows twice. Each file is open and closed before next file is opened.
+ *
+ * To just verify file contents: growfiles -g 0 -c 1 filename
+ *
+ * See help and prt_examples functions below.
+ *
+ * Basic code layout
+ * process cmdline
+ * print debug message about options used
+ * setup signal handlers
+ * return control to user (if wanted - default action)
+ * fork number of desired childern (if wanted)
+ * re-exec self (if wanted)
+ * Determine number of files
+ * malloc space or i/o buffer
+ * Loop until stop is set
+ * Determine if hit iteration, time, max errors or num bytes reached
+ * Loop through each file
+ * open file
+ * fstat file - to determine if file if a fifo
+ * prealloc file space (if wanted)
+ * growfile
+ * check last write
+ * check whole file
+ * shrink file
+ * close file
+ * delay (if wanted)
+ * End loop
+ * End loop
+ * remove all files (if wanted)
+ *
+ * Author: Richard Logan
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/signal.h>
+#include <errno.h>
+#include <string.h>
+#include "dataascii.h"
+#include "random_range.h"
+
+#ifdef CRAY
+#include <sys/panic.h>
+#include <sys/category.h>
+#endif
+
+extern int errno;
+
+extern char *openflags2symbols();
+
+extern int parse_open_flags();
+extern int background();
+extern int forker();
+extern int datapidgen();
+extern void databingen();
+extern int datapidchk();
+extern int databinchk();
+extern int file_lock();
+
+int file_size();
+int check_write();
+int shrinkfile();
+int check_file();
+int growfile();
+int cleanup();
+int handle_error();
+int lkfile();
+void usage();
+void help();
+void prt_examples();
+int set_sig();
+void sig_handler();
+static void notify_others();
+#ifndef linux
+int pre_alloc();
+#endif
+
+
+#define NEWIO 1 /* Use the tlibio.c functions */
+
+#ifndef NEWIO
+#define NEWIO 0 /* specifies to use original iowrite.c */
+ /* functions instead of tlibio.c functions */
+ /* Once it is proven tlibio.c functions work properly, */
+ /* only tlibio.c functions will be used */
+#else
+#include "tlibio.h"
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX 1023
+#endif
+
+
+#define DEF_DIR "."
+#define DEF_FILE "gf"
+
+char *Progname;
+int Debug = 1;
+
+int Pid=0;
+
+int io_type = 0; /* I/O type -sync */
+int open_flags = O_RDWR|O_CREAT; /* open flags */
+
+#define MAX_FC_READ 196608 /* 4096 * 48 - 48 blocks */
+
+#define PATTERN_ASCII 1 /* repeating alphabet letter pattern */
+ /* allows multiple writers and to be checked */
+#define PATTERN_PID 2 /* <pid><words byte offset><pid> */
+ /* Assumes 64 bit word. Only allows single */
+ /* process to write and check */
+/*
+ * 1234567890123456789012345678901234567890123456789012345678901234
+ * ________________________________________________________________
+ * < pid >< offset in file of this word >< pid >
+ */
+
+#define PATTERN_OFFSET 3 /* Like PATTERN_PID but has a fixed number */
+ /* (STATIC_NUM) instead of pid. */
+ /* Allows multiple processes to write/read */
+#define PATTERN_ALT 4 /* alternating bit pattern (i.e. 0x5555555...) */
+#define PATTERN_CHKER 5 /* checkerboard pattern (i.e. 0xff00ff00ff00...) */
+#define PATTERN_CNTING 6 /* counting pattern (i.e. 0 - 07, 0 - 07, ...) */
+#define PATTERN_ONES 7 /* all bits set (i.e. 0xffffffffffffff...) */
+#define PATTERN_ZEROS 8 /* all bits cleared (i.e. 0x000000000...) */
+#define PATTERN_RANDOM 9 /* random integers - can not be checked */
+#define STATIC_NUM 221849 /* used instead of pid when PATTERN_OFFSET */
+
+#define MODE_RAND_SIZE 1 /* random write and trunc */
+#define MODE_RAND_LSEEK 2 /* random lseek before write */
+#define MODE_GROW_BY_LSEEK 4 /* lseek beyond end of file then write a byte */
+#define RANDOM_OPEN 999876 /* if Open_flags set to this value, open flags */
+ /* will be randomly choosen from Open_flags[] */
+#define MODE_FIFO S_IFIFO /* defined in stat.h 0010000 */
+
+int num_files = 0; /* num_auto_files + cmd line files */
+char *filenames; /* pointer to space containing filenames */
+int remove_files = 0; /* if set, cleanup default is not to cleanup */
+int bytes_consumed = 0; /* total bytes consumed, all files */
+int bytes_to_consume = 0; /* non-zero if -B was specified, total bytes */
+int Maxerrs = 100; /* Max number errors before forced exit */
+int Errors = 0; /* number of encountered errors */
+int Upanic_on_error = 0; /* call upanic if error and this variable set */
+
+/* The *_size variables are only used when random iosize option (-r) is used */
+int max_size=5000;
+int min_size=1; /* also set in option parsing */
+int mult_size=1; /* when random iosz, iosz must be mult of mult_size */
+/* the *_lseek variables are only used when radon lseek option (-R) is used */
+int min_lseek=0; /* also set in option parsing */
+int max_lseek=-1; /* -1 means size of file */
+#ifdef CRAY
+int Pattern=PATTERN_OFFSET; /* This pattern is 64 bit word based */
+#else
+int Pattern=PATTERN_ASCII;
+#endif
+int Seed=-1; /* random number seed, < 0 == uninitialized */
+int Nseeds=0; /* Number of seed specified by the user */
+int *Seeds; /* malloc'ed arrary of ints holding user spec seeds */
+
+int using_random=0; /* flag indicating randomization is being used */
+float delaysecs=0.0; /* delay between iterations (in seconds) */
+int delaytime; /* delay between iterations in clocks/uses */
+int lockfile=0; /* if set, do file locking */
+ /* 1 = do file locking around write, trunc */
+ /* and reads. */
+ /* 2 = write lock around all file operations */
+
+int Woffset=0; /* offset before last write */
+int Grow_incr=4096; /* sz of last write */
+int Mode=0; /* bitmask of write/trunc mode */
+ /* also knows if dealing with fifo */
+char *Buffer = NULL; /* buffer used by write and write check */
+int Alignment=0; /* if non word multiple, io will not be word aligned */
+int Opid=0; /* original pid */
+
+int Sync_with_others = 0; /* Flag indicating to stop other if we stop before DONE */
+int Iter_cnt = 0; /* contains current iteration count value */
+char TagName[40]; /* name of this growfiles (see Monster) */
+
+struct fileinfo_t {
+ char *filename;
+ int fd;
+ int openflags;
+ int mode;
+} Fileinfo;
+
+/*
+ * Define open flags that will be used when '-o random' option is used.
+ * Note: If there is more than one growfiles doing its thing to the same
+ * file, O_TRUNC will cause data mismatches. How you ask?
+ * timing of events, example:
+ * Process one Process two
+ * --------------- -------------
+ * get write lock
+ * fstat file
+ * lseek
+ * generate pattern
+ * open with O_TRUNC
+ * write with wrong pattern
+ * because offset is wrong
+ *
+ * The second process truncated the file after the pattern was
+ * determined, thus the pattern is wrong for the file location.
+ *
+ * There can also be a timing problem with open flag O_APPEND if
+ * file locks are not being used (-l option). Things could happen
+ * between the fstat and the write. Thus, writing the wrong pattern.
+ * If all processes observe the file locks, O_APPEND should be ok
+ * to use.
+ */
+int Open_flags[] = {
+#ifdef CRAY
+ O_RDWR|O_CREAT,
+ O_RDWR|O_CREAT|O_RAW,
+ O_RDWR|O_CREAT|O_BIG,
+ O_RDWR|O_CREAT|O_APPEND,
+ O_RDWR|O_CREAT|O_NDELAY,
+ O_RDWR|O_CREAT|O_PLACE,
+ O_RDWR|O_CREAT|O_SYNC,
+ O_RDWR|O_CREAT|O_RAW|O_SYNC,
+ O_RDWR|O_CREAT|O_NDELAY|O_SYNC,
+ O_RDWR|O_CREAT|O_NDELAY|O_SYNC|O_BIG,
+ O_RDWR|O_CREAT|O_RAW,
+ O_RDWR|O_CREAT|O_RAW|O_APPEND,
+ O_RDWR|O_CREAT|O_RAW|O_BIG,
+ O_RDWR|O_CREAT|O_RAW|O_APPEND|O_BIG,
+/***
+ * O_WELLFORMED makes -o random require well formed i/o
+ ***/
+#if ALLOW_O_WELLFORMED
+#if O_PARALLEL
+ O_RDWR|O_CREAT|O_PARALLEL|O_WELLFORMED|O_RAW,
+ O_RDWR|O_CREAT|O_PARALLEL|O_WELLFORMED|O_RAW|O_TRUNC,
+#endif /* O_PARALLEL */
+#endif
+
+#else /* CRAY */
+ O_RDWR|O_CREAT,
+ O_RDWR|O_CREAT|O_APPEND,
+ O_RDWR|O_CREAT|O_NDELAY,
+ O_RDWR|O_CREAT|O_SYNC,
+ O_RDWR|O_CREAT|O_SYNC|O_NDELAY,
+ O_RDWR|O_CREAT|O_APPEND|O_NDELAY,
+
+#endif /* CRAY */
+};
+
+#define REXEC_INIT 0 /* don't do re-exec of childern */
+#define REXEC_DOIT 1 /* Do re-exec of childern */
+#define REXEC_DONE 2 /* We've already been re-exec'ed */
+
+#ifndef BSIZE
+#ifdef CRAY
+#define BSIZE 1024
+#else
+#define BSIZE 512
+#endif /* CRAY */
+#endif /* BSIZE */
+
+#define USECS_PER_SEC 1000000 /* microseconds per second */
+
+/*
+ * Define marcos used when dealing with file locks.
+ */
+#define LKLVL0 1 /* file lock around write/read/trunc */
+#define LKLVL1 2 /* file lock after open to before close */
+
+/*
+ * Define special max lseek values
+ */
+#define LSK_EOF -1 /* set fptr up to EOF */
+#define LSK_EOFPLUSGROW -2 /* set fptr up to EOF + grow - leave whole */
+#define LSK_EOFMINUSGROW -3 /* set fptr up to EOF-grow - no grow */
+
+
+/***********************************************************************
+ * MAIN
+ ***********************************************************************/
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+extern char *optarg; /* used by getopt */
+extern int optind;
+extern int opterr;
+
+int ind;
+int first_file_ind = 0;
+int num_auto_files = 0; /* files created by tool */
+int seq_auto_files = 0; /* auto files created by tool created by tool */
+char *auto_dir = DEF_DIR;
+char *auto_file = DEF_FILE;
+int grow_incr = 4096;
+int trunc_incr = 4096;
+int trunc_inter = 0; /* 0 means none, */
+int unlink_inter = 0; /* 0 means none, 1 means always unlink */
+int unlink_inter_ran = -1; /* -1 -use unlink_inter, otherwise randomly choose */
+ /* between unlink_inter and unlink_inter_ran */
+int file_check_inter = 0; /* 0 means never, 1 means always */
+int write_check_inter = 1; /* 0 means never, 1 means always */
+int iterations = 1; /* number of increments to be added */
+int no_file_check = 0; /* if set, no whole file checking will be done */
+int num;
+int fd; /* file descriptor */
+int stop = 0; /* loop stopper if set */
+int tmp;
+char chr;
+int ret;
+int pre_alloc_space = 0;
+#ifndef linux
+int total_grow_value; /* used in pre-allocations */
+#endif
+int backgrnd = 1; /* return control to user */
+struct stat statbuf;
+int time_iterval = -1;
+time_t start_time;
+char reason[40]; /* reason for loop termination */
+int num_procs=1;
+int forker_mode=0;
+int reexec=REXEC_INIT; /* reexec info */
+char *exec_path=NULL;
+
+char *strrchr();
+
+char *filename; /* name of file specified by user */
+char *cptr; /* temp char pointer */
+extern int Forker_npids; /* num of forked pid, defined in forker.c */
+
+
+ if ( argv[0][0] == '-' )
+ reexec=REXEC_DONE;
+ /*
+ * Determine name of file used to invoke this program
+ */
+ if ((Progname=strrchr(argv[0], '/')) != NULL)
+ Progname++;
+ else
+ Progname=argv[0];
+
+ TagName[0] = '\0';
+
+ /*
+ * Process options
+ */
+ while ((ind=getopt(argc, argv,
+ "hB:C:c:bd:D:e:Ef:g:H:I:i:lL:n:N:O:o:pP:q:wt:r:R:s:S:T:uU:W:xy")) != EOF) {
+ switch(ind) {
+
+ case 'h' :
+ help();
+ exit(0);
+
+ case 'B':
+ switch (sscanf(optarg, "%i%c",
+ &bytes_to_consume, &chr)) {
+ case 1: /* noop */
+ break;
+
+ case 2:
+ if (chr == 'b') {
+ bytes_to_consume *= BSIZE;
+ } else {
+ fprintf(stderr,
+ "%s%s: --B option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "%s%s: --B option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ break;
+ }
+
+ break;
+
+ case 'E' :
+ prt_examples(stdout);
+ exit(0);
+
+ case 'b' : /* batch */
+ backgrnd=0;
+ break;
+
+ case 'C':
+ if (sscanf(optarg, "%i", &write_check_inter) != 1 ) {
+ fprintf(stderr, "%s%s: --c option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'c':
+ if (sscanf(optarg, "%i", &file_check_inter) != 1 ) {
+ fprintf(stderr, "%s%s: --c option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+
+ case 'd':
+ auto_dir=optarg;
+#ifdef CRAY
+ unsetenv("TMPDIR"); /* force the use of auto_dir */
+#endif
+ if ( stat(auto_dir, &statbuf) == -1 ) {
+ if ( mkdir(auto_dir, 0777) == -1 ) {
+ if ( errno != EEXIST ) {
+ fprintf(stderr,
+ "%s%s: Unable to make dir %s\n",
+ Progname, TagName, auto_dir);
+ exit(1);
+ }
+ }
+ }
+ else {
+ if ( ! (statbuf.st_mode & S_IFDIR) ) {
+ fprintf(stderr,
+ "%s%s: %s already exists and is not a directory\n",
+ Progname, TagName, auto_dir);
+ exit(1);
+ }
+ }
+ break;
+
+ case 'D':
+ if (sscanf(optarg, "%i", &Debug) != 1 ) {
+ fprintf(stderr, "%s%s: --D option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'e':
+ if (sscanf(optarg, "%i", &Maxerrs) != 1 ) {
+ fprintf(stderr, "%s%s: --e option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'f':
+ auto_file=optarg;
+ break;
+
+ case 'g':
+ if ((ret=sscanf(optarg, "%i%c", &grow_incr, &chr)) < 1 ||
+ grow_incr < 0 ) {
+
+ fprintf(stderr, "%s%s: --g option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ if ( ret == 2 ) {
+ if ( chr == 'b' || chr == 'B' )
+ grow_incr *= 4096;
+ else {
+ fprintf(stderr,
+ "%s%s: --g option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ }
+ break;
+
+ case 'H':
+ if (sscanf(optarg, "%f", &delaysecs) != 1 || delaysecs < 0 ) {
+
+ fprintf(stderr, "%s%s: --H option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'i':
+ if (sscanf(optarg, "%i", &iterations) != 1 ||
+ iterations < 0 ) {
+
+ fprintf(stderr, "%s%s: --i option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'I':
+#if NEWIO
+ if((io_type=lio_parse_io_arg1(optarg)) == -1 ) {
+ fprintf(stderr,
+ "%s%s: --I arg is invalid, must be s, p, f, a, l, L or r.\n",
+ Progname, TagName);
+ exit(1);
+ }
+ if( io_type & LIO_RANDOM )
+ using_random++;
+#else
+ if((io_type=parse_io_arg(optarg)) == -1 ) {
+ fprintf(stderr,
+ "%s%s: --I arg is invalid, must be s, p, f, a, l, L or r.\n",
+ Progname, TagName);
+ exit(1);
+ }
+ if( io_type == 99 ) /* hold-over until tlibio.h */
+ using_random++;
+#endif
+ break;
+
+ case 'l':
+ lockfile++;
+ if ( lockfile > 2 )
+ lockfile=2; /* lockfile can only be 1 or 2 */
+ break;
+
+ case 'L':
+ if (sscanf(optarg, "%i", &time_iterval) != 1 ||
+ time_iterval < 0 ) {
+ fprintf(stderr, "%s%s: --L option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'n':
+ if (sscanf(optarg, "%i:%i", &num_procs, &forker_mode) < 1 ||
+ num_procs < 0 ) {
+
+ fprintf(stderr, "%s%s: --n option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+
+ break;
+
+ case 'N':
+ if (sscanf(optarg, "%i", &num_auto_files) != 1 ||
+ num_auto_files < 0 ) {
+
+ fprintf(stderr, "%s%s: --N option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'O':
+ if (sscanf(optarg, "%i", &Alignment) != 1 ||
+ num_auto_files < 0 ) {
+
+ fprintf(stderr, "%s%s: --O option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'o':
+ if ( strcmp(optarg, "random") == 0 ){
+ open_flags=RANDOM_OPEN;
+ using_random++;
+
+ } else if ((open_flags=parse_open_flags(optarg, NULL)) == -1 ) {
+ fprintf(stderr, "%s%s: --o arg contains invalid flag\n",
+ Progname, TagName);
+ exit(1);
+ }
+ break;
+
+
+ case 'p' : /* pre allocate space */
+#ifdef linux
+ printf("%s%s: --p is illegal option on linux system\n",
+ Progname, TagName);
+ exit(1);
+#else
+ pre_alloc_space++;
+#endif
+ break;
+
+ case 'P':
+#ifdef CRAY
+ if (strcmp(optarg, "PANIC") != 0 ) {
+ fprintf(stderr, "%s%s: --P arg must be PANIC\n", Progname, TagName);
+ exit(1);
+ }
+ Upanic_on_error++;
+ printf("%s: Will call upanic after writes\n");
+#else
+ printf("%s%s: --P is illegal option on non-cray system\n",
+ Progname, TagName);
+ exit(1);
+#endif
+ break;
+
+ case 'q': /* file content or pattern */
+ switch(optarg[0]) {
+ case 'A':
+ Pattern = PATTERN_ALT;
+ break;
+ case 'a':
+ Pattern = PATTERN_ASCII;
+ break;
+ case 'p':
+ Pattern = PATTERN_PID;
+ break;
+ case 'o':
+ Pattern = PATTERN_OFFSET;
+ break;
+ case 'c':
+ Pattern = PATTERN_CHKER;
+ break;
+ case 'C':
+ Pattern = PATTERN_CNTING;
+ break;
+ case 'r':
+ Pattern = PATTERN_RANDOM;
+ using_random++;
+ break;
+ case 'z':
+ Pattern = PATTERN_ZEROS;
+ break;
+ case 'O':
+ Pattern = PATTERN_ONES;
+ break;
+ default:
+ fprintf(stderr,
+ "%s%s: --C option arg invalid, A, a, p, o, c, C, r, z, or 0\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'R': /* random lseek before write arg: [min-]max*/
+ if (sscanf(optarg, "%i-%i", &min_lseek, &max_lseek) != 2 ) {
+ min_lseek=1; /* same as default in define */
+ if (sscanf(optarg, "%i%c", &max_lseek, &chr) != 1 ) {
+ fprintf(stderr, "%s%s: --R option arg invalid: [min-]max\n",
+ Progname, TagName);
+ exit(1);
+ }
+ }
+ if ( max_lseek < LSK_EOFMINUSGROW ) {
+ fprintf(stderr, "%s%s: --R option, max_lseek is invalid\n",
+ Progname, TagName);
+ exit(1);
+ }
+ Mode |= MODE_RAND_LSEEK;
+ using_random++;
+ break;
+
+ case 'r': /* random io size arg: [min-]max[:mult] */
+
+ /* min-max:mult format */
+ if (sscanf(optarg, "%i-%i:%i%c", &min_size, &max_size,
+ &mult_size, &chr) != 3 ) {
+ min_size=1;
+ /* max:mult format */
+ if (sscanf(optarg, "%i:%i%c", &max_size,
+ &mult_size, &chr) != 2 ) {
+ /* min-max format */
+ if (sscanf(optarg, "%i-%i%c", &min_size,
+ &max_size, &chr) != 2 ) {
+ min_size=1;
+ if (sscanf(optarg, "%i%c", &max_size, &chr) != 1 ) {
+ fprintf(stderr,
+ "%s%s: --r option arg invalid: [min-]max[:mult]\n",
+ Progname, TagName);
+ exit(1);
+ }
+ }
+ }
+ }
+
+ if ( max_size < 0 ) {
+ fprintf(stderr, "%s%s: --r option, max_size is invalid\n",
+ Progname, TagName);
+ exit(1);
+ }
+ /*
+ * If min and max are the same, no randomness
+ */
+ if ( min_size != max_size ) {
+ Mode |= MODE_RAND_SIZE;
+ using_random++;
+ }
+ break;
+
+ case 'S':
+ if (sscanf(optarg, "%i", &seq_auto_files) != 1 ||
+ seq_auto_files < 0 ) {
+
+ fprintf(stderr, "%s%s: --S option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 's': /* format: seed[,seed...] */
+
+ /* count the number of seeds */
+ cptr=optarg;
+ for(Nseeds=1; *cptr ; Nseeds++) {
+ if ( (filename=strchr(cptr, ',')) == NULL )
+ break;
+ cptr=filename;
+ cptr++;
+ }
+ Seeds=(int *)malloc(Nseeds*sizeof(int));
+
+ /*
+ * check that each seed is valid and put them in
+ * the newly malloc'ed Seeds arrary.
+ */
+ filename=cptr=optarg;
+ for(Nseeds=0; *cptr; Nseeds++) {
+ if ( (filename=strchr(cptr, ',')) == NULL ) {
+ if ( sscanf(cptr, "%i", &Seeds[Nseeds]) < 1 ) {
+ fprintf(stderr, "%s%s: --s option arg %s invalid\n",
+ Progname, TagName, cptr);
+ usage();
+ exit(1);
+ }
+ Nseeds++;
+ break;
+ }
+
+ *filename='\0';
+ if ( sscanf(cptr, "%i", &Seeds[Nseeds]) < 1 ) {
+ fprintf(stderr, "%s%s: --s option arg %s invalid\n",
+ Progname, TagName, cptr);
+ usage();
+ exit(1);
+ }
+ *filename=','; /* restore string */
+ cptr=filename;
+ cptr++;
+ }
+ break;
+
+ case 't':
+ if ((ret=sscanf(optarg, "%i%c", &trunc_incr, &chr)) < 1 ||
+ trunc_incr < 0 ) {
+
+ fprintf(stderr, "%s%s: --t option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ if ( ret == 2 ) {
+ if ( chr == 'b' || chr == 'B' )
+ trunc_incr *= 4096;
+ else {
+ fprintf(stderr,
+ "%s%s: --t option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ }
+ break;
+
+ case 'T': /* truncate interval */
+ if (sscanf(optarg, "%i%c", &trunc_inter, &chr) != 1 ||
+ trunc_inter < 0 ) {
+
+ fprintf(stderr, "%s%s: --T option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'u':
+ remove_files++;
+ break;
+
+ case 'U': /* how often to unlink file */
+ /*
+ * formats:
+ * A-B - randomly pick interval between A and B
+ * X - unlink file every X iteration
+ */
+ if (sscanf(optarg, "%i-%i", &unlink_inter,
+ &unlink_inter_ran) == 2 ) {
+
+ if ( unlink_inter < 0 || unlink_inter_ran < 0 ) {
+ fprintf(stderr, "%s%s: --U option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ /* ensure unlink_inter contains smaller value */
+ if ( unlink_inter > unlink_inter_ran ) {
+ tmp=unlink_inter_ran;
+ unlink_inter_ran=unlink_inter;
+ unlink_inter=tmp;
+ }
+ using_random++;
+
+ } else if (sscanf(optarg, "%i%c", &unlink_inter, &chr) != 1 ||
+ unlink_inter < 0 ) {
+
+ fprintf(stderr, "%s%s: --U option arg invalid\n",
+ Progname, TagName);
+ usage();
+ exit(1);
+ }
+ break;
+
+ case 'x':
+ if ( reexec != REXEC_DONE )
+ reexec=REXEC_DOIT;
+ break;
+
+ case 'w':
+ Mode |= MODE_GROW_BY_LSEEK;
+ break;
+
+ case 'W':
+ sprintf( TagName, "(%.39s)", optarg );
+ break;
+
+ case 'y':
+ Sync_with_others=1;
+ break;
+
+ case '?':
+ usage();
+ exit(1);
+ break;
+ }
+ }
+
+ if( Debug == 1 ){
+ cptr = getenv("TOUTPUT");
+ if( (cptr != NULL) && (strcmp( cptr, "NOPASS" ) == 0) ){
+ Debug = 0;
+ }
+ }
+
+ if ( Pattern == PATTERN_RANDOM ) {
+ no_file_check=1;
+ if ( write_check_inter || file_check_inter )
+ printf("%s%s: %d Using random pattern - no data checking will be performed!\n",
+ Progname, TagName, getpid());
+ }
+ else if ( max_lseek == LSK_EOFPLUSGROW || Mode & MODE_GROW_BY_LSEEK ) {
+ no_file_check=1;
+
+ if ( file_check_inter )
+ printf("%s%s: %d Using random lseek beyond EOF or lseek grow,\n\
+no whole file checking will be performed!\n", Progname, TagName, getpid());
+
+ }
+
+ if ( Mode & MODE_RAND_SIZE )
+ grow_incr=max_size;
+
+ set_sig();
+
+ Opid=getpid();
+ Pid=Opid;
+
+ if ( backgrnd ) {
+ if ( Debug > 1 )
+ printf("%s: %d DEBUG2 forking, returning control to the user\n",
+ Progname, Opid);
+ background(Progname); /* give user their prompt back */
+ }
+
+#if CRAY
+ if ( Sync_with_others )
+ setpgrp();
+#endif
+
+ if ( Debug > 3 ) {
+#if NEWIO
+ lio_set_debug(Debug-3);
+#else
+ set_iowrite_debug(Debug-3);
+#endif
+ }
+
+ /*
+ * Print some program information here if debug is turned on to
+ * level 3 or higher.
+ */
+
+ if ( Debug > 2 ) {
+
+ if ( Mode & MODE_GROW_BY_LSEEK )
+ printf("%s: %d DEBUG lseeking past end of file, writting a \"w\"\n",
+ Progname, Pid);
+ else if ( Pattern == PATTERN_OFFSET )
+ printf("%s: %d DEBUG3 %d<byteoffset>%d per word pattern multi-writers.\n",
+ Progname, Pid, STATIC_NUM, STATIC_NUM);
+ else if ( Pattern == PATTERN_PID )
+ printf("%s: %d DEBUG3 <pid><byteoffset><pid> per word pattern - 1 writer\n",
+ Progname, Pid);
+ else if ( Pattern == PATTERN_ASCII )
+ printf("%s: %d DEBUG3 ascii pattern (vi'able)- allows multiple writers\n",
+ Progname, Pid);
+ else if ( Pattern == PATTERN_ALT )
+ printf("%s: %d DEBUG3 alt bit pattern - allows multiple writers\n",
+ Progname, Pid);
+ else if ( Pattern == PATTERN_CHKER )
+ printf("%s: %d DEBUG3 checkerboard pattern - allows multiple writers\n",
+ Progname, Pid);
+ else if ( Pattern == PATTERN_CNTING )
+ printf("%s: %d DEBUG3 counting pattern - allows multiple writers\n",
+ Progname, Pid);
+ else if ( Pattern == PATTERN_RANDOM )
+ printf("%s: %d DEBUG3 random integer pattern - no write/file checking\n",
+ Progname, Pid);
+ else if ( Pattern == PATTERN_ONES )
+ printf("%s: %d DEBUG3 all ones pattern - allows multiple writers\n",
+ Progname, Pid);
+ else if ( Pattern == PATTERN_ZEROS )
+ printf("%s: %d DEBUG3 all zeros pattern - allows multiple writers\n",
+ Progname, Pid);
+
+ else
+ printf("%s: %d DEBUG3 unknown pattern\n",
+ Progname, Pid);
+ if ( bytes_to_consume )
+ printf("%s: %d DEBUG3 bytes_to_consume = %d\n",
+ Progname, Pid, bytes_to_consume);
+ printf("%s: %d DEBUG3 Maxerrs = %d, pre_alloc_space = %d, filelocking = %d\n",
+ Progname, Pid, Maxerrs, pre_alloc_space, lockfile);
+
+ printf("%s: %d DEBUG3 Debug = %d, remove files in cleanup : %d\n",
+ Progname, Pid, Debug, remove_files);
+
+ printf("%s: %d DEBUG3 Mode = %#o\n", Progname, Pid, Mode);
+
+ if ( open_flags == RANDOM_OPEN )
+ printf("%s: %d DEBUG3 open_flags = (random), io_type = %#o\n", Progname,
+ Pid, io_type);
+ else
+ printf("%s: %d DEBUG3 open_flags = %#o, io_type = %#o\n", Progname,
+ Pid, open_flags, io_type);
+
+ if ( Mode & MODE_RAND_SIZE ) {
+ printf("%s: %d DEBUG3 random write/trunc: min=%d, max=%d, mult = %d\n",
+ Progname, Pid, min_size, max_size, mult_size);
+ }
+ else {
+ printf("%s: %d DEBUG3 grow_incr = %d\n",
+ Progname, Pid, grow_incr);
+ }
+ if ( Mode & MODE_RAND_LSEEK ) {
+ if ( max_lseek == LSK_EOF )
+ printf("%s: %d DEBUG3 random lseek: min=%d, max=<endoffile>\n",
+ Progname, Pid, min_lseek);
+ else if ( max_lseek == LSK_EOFPLUSGROW )
+ printf("%s: %d DEBUG3 random lseek: min=%d, max=<endoffile+iosize>\n",
+ Progname, Pid, min_lseek);
+ else if ( max_lseek == LSK_EOFMINUSGROW )
+ printf("%s: %d DEBUG3 random lseek: min=%d, max=<endoffile-iosize>\n",
+ Progname, Pid, min_lseek);
+ else
+ printf("%s: %d DEBUG3 random lseek: min=%d, max=%d\n",
+ Progname, Pid, min_lseek, max_lseek);
+ }
+
+ printf("%s: %d DEBUG3 check write interval = %d, check file interval = %d\n",
+ Progname, Pid, write_check_inter, file_check_inter);
+
+ printf("%s: %d DEBUG3 trunc interval = %d, trunc_incr = %d\n",
+ Progname, Pid, trunc_inter, trunc_incr);
+
+ if ( no_file_check )
+ printf("%s: %d DEBUG3 no whole file checking will be done\n",
+ Progname, Pid);
+
+ if ( unlink_inter_ran == -1 ) {
+ printf("%s: %d DEBUG3 unlink_inter = %d\n",
+ Progname, Pid, unlink_inter);
+ } else {
+ printf("%s: %d DEBUG3 unlink_inter = %d, unlink_inter_ran = %d\n",
+ Progname, Pid, unlink_inter, unlink_inter_ran);
+ }
+
+ if ( Debug > 8 ) {
+ num=sizeof(Open_flags)/sizeof(int);
+ printf("%s: %d DEBUG9 random open flags values:\n", Progname, Pid);
+ for(ind=0; ind<num; ind++) {
+ printf("\t%#o\n", Open_flags[ind]);
+ }
+ }
+ } /* end of DEBUG > 2 */
+
+ if ( Debug > 1 && num_procs > 1 ) {
+ printf("%s: %d DEBUG2 about to fork %d more copies\n", Progname,
+ Opid, num_procs-1);
+ }
+
+ fflush(stdout); /* ensure pending i/o is flushed before forking */
+ fflush(stderr);
+
+ forker(num_procs, forker_mode, Progname);
+
+ Pid=getpid(); /* reset after the forks */
+ /*
+ * If user specified random seed(s), get that random seed value.
+ * get random seed if it was not specified by the user.
+ * This is done after the forks, because pid is used to get the seed.
+ */
+ if ( Nseeds == 1 ) {
+ /*
+ * If only one seed specified, all processes will get that seed.
+ */
+ Seed=Seeds[0];
+ } else if ( Nseeds > 1 ) {
+ /*
+ * More than one seed was specified.
+ * The original process gets the first seed. Each
+ * process will be get the next seed in the specified list.
+ */
+ if ( Opid == Pid ) {
+ Seed=Seeds[0];
+ } else {
+ /*
+ * If user didn't specify enough seeds, use default method.
+ */
+ if ( Forker_npids >= Nseeds )
+ Seed=time(0) + Pid; /* default random seed */
+ else {
+ Seed=Seeds[Forker_npids];
+ }
+ }
+ } else {
+ /*
+ * Generate a random seed based on time and pid.
+ * It has a good chance of being unique for each pid.
+ */
+ Seed=time(0) + Pid; /* default random seed */
+ }
+
+ random_range_seed(Seed);
+
+ if ( using_random && Debug > 0 )
+ printf("%s%s: %d DEBUG1 Using random seed of %d\n",
+ Progname, TagName, Pid, Seed);
+
+ if ( unlink_inter_ran > 0 ) {
+ /*
+ * Find unlinking file interval. This must be done after
+ * the seed was set. This allows multiple copies to
+ * get different intervals.
+ */
+ tmp=unlink_inter;
+ unlink_inter=random_range(tmp, unlink_inter_ran, 1, NULL);
+
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 Unlink interval is %d (random %d - %d)\n",
+ Progname, Pid, unlink_inter, tmp, unlink_inter_ran);
+ }
+
+ /*
+ * re-exec all childern if reexec is set to REXEC_DOIT.
+ * This is useful on MPP systems to get the
+ * child process on another PE.
+ */
+ if ( reexec == REXEC_DOIT && Opid != Pid ) {
+ if ( exec_path == NULL ) {
+ exec_path = argv[0];
+ /* Get space for cmd (2 extra, 1 for - and 1 fro NULL */
+ argv[0] = (char *)malloc(strlen(exec_path) + 2);
+ sprintf(argv[0], "-%s", exec_path);
+ }
+
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: execvp(%s, argv)\n",
+ Progname, Pid, __FILE__, __LINE__, argv[0]);
+
+ execvp(argv[0], argv);
+ }
+
+ /*** begin filename stuff here *****/
+ /*
+ * Determine the number of files to be dealt with
+ */
+ if ( optind == argc ) {
+ /*
+ * no cmd line files, therfore, set
+ * the default number of auto created files
+ */
+ if ( ! num_auto_files && ! seq_auto_files )
+ num_auto_files=1;
+ }
+ else {
+ first_file_ind=optind;
+ num_files += argc-optind;
+ }
+
+ if ( num_auto_files ) {
+ num_files += num_auto_files;
+ }
+
+ if ( seq_auto_files ) {
+ num_files += seq_auto_files;
+ }
+
+ /*
+ * get space for file names
+ */
+ if ((filenames=(char *)malloc(num_files*PATH_MAX)) == NULL) {
+ fprintf(stderr, "%s%s: %d %s/%d: malloc(%d) failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, num_files*PATH_MAX,
+ strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * fill in filename cmd files then auto files.
+ */
+
+ num=0;
+ if ( first_file_ind ) {
+ for(ind=first_file_ind; ind<argc; ind++, num++) {
+ strcpy((char *)filenames+(num*PATH_MAX), argv[ind]);
+ }
+ }
+
+ /*
+ * construct auto filename and insert them into filenames space
+ */
+
+ for(ind=0;ind<num_auto_files; ind++, num++) {
+ sprintf((char *)filenames+(num*PATH_MAX), "%s.%d",
+ tempnam(auto_dir, auto_file), ind );
+ }
+
+ /*
+ * construct auto seq filenames
+ */
+ for(ind=1; ind<=seq_auto_files; ind++, num++) {
+ sprintf((char *)filenames+(num*PATH_MAX), "%s/%s%d",
+ auto_dir, auto_file, ind);
+ }
+
+/**** end filename stuff ****/
+
+ if ( time_iterval > 0 )
+ start_time=time(0);
+
+ /*
+ * get space for I/O buffer
+ */
+ if ( grow_incr ) {
+ if ((Buffer=(char *)malloc(grow_incr+Alignment)) == NULL) {
+ fprintf(stderr, "%s%s: %d %s/%d: malloc(%d) failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, grow_incr, strerror(errno));
+ exit(1);
+ }
+ if ( Alignment )
+ Buffer = Buffer + Alignment;
+
+ }
+
+ if ( Debug > 2 ) {
+ printf("%s: %d DEBUG3 num_files = %d\n",
+ Progname, Pid, num_files);
+ }
+
+#ifndef linux
+ if ( pre_alloc_space ) {
+ if ( iterations == 0 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: can NOT pre-alloc and grow forever\n",
+ Progname, TagName, Pid, __FILE__, __LINE__);
+ exit(1);
+ }
+ if ( Mode & MODE_RAND_SIZE ) {
+ fprintf(stderr,
+ "%s%s: %d %s/%d: can NOT pre-alloc and do random io size\n",
+ Progname, TagName, Pid, __FILE__, __LINE__);
+ exit(1);
+ }
+
+ total_grow_value=grow_incr * iterations;
+
+ /*
+ * attempt to limit
+ */
+ if ( bytes_to_consume && bytes_to_consume < total_grow_value ) {
+ total_grow_value=bytes_to_consume;
+ }
+ }
+#endif
+
+ /*
+ * If delaying between iterations, get amount time to
+ * delaysecs in clocks or usecs.
+ * If on the CRAY, delaytime is in clocks since
+ * _rtc() will be used, which does not have the overhead
+ * of gettimeofday(2).
+ */
+ if ( delaysecs ) {
+#if CRAY
+ int hz;
+ hz=sysconf(_SC_CLK_TCK);
+ delaytime=(int)((float)hz * delaysecs);
+#else
+ delaytime=(int)((float)USECS_PER_SEC * delaysecs);
+#endif
+ }
+
+ /*
+ * This is the main iteration loop.
+ * Each iteration, all files can be opened, written to,
+ * read to check the write, check the whole file,
+ * truncated, and closed.
+ */
+ for(Iter_cnt=1; ! stop ; Iter_cnt++) {
+
+ if ( iterations && Iter_cnt >= iterations+1 ) {
+ strcpy(reason, "Hit iteration value");
+ stop=1;
+ continue;
+ }
+
+ if ( (time_iterval > 0) && (start_time + time_iterval < time(0)) ) {
+ sprintf(reason, "Hit time value of %d", time_iterval);
+ stop=1;
+ continue;
+ }
+
+ if ( bytes_to_consume && bytes_consumed >= bytes_to_consume) {
+ sprintf(reason, "Hit bytes consumed value of %d", bytes_to_consume);
+ stop=1;
+ continue;
+ }
+
+ /*
+ * This loop will loop through all files.
+ * Each iteration, a single file can be opened, written to,
+ * read to check the write, check the whole file,
+ * truncated, and closed.
+ */
+ for(ind=0; ind<num_files; ind++) {
+
+ fflush(stdout);
+ fflush(stderr);
+
+ filename=(char *)filenames+(ind*PATH_MAX);
+ Fileinfo.filename=(char *)filenames+(ind*PATH_MAX);
+
+
+ if ( open_flags == RANDOM_OPEN ) {
+ ret=Open_flags[random_range(0, sizeof(Open_flags)/sizeof(int)-1, 1, NULL)];
+ }
+
+ else
+ ret=open_flags;
+
+ Fileinfo.openflags=ret;
+
+ if ( Debug > 3 ) {
+ printf("%s: %d DEBUG3 %s/%d: %d Open filename = %s, open flags = %#o %s\n",
+ Progname, Pid, __FILE__, __LINE__, Iter_cnt, filename, ret,
+ openflags2symbols(ret, ",", NULL));
+ } else if ( Debug > 2 ) {
+ printf("%s: %d DEBUG3 %s/%d: %d filename = %s, open flags = %#o\n",
+ Progname, Pid, __FILE__, __LINE__, Iter_cnt, filename, ret);
+ }
+
+ /*
+ * open file with desired flags.
+ */
+ if ( (fd=open(filename, ret, 0777)) == -1 ) {
+ fprintf(stderr,
+ "%s%s: %d %s/%d: open(%s, %#o, 0777) returned -1, errno:%d %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, filename, ret, errno, strerror(errno));
+ handle_error();
+ continue;
+ }
+
+ Fileinfo.fd=fd;
+
+ lkfile(fd, LOCK_EX, LKLVL1); /* lock if lockfile is LKLVL1 */
+
+#ifndef linux
+ /*
+ * preallocation is only done once, if specified.
+ */
+ if ( pre_alloc_space ) {
+ if (pre_alloc(fd, total_grow_value) != 0 ) {
+ cleanup();
+ exit(2);
+ }
+ if ( Debug > 1 ) {
+ printf("%s: %d DEBUG2 %s/%d: pre_allocated %d for file %s\n",
+ Progname, Pid, __FILE__, __LINE__, total_grow_value, filename);
+ }
+ lkfile(fd, LOCK_UN, LKLVL1); /* release lock */
+ close(fd);
+ Iter_cnt=0; /* reset outside loop to restart from one */
+ continue;
+ }
+#endif
+
+ /*
+ * grow file by desired amount.
+ * growfile() will set the Grow_incr variable and
+ * possiblly update the Mode variable indicating
+ * if we are dealing with a FIFO file.
+ */
+
+ if (growfile(fd, filename, grow_incr, Buffer) != 0 ) {
+ handle_error();
+ lkfile(fd, LOCK_UN, LKLVL1); /* release lock */
+ close(fd);
+ continue;
+ }
+
+ /*
+ * check if last write is not corrupted
+ */
+ if ( check_write(fd, write_check_inter, filename,
+ Mode) != 0 ) {
+ handle_error();
+ }
+
+ /*
+ * Check that whole file is not corrupted.
+ */
+ if ( check_file(fd, file_check_inter, filename,
+ no_file_check) != 0 ) {
+ handle_error();
+ }
+
+ /*
+ * shrink file by desired amount if it is time
+ */
+
+ if ( shrinkfile(fd, filename, trunc_incr, trunc_inter, Mode) != 0 ) {
+ handle_error();
+ }
+
+ lkfile(fd, LOCK_UN, LKLVL1); /* release lock */
+
+ if ( Debug > 4 )
+ printf("%s: %d DEBUG5 %s/%d: %d Closing file %s fd:%d \n",
+ Progname, Pid, __FILE__, __LINE__, Iter_cnt, filename, fd);
+ close(fd);
+
+ /*
+ * Unlink the file if that is desired
+ */
+ if ( unlink_inter && (Iter_cnt % unlink_inter == 0) ) {
+
+ if ( Debug > 4 )
+ printf("%s: %d DEBUG5 %s/%d: %d Unlinking file %s\n",
+ Progname, Pid, __FILE__, __LINE__, Iter_cnt, filename);
+
+ unlink(filename);
+ }
+
+ /*
+ * delay while staying active for "delaysecs" seconds.
+ */
+ if ( delaytime ) {
+
+ int ct, end;
+#ifdef CRAY
+ ct=_rtc();
+ end=ct+delaytime;
+ while ( ct < end ) {
+ ct = _rtc();
+ }
+#else
+ struct timeval curtime;
+ gettimeofday(&curtime, NULL);
+ ct=curtime.tv_sec*USECS_PER_SEC + curtime.tv_usec;
+ end=ct+delaytime;
+ while ( ct < end ) {
+
+ gettimeofday(&curtime, NULL);
+ ct=curtime.tv_sec*USECS_PER_SEC + curtime.tv_usec;
+ }
+#endif
+ }
+ }
+#ifndef linux
+ /*
+ * if Iter_cnt == 0, then we pre allocated space to all files
+ * and we are starting outside loop over. Set pre_alloc_space
+ * to zero otherwise we get in infinite loop
+ */
+ if ( Iter_cnt == 0 ) {
+ pre_alloc_space=0;
+ }
+#endif
+
+
+ } /* end iteration for loop */
+
+
+ if ( Debug ) {
+ printf("%s%s: %d %s/%d: DONE %d iterations to %d files. %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, num_files, reason);
+ }
+ fflush(stdout);
+ fflush(stderr);
+
+ cleanup();
+
+ if ( Errors ) {
+ if ( Debug > 2 ) {
+ printf("%s%s: %d DEBUG3 %d error(s) encountered\n",
+ Progname, TagName, Pid, Errors);
+ printf("%s%s: %d DEBUG3 %s/%d: exiting with value of 1\n", Progname, TagName, Pid, __FILE__, __LINE__);
+ }
+ exit(1);
+ }
+ if ( Debug > 2 )
+ printf("%s%s: %d DEBUG3 %s/%d: no errors, exiting with value of 0\n", Progname, TagName, Pid, __FILE__, __LINE__);
+ exit(0);
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+set_sig()
+{
+ int sig;
+
+
+ /*
+ * now loop through all signals and set the handlers
+ */
+
+ for (sig = 1; sig < NSIG; sig++) {
+ switch (sig) {
+ case SIGKILL:
+ case SIGSTOP:
+ case SIGCONT:
+#ifdef CRAY
+ case SIGINFO:
+ case SIGRECOVERY:
+#endif /* CRAY */
+#ifdef SIGCKPT
+ case SIGCKPT:
+#endif /* SIGCKPT */
+#ifdef SIGRESTART
+ case SIGRESTART:
+#endif /* SIGRESTART */
+ case SIGCLD:
+ break;
+
+ default:
+#ifdef sgi
+ sigset( sig, sig_handler );
+#else
+/* linux and cray */
+ signal(sig, sig_handler);
+#endif
+ break;
+ }
+ } /* endfor */
+
+
+ return 0;
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+void
+sig_handler(sig)
+int sig;
+{
+ int exit_stat = 2;
+
+ if ( sig == SIGUSR2 ) {
+ fprintf(stdout, "%s%s: %d %s/%d: received SIGUSR2 (%d) - stopping.\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, sig);
+#ifndef sgi
+ signal(sig, sig_handler); /* allow us to get this signal more than once */
+#endif
+
+ } else if( sig == SIGINT ){
+ /* The user has told us to cleanup, don't pretend it's an error. */
+ exit_stat=0;
+ if ( Debug != 0 ){
+ fprintf(stderr, "%s%s: %d %s/%d: received unexpected signal: %d\n", Progname, TagName,
+ Pid, __FILE__, __LINE__, sig);
+ }
+ } else {
+ fprintf(stderr, "%s%s: %d %s/%d: received unexpected signal: %d\n", Progname, TagName,
+ Pid, __FILE__, __LINE__, sig);
+ }
+
+ notify_others();
+ cleanup();
+ if ( Debug > 2 ){
+ printf("%s%s: %d DEBUG3 %s/%d: Exiting with a value of %d\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, exit_stat);
+ }
+ exit(exit_stat);
+}
+
+/***********************************************************************
+ * this function attempts to send SIGUSR2 to other growfiles processes
+ * telling them to stop.
+ *
+ ***********************************************************************/
+static void
+notify_others()
+{
+ static int send_signals = 0;
+ int ind;
+ extern int Forker_pids[];
+ extern int Forker_npids;
+
+ if ( Sync_with_others && send_signals == 0 ) {
+
+#if CRAY
+ send_signals=1; /* only send signals once */
+ if ( Debug > 1 )
+ printf("%s%s: %d DEBUG2 %s/%d: Sending SIGUSR2 to pgrp\n",
+ Progname, TagName, Pid, __FILE__, __LINE__);
+ killm(C_PGRP, getpgrp(), SIGUSR2);
+#else
+ send_signals=1; /* only send signals once */
+
+ for (ind=0; ind< Forker_npids; ind++) {
+ if ( Forker_pids[ind] != Pid )
+ if ( Debug > 1 )
+ printf("%s%s: %d DEBUG2 %s/%d: Sending SIGUSR2 to pid %d\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Forker_pids[ind]);
+ kill(Forker_pids[ind], SIGUSR2);
+ }
+#endif
+ }
+
+}
+
+/***********************************************************************
+ * this function will count the number of errors encountered.
+ * This function will call upanic if wanted or cleanup and
+ * and exit is Maxerrs were encountered.
+ ***********************************************************************/
+int
+handle_error()
+{
+ Errors++;
+
+#ifdef CRAY
+ if ( Errors & Upanic_on_error ) {
+ upanic(PA_PANIC);
+ }
+#endif
+
+ if ( Maxerrs && Errors >= Maxerrs ) {
+ printf("%s%s: %d %s/%d: %d Hit max errors value of %d\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, Maxerrs);
+ notify_others();
+ cleanup();
+
+ if ( Debug > 2 ) {
+ printf("%s%s: %d DEBUG3 %d error(s) encountered\n",
+ Progname, TagName, Pid, Errors);
+ printf("%s%s: %d DEBUG3 %s/%d: exiting with value of 1\n", Progname, TagName, Pid, __FILE__, __LINE__);
+ }
+
+ exit(1);
+ }
+
+ return 0;
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+cleanup()
+{
+ int ind;
+
+ if ( remove_files ) {
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 Removing all %d files\n",
+ Progname, Pid, num_files);
+ for(ind=0; ind<=num_files; ind++) {
+ unlink(filenames+(ind*PATH_MAX));
+ }
+ }
+ if ( using_random && Debug > 1 )
+ printf("%s%s: %d DEBUG2 Used random seed: %d\n",
+ Progname, TagName, Pid, Seed);
+ return 0;
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+void
+usage()
+{
+ fprintf(stderr,
+ "Usage: %s%s [-bhEluy][[-g grow_incr][-i num][-t trunc_incr][-T trunc_inter]\n",
+ Progname, TagName );
+ fprintf(stderr,
+ "[-d auto_dir][-e maxerrs][-f auto_file][-N num_files][-w][-c chk_inter][-D debug]\n");
+ fprintf(stderr,
+ "[-s seed][-S seq_auto_files][-p][-P PANIC][-I io_type][-o open_flags][-B maxbytes]\n");
+ fprintf(stderr,
+ "[-r iosizes][-R lseeks][-U unlk_inter][-W tagname] [files]\n");
+
+ return;
+
+} /* end of usage */
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+void
+help()
+{
+ usage();
+
+fprintf(stdout, "\
+ -h Specfied to print this help and exit.\n\
+ -b Specfied to execute in sync mode.(def async mode)\n\
+ -B maxbytes Max bytes to consume by all files. growfiles exits when more\n\
+ than maxbytes have been consumed. (def no chk) If maxbytes ends\n\
+ with the letter 'b', maxbytes is multiplied by BSIZE\n\
+ -C write_chk Specifies how often to check the last write (default 1)\n\
+ -c file_chk Specifies how often to check whole file (default 0)\n\
+ -d auto_dir Specifies the directory to auto created files. (default .)\n\
+ -D debug_lvl Specifies the debug level (default 1)\n\
+ -E Print examples and exit\n\
+ -e errs The number errors that will terminate this program (def 100)\n\
+ -f auto_file Specifies the base filename files created. (default \"gf\")\n\
+ -g grow_incr Specfied to grow by incr for each num. (default 4096)\n\
+ grow_incr may end in b for blocks\n\
+ If -r option is used, this option is ignored and size is random\n\
+ -H delay Amount of time to delay between each file (default 0.0)\n\
+ -I io_type Specifies io type: s - sync, p - polled async, a - async (def s)\n\
+ l - listio sync, L - listio async, r - random\n\
+ -i iteration Specfied to grow each file num times. 0 means forever (default 1)\n\
+ -l Specfied to do file locking around write/read/trunc\n\
+ If specified twice, file locking after open to just before close\n\
+ -L time Specfied to exit after time secs, must be used with -i.\n\
+ -N num_files Specifies the number of files to be created.\n\
+ The default is zero if cmd line files.\n\
+ The default is one if no cmd line files.\n\
+ -n num_procs Specifies the number of copies of this cmd.\n\
+ -o op_type Specifies open flages: (def O_RDWR,O_CREAT) op_type can be 'random'\n\
+ -O offset adjust i/o buffer alignment by offset bytes\n\
+ -P PANIC Specifies to call upanic on error.\n\
+ -p Specifies to pre-allocate space\n\
+ -q pattern pattern can be a - ascii, p - pid with boff, o boff (def)\n\
+ A - Alternating bits, r - random, O - all ones, z - all zeros,\n\
+ c - checkboard, C - counting\n\
+ -R [min-]max random lseek before write and trunc, max of -1 means filesz,\n\
+ -2 means filesz+grow, -3 filesz-grow. (min def is 0)\n\
+ -r [min-]max random io write size (min def is 1)\n\
+ -S seq_auto_files Specifies the number of seqental auto files (default 0)\n\
+ -s seed[,seed...] Specifies the random number seed (default time(0)+pid)\n\
+ -t trunc_incr Specfied the amount to shrink file. (default 4096)\n\
+ trunc_inter may end in b for blocks\n\
+ If -R option is used, this option is ignored and trunc is random\n\
+ -T trunc_inter Specfied the how many grows happen before shrink. (default 0)\n\
+ -u unlink files before exit\n\
+ -U ui[-ui2] Unlink files each ui iteration (def 0)\n\
+ -w Specfied to grow via lseek instead of writes.\n\
+ -W tag-name Who-am-i. My Monster tag name. (used by Monster).\n\
+ -x Re-exec children before continuing - useful on MPP systems\n\
+ -y Attempt to sync copies - if one fails it will send sigusr2 to others\n\
+ Action to each file every iteration is open, write, write check\n\
+ file check, trunc and closed.\n");
+
+ return;
+}
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+void
+prt_examples(FILE *stream)
+{
+ /* This example creates 200 files in directory dir1. It writes */
+ /* 4090 bytes 100 times then truncates 408990 bytes off the file */
+ /* The file contents are checked every 1000 grow. */
+ fprintf(stream,
+ "# run forever: writes of 4090 bytes then on every 100 iterval\n\
+# truncate file by 408990 bytes. Done to 200 files in dir1.\n\
+%s -i 0 -g 4090 -T 100 -t 408990 -l -C 10 -c 1000 -d dir1 -S 200\n\n", Progname);
+
+ /* same as above with 5000 byte grow and a 499990 byte tuncate */
+ fprintf(stream,
+ "# same as above with writes of 5000 bytes and truncs of 499990\n\
+%s -i 0 -g 5000 -T 100 -t 499990 -l -C 10 -c 1000 -d dir2 -S 200\n\n", Progname);
+
+ /* This example beats on opens and closes */
+ fprintf(stream,
+ "# runs forever: beats on opens and closes of file ocfile - no io\n\
+%s -i 0 -g 0 -c 0 -C 0 ocfile\n\n", Progname);
+
+ fprintf(stream,
+ "# writes 4096 to files until 50 blocks are written\n\
+%s -i 0 -g 4096 -B 50b file1 file2\n\n", Progname);
+
+ fprintf(stream,
+ "# write one byte to 750 files in gdir then unlinks them\n\
+%s -g 1 -C 0 -d gdir -u -S 750\n\n", Progname);
+
+ fprintf(stream,
+ "# run 30 secs: random iosize, random lseek up to eof\n\
+%s -r 1-5000 -R 0--1 -i 0 -L 30 -C 1 g_rand1 g_rand2\n\n", Progname);
+
+ fprintf(stream,
+ "# run 30 secs: grow by lseek then write single byte, trunc every 10 itervals\n\
+%s -g 5000 -wlu -i 0 -L 30 -C 1 -T 10 g_sleek1 g_lseek2\n\n", Progname);
+
+ fprintf(stream,
+ "# run forever: 5 copies of random iosize, random lseek to beyond eof,\n\
+# rand io types doing a trunc every 5 iterations, with unlinks.\n\
+%s -i0 -r 1-50000 -R 0--2 -I r -C1 -l -n5 -u -U 100-200 gf_rana gf_ranb\n\n",
+ Progname);
+
+ fprintf(stream,
+ "# run forever: 5 copies of random iosize, random lseek to beyond eof,\n\
+# random open flags, rand io types doing a trunc every 10 iterations.\n\
+%s -i0 -r 1-50000 -R 0--2 -o random -I r -C0 -l -T 20 -uU100-200 -n 5 gf_rand1 gf_rand2\n",
+ Progname);
+
+
+ return;
+}
+
+/***********************************************************************
+ *
+ * The file descriptor current offset is assumed to be the end of the
+ * file.
+ * Woffset will be set to the offset before the write.
+ * Grow_incr will be set to the size of the write or lseek write.
+ ***********************************************************************/
+int
+growfile(fd, file, grow_incr, buf)
+int fd;
+char *file;
+int grow_incr;
+char *buf;
+{
+ int noffset;
+ int ret;
+ int cur_offset;
+ char *errmsg;
+ int fsize; /* current size of file */
+ int size_grew; /* size the file grew */
+ struct stat stbuf;
+ int tmp = 0;
+
+ /*
+ * Do a stat on the open file.
+ * If the file is a fifo, set the bit in Mode variable.
+ * This fifo check must be done prior to growfile() returning.
+ * Also get the current size of the file.
+ */
+ if ( fstat(fd, &stbuf) != -1 ) {
+ if ( S_ISFIFO(stbuf.st_mode) ) {
+ Fileinfo.mode |= MODE_FIFO;
+ Mode |= MODE_FIFO;
+ if ( Debug > 3 )
+ printf("%s: %d DEBUG4 %s/%d: file is a fifo - no lseek or truncs,\n",
+ Progname, Pid, __FILE__, __LINE__);
+ }
+ fsize = stbuf.st_size;
+
+ } else {
+ fprintf(stderr, "%s%s: %d %s/%d: Unable to fstat(%d, &buf), errno:%d %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, fd, errno, strerror(errno));
+
+ return -1;
+ }
+
+
+ if ( grow_incr <= 0 ) { /* don't attempt i/o if grow_incr <= 0 */
+
+ Grow_incr=grow_incr;
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: Not attempting to grow, growsize == %d\n",
+ Progname, Pid, __FILE__, __LINE__, grow_incr);
+ return grow_incr;
+ }
+
+ if ( Mode & MODE_RAND_SIZE ) {
+ grow_incr=random_range(min_size, max_size, mult_size, &errmsg);
+ if (errmsg != NULL) {
+ fprintf(stderr, "%s%s: %d %s/%d: random_range() failed - %s\n", Progname, TagName, Pid, __FILE__, __LINE__, errmsg);
+ return -1;
+ }
+ Grow_incr=grow_incr;
+ }
+ else
+ Grow_incr=grow_incr;
+
+ if ( ! (Mode & MODE_FIFO) ) {
+ if ((cur_offset=lseek(fd,0,SEEK_CUR)) == -1 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: tell failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, strerror(errno));
+ return -1;
+ }
+ }
+
+ if ( Mode & MODE_GROW_BY_LSEEK ) {
+ Woffset=fsize;
+ if ( Debug > 2 ) {
+ printf("%s: %d DEBUG3 %s/%d: Current size of file is %d\n", Progname,
+ Pid, __FILE__, __LINE__, Woffset);
+ printf("%s: %d DEBUG3 %s/%d: lseeking to %d byte with SEEK_END\n", Progname,
+ Pid, __FILE__, __LINE__, grow_incr-1);
+ }
+
+ if ((noffset=lseek(fd, grow_incr-1, SEEK_END)) == -1 ) {
+ fprintf(stderr, "%s%s: %s/%d: lseek(fd, %d, SEEK_END) failed: %s\n",
+ Progname, TagName, __FILE__, __LINE__, grow_incr-1, strerror(errno));
+ return -1;
+ }
+
+ lkfile(fd, LOCK_EX, LKLVL0); /* get exclusive lock */
+
+#if NEWIO
+ ret=lio_write_buffer(fd, io_type, "w", 1, SIGUSR1, &errmsg,0);
+#else
+ ret=write_buffer(fd, io_type, "w", 1, 0, &errmsg);
+#endif
+
+ if ( ret != 1 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: %d %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+ if ( ret == -ENOSPC ) {
+ cleanup();
+ exit(2);
+ }
+ }
+/***
+ write(fd, "w", 1);
+****/
+
+ lkfile(fd, LOCK_UN, LKLVL0);
+
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: %d wrote 1 byte to file\n",
+ Progname, Pid, __FILE__, __LINE__, Iter_cnt);
+
+ } else { /* end of grow by lseek */
+
+ if ( Fileinfo.openflags & O_APPEND ) {
+ /*
+ * Deal with special case of the open flag containing O_APPEND.
+ * If it does, the current offset does not matter since the write
+ * will be done end of the file.
+ */
+ if ( Debug > 4 )
+ printf("%s: %d DEBUG5 %s/%d: dealing with O_APPEND condition\n",
+ Progname, Pid, __FILE__, __LINE__ );
+ lkfile(fd, LOCK_EX, LKLVL0); /* get exclusive lock */
+
+ /*
+ * do fstat again to get size of the file.
+ * This is done inside a file lock (if locks are being used).
+ */
+ if ( fstat(fd, &stbuf) != -1 ) {
+ Woffset = stbuf.st_size;
+ } else {
+ fprintf(stderr, "%s%s: %d %s/%d: Unable to fstat(%d, &buf), errno:%d %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, fd, errno, strerror(errno));
+
+ lkfile(fd, LOCK_UN, LKLVL0); /* release lock */
+ return -1;
+ }
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: dealing with O_APPEND condition (offset:fsz:%d)\n",
+ Progname, Pid, __FILE__, __LINE__, (int)stbuf.st_size);
+
+
+ } else if ( Mode & MODE_RAND_LSEEK ) {
+ if ( max_lseek == LSK_EOF ) { /* within file size */
+ noffset=random_range(min_lseek, fsize, 1, NULL);
+ }
+ else if ( max_lseek == LSK_EOFPLUSGROW ) {
+ /* max to beyond file size */
+ noffset=random_range(min_lseek, fsize+grow_incr, 1, NULL);
+ }
+ else if ( max_lseek == LSK_EOFMINUSGROW ) {
+ /*
+ * Attempt to not grow the file.
+ * If the i/o will fit from min_lseek to EOF,
+ * pick offset to allow it to fit.
+ * Otherwise, pick the min_lseek offset and grow
+ * file by smallest amount.
+ * If min_lseek is != 0, there will be a problem
+ * with whole file checking if file is ever smaller
+ * than min_lseek.
+ */
+ if ( fsize <= min_lseek + grow_incr )
+ noffset=min_lseek; /* file will still grow */
+ else
+ noffset=random_range(min_lseek, fsize-grow_incr, 1, NULL);
+ }
+ else {
+ noffset=random_range(min_lseek, max_lseek, 1, NULL);
+ }
+
+ if ((Woffset=lseek(fd, noffset, SEEK_SET)) == -1 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: lseek(%d, %d, SEEK_SET) l2 failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, fd, noffset, strerror(errno));
+ return -1;
+ }
+ else if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: lseeked to random offset %d (fsz:%d)\n",
+ Progname, Pid, __FILE__, __LINE__, Woffset,
+ (int)stbuf.st_size);
+
+ }
+
+ /*
+ * lseek to end of file only if not fifo
+ */
+ else if ( ! (Mode & MODE_FIFO) ) {
+ if ((Woffset=lseek(fd, 0, SEEK_END)) == -1 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: lseek(fd, 0, SEEK_END) failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, strerror(errno));
+ return -1;
+ }
+ else if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: lseeked to end of file, offset %d\n",
+ Progname, Pid, __FILE__, __LINE__, Woffset);
+ }
+
+ if ( Pattern == PATTERN_OFFSET )
+ datapidgen(STATIC_NUM, buf, grow_incr, Woffset);
+ else if ( Pattern == PATTERN_PID )
+ datapidgen(Pid, buf, grow_incr, Woffset);
+ else if ( Pattern == PATTERN_ASCII )
+ dataasciigen(NULL, buf, grow_incr, Woffset);
+ else if ( Pattern == PATTERN_RANDOM )
+ databingen('r', buf, grow_incr, Woffset);
+ else if ( Pattern == PATTERN_ALT )
+ databingen('a', buf, grow_incr, Woffset);
+ else if ( Pattern == PATTERN_CHKER )
+ databingen('c', buf, grow_incr, Woffset);
+ else if ( Pattern == PATTERN_CNTING )
+ databingen('C', buf, grow_incr, Woffset);
+ else if ( Pattern == PATTERN_ZEROS )
+ databingen('z', buf, grow_incr, Woffset);
+ else if ( Pattern == PATTERN_ONES )
+ databingen('o', buf, grow_incr, Woffset);
+ else
+ dataasciigen(NULL, buf, grow_incr, Woffset);
+
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: attempting to write %d bytes\n",
+ Progname, Pid, __FILE__, __LINE__, grow_incr);
+
+ lkfile(fd, LOCK_EX, LKLVL0); /* get exclusive lock */
+
+/*****
+ ret=write(fd, buf, grow_incr);
+
+ tmp=tell(fd);
+
+ lkfile(fd, LOCK_UN, LKLVL0);
+
+ if ( ret != grow_incr) {
+ fprintf(stderr, "%s: %s/%d: write failed: %s\n",
+ Progname, __FILE__, __LINE__, strerror(errno));
+ return -1;
+ }
+*****/
+
+#if NEWIO
+ ret=lio_write_buffer(fd, io_type, buf, grow_incr,
+ SIGUSR1, &errmsg,0);
+#else
+ ret=write_buffer(fd, io_type, buf, grow_incr, 0, &errmsg);
+#endif
+
+ if( Mode & MODE_FIFO ){
+ /* If it is a fifo then just pretend the file
+ * offset is where we think it should be.
+ */
+ tmp = Woffset + grow_incr;
+ }
+ else{
+ if( (tmp=lseek(fd,0,SEEK_CUR)) < 0 ){ /* get offset after the write */
+ fprintf(stderr, "%s%s: %s/%d: tell(2) failed: %d %s\n",
+ Progname, TagName, __FILE__, __LINE__, errno, strerror(errno) );
+ return -1;
+ }
+#if NEWIO
+#ifdef sgi
+ /* If this is POSIX I/O and it is via aio_{read,write}
+ * or lio_listio then after completion of the I/O the
+ * value of the file offset for the file is
+ * unspecified--which means we cannot trust what
+ * tell() told us. Fudge it here.
+ */
+ if( (io_type & LIO_IO_ASYNC_TYPES) || (io_type & LIO_RANDOM) ){
+ if( tmp != Woffset + grow_incr ){
+ if( Debug > 5 ){
+ printf("%s: %d DEBUG6 %s/%d: posix fudge, forcing tmp (%d) to match Woffset+grow_incr (%d)\n",
+ Progname, Pid, __FILE__, __LINE__, tmp, Woffset+grow_incr);
+ }
+ tmp = Woffset + grow_incr;
+ }
+ }
+#endif
+#endif
+ }
+
+ lkfile(fd, LOCK_UN, LKLVL0);
+
+ if ( ret != grow_incr ) {
+ fprintf(stderr, "%s%s: %d %s/%d: %d %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+ if ( ret == -ENOSPC ) {
+ cleanup();
+ exit(2);
+ }
+ return -1;
+ }
+
+ /*
+ * Check for a condition where the file was truncated just before
+ * the write.
+ */
+ if ( tmp != Woffset + grow_incr) {
+ /*
+ * The offset after the write was not as expected.
+ * This could be caused by the following:
+ * - file truncated after the lseek and before the write.
+ * - the file was written to after fstat and before the write
+ * and the file was opened with O_APPEND.
+ *
+ * The pattern written to the file will be considered corrupted.
+ */
+ if ( Debug > 0 && lockfile ) {
+ printf("%s%s: %d DEBUG1 %s/%d: offset after write(%d) not as exp(%d+%d=%d)\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, tmp, Woffset, grow_incr, Woffset+grow_incr);
+ printf("%s%s: %d DEBUG1 %s/%d: %d Assuming file changed by another process, resetting offset:%d (expect pattern mismatch)\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, tmp-grow_incr);
+ }
+ if( Debug > 4 ){
+ printf("%s: %d DEBUG5 %s/%d: about to chop Woffset. tmp=%d, grow_incr=%d, Woffset was %d\n",
+ Progname, Pid, __FILE__, __LINE__, tmp, grow_incr, Woffset);
+ }
+ Woffset=tmp-grow_incr;
+ if( Woffset < 0 )
+ Woffset = 0;
+ }
+
+ } /* end of grow by write */
+
+
+ /*
+ * Woffset - holds start of grow (start of write expect in grow by lseek)
+ * Grow_incr - holds size of grow (write).
+ * fsize - holds size of file before write
+ */
+ size_grew=(Woffset + Grow_incr) - fsize;
+ if ( Debug > 1) {
+ if ( Mode & MODE_FIFO ) {
+ printf("%s: %d DEBUG2 %s/%d: file is fifo, %d wrote %d bytes\n",
+ Progname, Pid, __FILE__, __LINE__, Grow_incr, Iter_cnt);
+ }
+
+ else if ( size_grew > 0 )
+ printf("%s: %d DEBUG2 %s/%d: %d wrote %d bytes(off:%d), grew file by %d bytes\n",
+ Progname, Pid, __FILE__, __LINE__, Iter_cnt, Grow_incr, Woffset, size_grew);
+ else
+ printf("%s: %d DEBUG2 %s/%d: %d wrote %d bytes(off:%d), did not grow file\n",
+ Progname, Pid, __FILE__, __LINE__, Iter_cnt, Grow_incr, Woffset);
+ }
+
+ bytes_consumed += size_grew;
+ return 0;
+
+} /* end of growfile */
+
+/***********************************************************************
+ * shrinkfile file by trunc_incr. file can not be made smaller than
+ * size zero. Therefore, if trunc_incr is larger than file size,
+ * file will be truncated to zero.
+ * The file descriptor current offset is assumed to be the end of the
+ * file.
+ *
+ ***********************************************************************/
+int
+shrinkfile(fd, filename, trunc_incr, trunc_inter, just_trunc)
+int fd;
+char *filename;
+int trunc_incr;
+int trunc_inter; /* interval */
+int just_trunc; /* lseek has already been done for you */
+{
+ static int shrink_cnt = 0;
+ int cur_offset;
+ int new_offset;
+ int ret;
+#ifdef CRAY
+ int offset;
+#endif
+
+ shrink_cnt++;
+
+ if ( trunc_inter == 0 || (shrink_cnt % trunc_inter != 0)) {
+ if ( Debug > 3 )
+ printf("%s: %d DEBUG4 %s/%d: Not shrinking file - not time, iter=%d, cnt=%d\n",
+ Progname, Pid, __FILE__, __LINE__, trunc_inter, shrink_cnt);
+ return 0; /* not this time */
+ }
+
+ if ( Mode & MODE_FIFO ) {
+ if ( Debug > 5 )
+ printf("%s: %d DEBUG5 %s/%d: Not attempting to shrink a FIFO\n",
+ Progname, Pid, __FILE__, __LINE__);
+ return 0; /* can not truncate fifo */
+ }
+
+ lkfile(fd, LOCK_EX, LKLVL0);
+
+ if ((cur_offset=lseek(fd,0,SEEK_CUR)) == -1 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: tell(%d) failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, fd, strerror(errno));
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return -1;
+ }
+
+ if ( Mode & MODE_RAND_LSEEK ) {
+ if ( max_lseek <= -1 ) {
+ if ( (new_offset=file_size(fd)) == -1 ) {
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return -1;
+ }
+
+ if ( new_offset < min_lseek )
+ new_offset=min_lseek;
+ else
+ new_offset=random_range(min_lseek, new_offset, 1, NULL);
+ }
+ else {
+ new_offset=random_range(min_lseek, max_lseek, 1, NULL);
+ }
+
+#ifdef CRAY
+ if ((offset=lseek(fd, new_offset, SEEK_SET)) == -1 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: lseek(%d, %d, SEEK_SET) l3 failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, fd, new_offset, strerror(errno));
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return -1;
+ }
+ else if ( Debug > 3 )
+ printf("%s: %d DEBUG4 %s/%d: lseeked to random offset %d\n",
+ Progname, Pid, __FILE__, __LINE__, offset);
+
+#endif
+ }
+
+ else { /* remove trunc_incr from file */
+
+ new_offset = cur_offset-trunc_incr;
+
+ if ( new_offset < 0 )
+ new_offset=0;
+
+#ifdef CRAY
+ if ( lseek(fd, new_offset, SEEK_SET) == -1 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: lseek(fd, %d, SEEK_SET) l4 failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, new_offset, strerror(errno));
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return -1;
+ }
+ else if ( Debug > 3 )
+ printf("%s: %d DEBUG4 %s/%d: lseeked to offset %d, %d bytes from end\n",
+ Progname, Pid, __FILE__, __LINE__, new_offset, trunc_incr);
+#endif
+ }
+
+
+#ifdef CRAY
+ ret=trunc(fd);
+#else
+ ret=ftruncate(fd, new_offset );
+ if( (ret == 0) && (Debug > 3) ){
+ printf("%s: %d DEBUG4 %s/%d: ftruncated to offset %d, %d bytes from end\n",
+ Progname, Pid, __FILE__, __LINE__, new_offset, trunc_incr);
+ }
+#endif
+
+ lkfile(fd, LOCK_UN, LKLVL0);
+
+ if ( ret == -1 ) {
+#ifdef CRAY
+ fprintf(stderr, "%s%s: %d %s/%d: trunc failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, strerror(errno));
+#else
+ fprintf(stderr, "%s%s: %d %s/%d: ftruncate failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, strerror(errno));
+#endif
+ return -1;
+ }
+
+ if ( Debug > 2 ) {
+ printf("%s: %d DEBUG2 %s/%d: trunc file by %d bytes, to size of = %d bytes\n",
+ Progname, Pid, __FILE__, __LINE__, cur_offset-new_offset, new_offset);
+ }
+
+
+ bytes_consumed -= (cur_offset - new_offset);
+ return 0;
+
+} /* end of shrinkfile */
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+check_write(fd, cf_inter, filename, mode)
+int fd;
+int cf_inter; /* check file interval */
+char *filename; /* needed for error messages */
+int mode; /* write mode */
+{
+ int fsize;
+ static int cf_count = 0;
+ int ret = 0;
+ int tmp;
+ char *errmsg;
+ char *ptr;
+
+ cf_count++;
+
+ if ( cf_inter == 0 || (cf_count % cf_inter != 0)) {
+ if ( Debug > 4 )
+ printf("%s: %d DEBUG5 %s/%d: no write check, not time iter=%d, cnt=%d\n",
+ Progname, Pid, __FILE__, __LINE__, cf_inter, cf_count);
+ return 0; /* no check done */
+ }
+
+ if ( Grow_incr <= 0 ) {
+ if ( Debug > 3 )
+ printf("%s: %d DEBUG4 %s/%d: No write validation, Grow_incr = %d, offset = %d\n",
+ Progname, Pid, __FILE__, __LINE__, Grow_incr, Woffset);
+ return 0; /* no check */
+ }
+
+
+
+ /*
+ * Get the shared file lock. We need to hold the lock from before
+ * we do the stat until after the read.
+ */
+ lkfile(fd, LOCK_SH, LKLVL0);
+
+ if ((fsize=file_size(fd)) == -1 ) {
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return -1;
+
+ } else if ( fsize <= Woffset ) {
+ /*
+ * The file was truncated between write and now.
+ * The contents of our last write is totally gone, no check.
+ */
+ if ( Debug > 1 )
+ printf("%s%s: %d DEBUG2 %s/%d: %d File size (%d) smaller than where last wrote (%d)- no write validation\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, fsize, Woffset);
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return 0; /* no validation, but not an error */
+
+ } else if ( fsize < (Woffset + Grow_incr)) {
+ /*
+ * The file was truncated between write and now.
+ * Part of our last write has been truncated, adjust our Grow_incr
+ * to reflect this.
+ */
+
+ tmp=Grow_incr;
+ Grow_incr=fsize-Woffset;
+
+ if ( Debug > 1 ) {
+
+ printf("%s%s: %d DEBUG2 %s/%d: %d fsz:%d, lost(%d)of wrt(off:%d, sz:%d), adj=%d\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, fsize, tmp-Grow_incr, Woffset, tmp, Grow_incr);
+ }
+
+ }
+
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: about to do write validation, offset = %d, size = %d\n",
+ Progname, Pid, __FILE__, __LINE__, Woffset, Grow_incr);
+
+ if ( ! (mode & MODE_FIFO) ) {
+
+ if ( lseek(fd, Woffset, 0) == -1 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: lseek(fd, %d, 0) failed: %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Woffset, strerror(errno));
+ }
+ if ( Debug > 3 )
+ printf("%s: %d DEBUG4 %s/%d: lseeked to offset:%d\n",
+ Progname, Pid, __FILE__, __LINE__, Woffset);
+ }
+
+ /*
+ * Read last writes data
+ */
+#if NEWIO
+ ret=lio_read_buffer(fd, io_type, Buffer, Grow_incr, SIGUSR1, &errmsg,0);
+#else
+ ret=read_buffer(fd, io_type, Buffer, Grow_incr, 0, &errmsg);
+#endif
+
+ /*
+ * report the error and debug information before releasing
+ * the file lock
+ */
+ if ( ret != Grow_incr ) {
+ fprintf(stderr, "%s%s: %d %s/%d: %d CW %s\n", Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+ {
+ struct stat stbuf;
+ fstat(fd, &stbuf);
+ if ( Debug > 2 )
+ printf("%s%s: %d DEBUG3 %s/%d: fd:%d, offset:%d, fsize:%d, openflags:%#o\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, fd,
+ lseek(fd,SEEK_CUR,0),
+ (int)stbuf.st_size,
+ Fileinfo.openflags);
+ }
+
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return 1;
+ }
+
+
+ lkfile(fd, LOCK_UN, LKLVL0);
+
+ if ( Mode & MODE_GROW_BY_LSEEK ) {
+ /* check that all zeros upto last character */
+ for(ptr=Buffer; ptr < (Buffer+Grow_incr-1); ptr++) {
+ if ( *ptr != '\0' ) {
+ fprintf(stderr,
+ "%s%s: %d %s/%d: data mismatch at offset %d, exp:%#o(zerofilled), act:%#o in file %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__,
+ (int)(Woffset+(Grow_incr-(Buffer-ptr))),
+ 0, *ptr, filename);
+ fflush(stderr);
+ return 1;
+ }
+ }
+ /* check that the last char is a 'w' */
+ if ( *ptr != 'w' ) {
+ fprintf(stderr,
+ "%s%s: %d %s/%d: data mismatch at offset %d, exp:%#o(zerofilled), act:%#o in file %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__,
+ (int)(Woffset+(Grow_incr-(Buffer-ptr))), 'w',
+ *ptr, filename);
+ fflush(stderr);
+ return 1;
+ }
+ return 0; /* all is well */
+
+ }
+ else if ( Pattern == PATTERN_OFFSET )
+ ret=datapidchk(STATIC_NUM, Buffer, Grow_incr, Woffset, &errmsg);
+ else if ( Pattern == PATTERN_PID )
+ ret=datapidchk(Pid, Buffer, Grow_incr, Woffset, &errmsg);
+ else if ( Pattern == PATTERN_ASCII )
+ ret=dataasciichk(NULL, Buffer, Grow_incr, Woffset, &errmsg);
+ else if ( Pattern == PATTERN_RANDOM )
+ ; /* no check for random */
+ else if ( Pattern == PATTERN_ALT )
+ ret=databinchk('a', Buffer, Grow_incr, Woffset, &errmsg);
+ else if ( Pattern == PATTERN_CHKER )
+ ret=databinchk('c', Buffer, Grow_incr, Woffset, &errmsg);
+ else if ( Pattern == PATTERN_CNTING )
+ ret=databinchk('C', Buffer, Grow_incr, Woffset, &errmsg);
+ else if ( Pattern == PATTERN_ZEROS )
+ ret=databinchk('z', Buffer, Grow_incr, Woffset, &errmsg);
+ else if ( Pattern == PATTERN_ONES )
+ ret=databinchk('o', Buffer, Grow_incr, Woffset, &errmsg);
+ else
+ ret=dataasciichk(NULL, Buffer, Grow_incr, Woffset, &errmsg);
+
+ if ( ret >= 0 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: %d CW %s in file %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg, filename);
+
+ if ( Debug > 0 )
+ printf("%s%s: %d DEBUG1 %s/%d: **fd:%d, lk:%d, offset:%d, sz:%d open flags:%#o %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, fd, lockfile,
+ Woffset, Grow_incr, Fileinfo.openflags, openflags2symbols(Fileinfo.openflags, ",", NULL));
+
+ fflush(stderr);
+ return 1;
+ }
+
+ if ( Debug > 6 )
+ printf("%s: %d DEBUG7 %s/%d: No corruption detected on write validation , offset = %d, size = %d\n",
+ Progname, Pid, __FILE__, __LINE__, Woffset, Grow_incr);
+
+ return 0; /* all is well */
+}
+
+
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+check_file(fd, cf_inter, filename, no_file_check)
+int fd;
+int cf_inter; /* check file interval */
+char *filename; /* needed for error messages */
+int no_file_check; /* if set, do not do file content check */
+{
+ int fsize;
+ static int cf_count = 0;
+ char *buf;
+ int ret;
+ int ret_val = 0;
+ int rd_cnt;
+ int rd_size;
+ char *errmsg;
+
+ cf_count++;
+
+ if ( cf_inter == 0 || (cf_count % cf_inter != 0)) {
+ if ( Debug > 4 )
+ printf("%s: %d DEBUG5 %s/%d: No file check - not time, iter=%d, cnt=%d\n",
+ Progname, Pid, __FILE__, __LINE__, cf_inter, cf_count);
+ return 0; /* no check done */
+ }
+
+ /*
+ * if we can't determine file content, don't bother checking
+ */
+ if ( no_file_check ) {
+ if ( Debug > 4 )
+ printf("%s: %d DEBUG5 %s/%d: No file check, lseek grow or random lseeks\n",
+ Progname, Pid, __FILE__, __LINE__);
+ return 0;
+ }
+
+ /*
+ * Lock the file. We need to have the file lock before
+ * the stat and until after the last read to prevent
+ * a trunc/truncate from "corrupting" our data.
+ */
+ lkfile(fd, LOCK_SH, LKLVL0);
+
+ if ((fsize=file_size(fd)) == -1 ) {
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return -1;
+ }
+
+ if ( fsize == 0 ) {
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: No file validation, file size == 0\n",
+ Progname, Pid, __FILE__, __LINE__);
+
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return 0;
+ }
+
+ if ( Debug > 2 )
+ printf("%s: %d DEBUG3 %s/%d: about to do file validation\n",
+ Progname, Pid, __FILE__, __LINE__);
+
+ if ( fsize > MAX_FC_READ ) {
+ /*
+ * read the file in MAX_FC_READ chuncks.
+ */
+
+ if ((buf=(char *)malloc(MAX_FC_READ)) == NULL ) {
+ fprintf(stderr, "%s%s: %s/%d: malloc(%d) failed: %s\n", Progname, TagName,
+ __FILE__, __LINE__, MAX_FC_READ, strerror(errno));
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return -1;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ lkfile(fd, LOCK_SH, LKLVL0); /* get lock on file before getting file size */
+
+ rd_cnt=0;
+ while (rd_cnt < fsize ) {
+ if ( fsize - rd_cnt > MAX_FC_READ )
+ rd_size=MAX_FC_READ;
+ else
+ rd_size=fsize - rd_cnt;
+
+#if NEWIO
+ ret=lio_read_buffer(fd, io_type, buf, rd_size,
+ SIGUSR1, &errmsg,0);
+#else
+ ret=read_buffer(fd, io_type, buf, rd_size, 0, &errmsg);
+#endif
+
+ if (ret != rd_size ) {
+ fprintf(stderr, "%s%s: %d %s/%d: %d CFa %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+ free(buf);
+ lkfile(fd, LOCK_UN, LKLVL0);
+ return -1;
+ }
+/**
+ read(fd, buf, rd_size);
+***/
+
+ if ( Pattern == PATTERN_OFFSET )
+ ret=datapidchk(STATIC_NUM, buf, rd_size, rd_cnt, &errmsg);
+ else if ( Pattern == PATTERN_PID )
+ ret=datapidchk(Pid, buf, rd_size, rd_cnt, &errmsg);
+ else if ( Pattern == PATTERN_ASCII )
+ ret=dataasciichk(NULL, buf, rd_size, rd_cnt, &errmsg);
+ else if ( Pattern == PATTERN_RANDOM )
+ ; /* no checks for random */
+ else if ( Pattern == PATTERN_ALT )
+ ret=databinchk('a', buf, rd_size, rd_cnt, &errmsg);
+ else if ( Pattern == PATTERN_CHKER )
+ ret=databinchk('c', buf, rd_size, rd_cnt, &errmsg);
+ else if ( Pattern == PATTERN_CNTING )
+ ret=databinchk('C', buf, rd_size, rd_cnt, &errmsg);
+ else if ( Pattern == PATTERN_ZEROS )
+ ret=databinchk('z', buf, rd_size, rd_cnt, &errmsg);
+ else if ( Pattern == PATTERN_ONES )
+ ret=databinchk('o', buf, rd_size, rd_cnt, &errmsg);
+ else
+ ret=dataasciichk(NULL, buf, rd_size, rd_cnt, &errmsg);
+
+
+ if ( ret >= 0 ) {
+ fprintf(stderr,
+ "%s%s: %d %s/%d: %d CFp %s in file %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg, filename);
+ fflush(stderr);
+ ret_val=1;
+ lkfile(fd, LOCK_UN, LKLVL0);
+ break;
+ }
+ rd_cnt += rd_size;
+ }
+
+ lkfile(fd, LOCK_UN, LKLVL0);
+
+ free(buf);
+
+ }
+ else {
+ /*
+ * Read the whole file in a single read
+ */
+ if((buf=(char *)malloc(fsize)) == NULL ) {
+ fprintf(stderr, "%s%s: %s/%d: malloc(%d) failed: %s\n", Progname, TagName,
+ __FILE__, __LINE__, fsize, strerror(errno));
+ fflush(stderr);
+ return -1;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+/****
+ read(fd, buf, fsize);
+****/
+#if NEWIO
+ ret=lio_read_buffer(fd, io_type, buf, fsize, SIGUSR1, &errmsg,0);
+#else
+ ret=read_buffer(fd, io_type, buf, fsize, 0, &errmsg);
+#endif
+
+ /* unlock the file as soon as we can */
+ lkfile(fd, LOCK_UN, LKLVL0);
+
+
+ if ( ret != fsize ) {
+ fprintf(stderr, "%s%s: %d %s/%d: %d CFw %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg);
+ ret_val=1;
+ }
+ else {
+ if ( Pattern == PATTERN_OFFSET )
+ ret=datapidchk(STATIC_NUM, buf, fsize, 0, &errmsg);
+ else if ( Pattern == PATTERN_PID )
+ ret=datapidchk(Pid, buf, fsize, 0, &errmsg);
+ else if ( Pattern == PATTERN_ASCII )
+ ret=dataasciichk(NULL, buf, fsize, 0, &errmsg);
+ else if ( Pattern == PATTERN_RANDOM )
+ ; /* no check for random */
+ else if ( Pattern == PATTERN_ALT )
+ ret=databinchk('a', buf, fsize, 0, &errmsg);
+ else if ( Pattern == PATTERN_CHKER )
+ ret=databinchk('c', buf, fsize, 0, &errmsg);
+ else if ( Pattern == PATTERN_CNTING )
+ ret=databinchk('C', buf, fsize, 0, &errmsg);
+ else if ( Pattern == PATTERN_ZEROS )
+ ret=databinchk('z', buf, fsize, 0, &errmsg);
+ else if ( Pattern == PATTERN_ONES )
+ ret=databinchk('o', buf, fsize, 0, &errmsg);
+ else
+ ret=dataasciichk(NULL, buf, fsize, 0, &errmsg);
+
+ if ( ret >= 0 ) {
+ fprintf(stderr, "%s%s: %d %s/%d: %d CFw %s in file %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, Iter_cnt, errmsg, filename);
+ fflush(stderr);
+ ret_val=1;
+ }
+ }
+ free(buf);
+ }
+
+ return ret_val;
+
+} /* end of check_file */
+
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+file_size(int fd)
+{
+ struct stat sb;
+
+ if (fstat(fd, &sb) < 0) {
+ fprintf(stderr, "%s%s: %d %s/%d: Unable to fstat(%d, &buf), errno:%d %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, fd, errno, strerror(errno));
+ return -1;
+
+ }
+
+ return sb.st_size;
+}
+
+/***********************************************************************
+ * do file lock/unlock action.
+ ***********************************************************************/
+int
+lkfile(int fd, int operation, int lklevel)
+{
+ char *errmsg;
+
+
+ if ( lockfile == lklevel) {
+
+ if ( Debug > 5 ) {
+ switch (operation) {
+ case LOCK_UN:
+ printf("%s: %d DEBUG6 %s/%d: Attempting to release lock on fd %d\n",
+ Progname, Pid, __FILE__, __LINE__, fd);
+ break;
+
+ case LOCK_SH:
+ printf("%s: %d DEBUG6 %s/%d: Attempting to get read/shared lock on fd %d\n",
+ Progname, Pid, __FILE__, __LINE__, fd);
+ break;
+
+ case LOCK_EX:
+ printf("%s: %d DEBUG6 %s/%d: Attempting to get write/exclusive lock on fd %d\n",
+ Progname, Pid, __FILE__, __LINE__, fd);
+ break;
+ }
+ }
+
+ /*
+ * Attempt to get/release desired lock.
+ * file_lock will attempt to do action over and over again until
+ * either an unretryable error or the action is completed.
+ */
+
+ if ( file_lock(fd, operation, &errmsg) != 0 ) {
+ printf("%s%s: %d %s/%d: Unable to perform lock operation. %s\n",
+ Progname, TagName, Pid, __FILE__, __LINE__, errmsg);
+
+ /* do we count this as an error? handle_error(); */
+ return -1;
+ }
+
+ if ( Debug > 2 ) {
+ switch (operation) {
+ case LOCK_UN:
+ printf("%s: %d DEBUG3 %s/%d: Released lock on fd %d\n",
+ Progname, Pid, __FILE__, __LINE__, fd);
+ break;
+
+ case LOCK_SH:
+ printf("%s: %d DEBUG3 %s/%d: Got read/shared lock on fd %d\n",
+ Progname, Pid, __FILE__, __LINE__, fd);
+ break;
+
+ case LOCK_EX:
+ printf("%s: %d DEBUG3 %s/%d: Got write/exclusive lock on fd %d\n",
+ Progname, Pid, __FILE__, __LINE__, fd);
+ break;
+
+ default:
+ printf("%s: %d DEBUG3 %s/%d: Completed action %d on fd %d\n",
+ Progname, Pid, __FILE__, __LINE__, operation, fd);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#ifndef linux
+/***********************************************************************
+ *
+ ***********************************************************************/
+int
+pre_alloc(fd, size)
+int fd;
+int size;
+{
+
+#ifdef CRAY
+ long avl;
+
+ if ( ialloc(fd, size, IA_CONT, &avl) == -1 ) {
+ fprintf(stderr, "%s%s %s/%d: Unable to pre-alloc space: ialloc failed: %d %s\n",
+ Progname, TagName,
+ __FILE__, __LINE__, errno, strerror(errno));
+ return -1;
+ }
+#endif
+
+#ifdef sgi
+ struct flock f;
+
+ f.l_whence = 0;
+ f.l_start = 0;
+ f.l_len = size;
+
+ /* non-zeroing reservation */
+ if( fcntl( fd, F_RESVSP, &f ) == -1 ){
+ fprintf(stderr, "%s%s %s/%d: Unable to pre-alloc space: fcntl(F_RESVSP) failed: %d %s\n",
+ Progname, TagName,
+ __FILE__, __LINE__, errno, strerror(errno));
+ return -1;
+ }
+
+#endif
+
+ return 0;
+}
+#endif
diff --git a/doio/iogen.c b/doio/iogen.c
new file mode 100644
index 0000000..08c23e9
--- /dev/null
+++ b/doio/iogen.c
@@ -0,0 +1,1983 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ * iogen - a tool for generating file/sds io for a doio process
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#ifdef CRAY
+#include <sys/file.h>
+#include <sys/iosw.h>
+#include <sys/listio.h>
+#endif
+#ifdef sgi
+#include <sys/statvfs.h>
+#include <sys/fs/xfs_itable.h>
+#endif
+
+#ifdef CRAY
+#include "libkern.h"
+#endif
+#include "doio.h"
+#include "str_to_bytes.h"
+#include "string_to_tokens.h"
+#include "open_flags.h"
+#include "random_range.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 512 /* ??? */
+#endif
+
+#ifndef BSIZE
+#ifdef linux
+#define BSIZE DEV_BSIZE
+#else
+#define BSIZE 512
+#endif
+#endif
+
+#define RAW_IO(_flags_) ((_flags_) & (O_RAW | O_SSD))
+
+#ifndef linux
+extern char *sys_errlist[];
+#endif
+#define SYSERR sys_errlist[errno]
+
+/*
+ * Structure for retaining test file information
+ */
+
+struct file_info {
+ char f_path[MAX_FNAME_LENGTH+1]; /* file name (full path) */
+ int f_length; /* length in bytes */
+ int f_iou; /* file iounit */
+ int f_riou; /* file raw iounit (for O_RAW/O_SSD) */
+ int f_dalign; /* direct I/O alignment */
+ int f_nextoff; /* offset of end of last io operation */
+ int f_type; /* file type S_IFREG, etc... */
+ int f_lastoffset; /* offset of last io operation */
+ int f_lastlength; /* length of last io operation */
+};
+
+/*
+ * Simple structure for associating strings with values - useful for converting
+ * cmdline args to internal values, as well as printing internal values in
+ * a human readable form.
+ */
+
+struct strmap {
+ char *m_string;
+ int m_value;
+ int m_flags;
+};
+
+/*
+ * Declare cmdline option flags/variables initialized in parse_cmdline()
+ */
+
+#define OPTS "a:dhf:i:L:m:op:qr:s:t:T:O:N:"
+
+int a_opt = 0; /* async io comp. types supplied */
+int o_opt = 0; /* form overlapping requests */
+int f_opt = 0; /* test flags */
+int i_opt = 0; /* iterations - 0 implies infinite */
+int L_opt = 0; /* listio min-max nstrides & nents */
+int m_opt = 0; /* offset mode */
+int O_opt = 0; /* file creation Open flags */
+int p_opt = 0; /* output pipe - default is stdout */
+int r_opt = 0; /* specify raw io multiple instead of */
+ /* getting it from the mounted on device. */
+ /* Only applies to regular files. */
+int s_opt = 0; /* syscalls */
+int t_opt = 0; /* min transfer size (bytes) */
+int T_opt = 0; /* max transfer size (bytes) */
+int q_opt = 0; /* quiet operation on startup */
+char TagName[40]; /* name of this iogen (see Monster) */
+struct strmap *Offset_Mode; /* M_SEQUENTIAL, M_RANDOM, etc. */
+int Iterations; /* # requests to generate (0 --> infinite) */
+int Time_Mode = 0; /* non-zero if Iterations is in seconds */
+ /* (ie. -i arg was suffixed with 's') */
+char *Outpipe; /* Pipe to write output to if p_opt */
+int Mintrans; /* min io transfer size */
+int Maxtrans; /* max io transfer size */
+int Rawmult; /* raw/ssd io multiple (from -r) */
+int Minstrides; /* min # of listio strides per request */
+int Maxstrides; /* max # of listio strides per request */
+int Oflags; /* open(2) flags for creating files */
+int Ocbits; /* open(2) cbits for creating files */
+int Ocblks; /* open(2) cblks for creating files */
+int Orealtime=0; /* flag set for -O REALTIME */
+int Oextsize=0; /* real-time extent size */
+int Oreserve=1; /* flag for -O [no]reserve */
+int Oallocate=0; /* flag for -O allocate */
+int Owrite=1; /* flag for -O nowrite */
+
+int Nfiles = 0; /* # files on cmdline */
+struct file_info *File_List; /* info about each file */
+int Nflags = 0; /* # flags on cmdline */
+struct strmap *Flag_List[128]; /* flags selected from cmdline */
+int Nsyscalls = 0; /* # syscalls on cmdline */
+struct strmap *Syscall_List[128]; /* syscalls selected on cmdline */
+int Fileio = 0; /* flag indicating that a file */
+ /* io syscall has been chosen. */
+int Naio_Strat_Types = 0; /* # async io completion types */
+struct strmap *Aio_Strat_List[128]; /* Async io completion types */
+
+void startup_info();
+
+/*
+ * Map async io completion modes (-a args) names to values. Macros are
+ * defined in doio.h.
+ */
+
+struct strmap Aio_Strat_Map[] = {
+#ifndef linux
+ { "poll", A_POLL },
+ { "signal", A_SIGNAL },
+#else
+ { "none", 0 },
+#endif /* !linux */
+#ifdef CRAY
+#if _UMK || RELEASE_LEVEL >= 8000
+ { "recall", A_RECALL },
+#endif
+
+#ifdef RECALL_SIZEOF
+ { "recalla", A_RECALLA },
+#endif
+ { "recalls", A_RECALLS },
+#endif /* CRAY */
+
+#ifdef sgi
+ { "suspend", A_SUSPEND },
+ { "callback", A_CALLBACK },
+#endif
+ { NULL, -1 }
+};
+
+/*
+ * Offset_Mode #defines
+ */
+
+#define M_RANDOM 1
+#define M_SEQUENTIAL 2
+#define M_REVERSE 3
+
+/*
+ * Map offset mode (-m args) names to values
+ */
+
+struct strmap Omode_Map[] = {
+ { "random", M_RANDOM },
+ { "sequential", M_SEQUENTIAL },
+ { "reverse", M_REVERSE },
+ { NULL, -1 }
+};
+
+/*
+ * Map syscall names (-s args) to values - macros are defined in doio.h.
+ */
+#define SY_ASYNC 00001
+#define SY_WRITE 00002
+#define SY_SDS 00010
+#define SY_LISTIO 00020
+#define SY_NENT 00100 /* multi entry vs multi stride >>> */
+
+struct strmap Syscall_Map[] = {
+ { "read", READ, 0 },
+ { "write", WRITE, SY_WRITE },
+#ifdef CRAY
+ { "reada", READA, SY_ASYNC },
+ { "writea", WRITEA, SY_WRITE|SY_ASYNC },
+#ifndef _CRAYMPP
+ { "ssread", SSREAD, SY_SDS },
+ { "sswrite", SSWRITE, SY_WRITE|SY_SDS },
+#endif
+ { "listio", LISTIO, SY_ASYNC },
+
+ /* listio as 4 system calls */
+ { "lread", LREAD, 0 },
+ { "lreada", LREADA, SY_ASYNC },
+ { "lwrite", LWRITE, SY_WRITE },
+ { "lwritea", LWRITEA, SY_WRITE|SY_ASYNC },
+
+ /* listio with nstrides > 1 */
+ { "lsread", LSREAD, 0 },
+ { "lsreada", LSREADA, SY_ASYNC },
+ { "lswrite", LSWRITE, SY_WRITE },
+ { "lswritea", LSWRITEA, SY_WRITE|SY_ASYNC },
+
+ /* listio with nents > 1 */
+ { "leread", LEREAD, 0|SY_NENT },
+ { "lereada", LEREADA, SY_ASYNC|SY_NENT },
+ { "lewrite", LEWRITE, SY_WRITE|SY_NENT },
+ { "lewritea", LEWRITEA, SY_WRITE|SY_ASYNC|SY_NENT },
+
+ /* listio with nents > 1 & nstrides > 1 */
+
+ /* all listio system calls under one name */
+ { "listio+", LREAD, 0 },
+ { "listio+", LREADA, SY_ASYNC },
+ { "listio+", LWRITE, SY_WRITE },
+ { "listio+", LWRITEA, SY_WRITE|SY_ASYNC },
+ { "listio+", LSREAD, 0 },
+ { "listio+", LSREADA, SY_ASYNC },
+ { "listio+", LSWRITE, SY_WRITE },
+ { "listio+", LSWRITEA, SY_WRITE|SY_ASYNC },
+ { "listio+", LEREAD, 0|SY_NENT },
+ { "listio+", LEREADA, SY_ASYNC|SY_NENT },
+ { "listio+", LEWRITE, SY_WRITE|SY_NENT },
+ { "listio+", LEWRITEA, SY_WRITE|SY_ASYNC|SY_NENT },
+#endif
+
+#ifdef sgi
+ { "pread", PREAD },
+ { "pwrite", PWRITE, SY_WRITE },
+ { "aread", AREAD, SY_ASYNC },
+ { "awrite", AWRITE, SY_WRITE|SY_ASYNC },
+#if 0
+ /* not written yet */
+ { "llread", LLREAD, 0 },
+ { "llaread", LLAREAD, SY_ASYNC },
+ { "llwrite", LLWRITE, 0 },
+ { "llawrite", LLAWRITE, SY_ASYNC },
+#endif
+ { "resvsp", RESVSP, SY_WRITE },
+ { "unresvsp", UNRESVSP, SY_WRITE },
+ { "reserve", RESVSP, SY_WRITE },
+ { "unreserve", UNRESVSP, SY_WRITE },
+ { "ffsync", DFFSYNC, SY_WRITE },
+#endif /* SGI */
+
+#ifndef CRAY
+ { "readv", READV },
+ { "writev", WRITEV, SY_WRITE },
+ { "mmread", MMAPR },
+ { "mmwrite", MMAPW, SY_WRITE },
+ { "fsync2", FSYNC2, SY_WRITE },
+ { "fdatasync", FDATASYNC, SY_WRITE },
+#endif
+
+ { NULL, -1 }
+};
+
+/*
+ * Map open flags (-f args) to values
+ */
+#define FLG_RAW 00001
+
+struct strmap Flag_Map[] = {
+ { "buffered", 0, 0 },
+ { "sync", O_SYNC, 0 },
+#ifdef CRAY
+ { "raw", O_RAW, FLG_RAW },
+ { "raw+wf", O_RAW | O_WELLFORMED, FLG_RAW },
+ { "raw+wf+ldraw", O_RAW | O_WELLFORMED | O_LDRAW, FLG_RAW },
+ { "raw+wf+ldraw+sync", O_RAW | O_WELLFORMED | O_LDRAW | O_SYNC, FLG_RAW },
+#ifdef O_SSD
+ { "ssd", O_SSD, FLG_RAW },
+#endif
+#ifdef O_LDRAW
+ { "ldraw", O_LDRAW, 0 },
+#endif
+#ifdef O_PARALLEL
+ { "parallel", O_PARALLEL | O_RAW | O_WELLFORMED,
+ FLG_RAW },
+ { "parallel+sync", O_PARALLEL | O_RAW | O_WELLFORMED | O_SYNC,
+ FLG_RAW },
+ { "parallel+ldraw", O_PARALLEL | O_RAW | O_WELLFORMED | O_LDRAW,
+ FLG_RAW },
+ { "parallel+ldraw+sync",
+ O_PARALLEL | O_RAW | O_WELLFORMED | O_LDRAW | O_SYNC,
+ FLG_RAW },
+#endif
+#endif /* CRAY */
+
+#ifdef sgi
+ { "direct", O_DIRECT, FLG_RAW },
+ { "dsync", O_DSYNC }, /* affects writes */
+ { "rsync", O_RSYNC }, /* affects reads */
+ { "rsync+dsync", O_RSYNC|O_DSYNC },
+#endif
+ { NULL, -1 }
+};
+
+/*
+ * Map file types to strings
+ */
+
+struct strmap Ftype_Map[] = {
+ { "regular", S_IFREG },
+ { "blk-spec", S_IFBLK },
+ { "chr-spec", S_IFCHR },
+ { NULL, 0 }
+};
+
+/*
+ * Misc declarations
+ */
+
+int Sds_Avail;
+
+char Byte_Patterns[26] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z' };
+
+
+int form_iorequest(struct io_req *);
+int init_output();
+int parse_cmdline(int argc, char **argv, char *opts);
+int help(FILE *stream);
+int usage(FILE *stream);
+
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ int rseed, outfd, infinite;
+ time_t start_time;
+ struct io_req req;
+
+ umask(0);
+
+#ifdef CRAY
+ Sds_Avail = sysconf(_SC_CRAY_SDS);
+#else
+ Sds_Avail = 0;
+#endif
+
+ TagName[0] = '\0';
+ parse_cmdline(argc, argv, OPTS);
+
+ /*
+ * Initialize output descriptor.
+ */
+ if (! p_opt) {
+ outfd = 1;
+ } else {
+ outfd = init_output();
+ }
+
+ rseed = getpid();
+ random_range_seed(rseed); /* initialize random number generator */
+
+ /*
+ * Print out startup information, unless we're running in quiet mode
+ */
+ if (!q_opt)
+ startup_info(stderr, rseed);
+
+ start_time = time(0);
+
+ /*
+ * While iterations (or forever if Iterations == 0) - compute an
+ * io request, and write the structure to the output descriptor
+ */
+
+ infinite = !Iterations;
+
+ while (infinite ||
+ (! Time_Mode && Iterations--) ||
+ (Time_Mode && time(0) - start_time <= Iterations)) {
+
+ memset(&req, 0, sizeof(struct io_req));
+ if (form_iorequest(&req) == -1) {
+ fprintf(stderr, "iogen%s: form_iorequest() failed\n", TagName);
+ continue;
+ }
+
+ req.r_magic = DOIO_MAGIC;
+ write(outfd, (char *)&req, sizeof(req));
+ }
+
+ exit(0);
+
+} /* main */
+
+void
+startup_info(FILE *stream, int seed)
+{
+ char *value_to_string(), *type;
+ int i;
+
+ fprintf(stream, "\n");
+ fprintf(stream, "iogen%s starting up with the following:\n", TagName);
+ fprintf(stream, "\n");
+
+ fprintf(stream, "Out-pipe: %s\n",
+ p_opt ? Outpipe : "stdout");
+
+ if (Iterations) {
+ fprintf(stream, "Iterations: %d", Iterations);
+ if (Time_Mode)
+ fprintf(stream, " seconds");
+
+ fprintf(stream, "\n");
+ } else {
+ fprintf(stream, "Iterations: Infinite\n");
+ }
+
+ fprintf(stream,
+ "Seed: %d\n", seed);
+
+ fprintf(stream,
+ "Offset-Mode: %s\n", Offset_Mode->m_string);
+
+ fprintf(stream, "Overlap Flag: %s\n",
+ o_opt ? "on" : "off");
+
+ fprintf(stream,
+ "Mintrans: %-11d (%d blocks)\n",
+ Mintrans, (Mintrans+BSIZE-1)/BSIZE);
+
+ fprintf(stream,
+ "Maxtrans: %-11d (%d blocks)\n",
+ Maxtrans, (Maxtrans+BSIZE-1)/BSIZE);
+
+ if (! r_opt)
+ fprintf(stream,
+ "O_RAW/O_SSD Multiple: (Determined by device)\n");
+ else
+ fprintf(stream,
+ "O_RAW/O_SSD Multiple: %-11d (%d blocks)\n",
+ Rawmult, (Rawmult+BSIZE-1)/BSIZE);
+
+ fprintf(stream, "Syscalls: ");
+ for (i = 0; i < Nsyscalls; i++)
+ fprintf(stream,
+ "%s ", Syscall_List[i]->m_string);
+ fprintf(stream, "\n");
+
+ fprintf(stream, "Aio completion types: ");
+ for (i = 0; i < Naio_Strat_Types; i++)
+ fprintf(stream,
+ "%s ", Aio_Strat_List[i]->m_string);
+ fprintf(stream, "\n");
+
+ if (Fileio) {
+ fprintf(stream, "Flags: ");
+ for (i = 0; i < Nflags; i++)
+ fprintf(stream,
+ "%s ", Flag_List[i]->m_string);
+
+ fprintf(stream, "\n");
+ fprintf(stream, "\n");
+ fprintf(stream, "Test Files: \n");
+ fprintf(stream, "\n");
+ fprintf(stream,
+ "Path Length iou raw iou file\n");
+ fprintf(stream,
+ " (bytes) (bytes) (bytes) type\n");
+ fprintf(stream,
+ "-----------------------------------------------------------------------------\n");
+
+ for (i = 0; i < Nfiles; i++) {
+ type = value_to_string(Ftype_Map, File_List[i].f_type);
+ fprintf(stream, "%-40s %12d %7d %7d %s\n",
+ File_List[i].f_path, File_List[i].f_length,
+ File_List[i].f_iou, File_List[i].f_riou, type);
+ }
+ }
+}
+
+/*
+ * Initialize output descriptor. If going to stdout, its easy,
+ * otherwise, attempt to create a FIFO on path Outpipe. Exit with an
+ * error code if this cannot be done.
+ */
+int
+init_output()
+{
+ int outfd;
+ struct stat sbuf;
+
+ if (stat(Outpipe, &sbuf) == -1) {
+ if (errno == ENOENT) {
+ if (mkfifo(Outpipe, 0666) == -1) {
+ fprintf(stderr, "iogen%s: Could not mkfifo %s: %s\n",
+ TagName, Outpipe, SYSERR);
+ exit(2);
+ }
+ } else {
+ fprintf(stderr, "iogen%s: Could not stat outpipe %s: %s\n",
+ TagName, Outpipe, SYSERR);
+ exit(2);
+ }
+ } else {
+ if (! S_ISFIFO(sbuf.st_mode)) {
+ fprintf(stderr,
+ "iogen%s: Output file %s exists, but is not a FIFO\n",
+ TagName, Outpipe);
+ exit(2);
+ }
+ }
+
+ if ((outfd = open(Outpipe, O_RDWR)) == -1) {
+ fprintf(stderr,
+ "iogen%s: Couldn't open outpipe %s with flags O_RDWR: %s\n",
+ TagName, Outpipe, SYSERR);
+ exit(2);
+ }
+
+ return(outfd);
+}
+
+
+/*
+ * Main io generation function. form_iorequest() selects a system call to
+ * do based on cmdline arguments, and proceeds to select parameters for that
+ * system call.
+ *
+ * Returns 0 if req is filled in with a complete doio request, otherwise
+ * returns -1.
+ */
+
+int
+form_iorequest(req)
+struct io_req *req;
+{
+ int mult, offset, length, slength;
+ int minlength, maxlength, laststart, lastend;
+ int minoffset, maxoffset;
+ int maxstride, nstrides;
+ char pattern, *errp;
+ struct strmap *flags, *sc, *aio_strat;
+ struct file_info *fptr;
+#ifdef CRAY
+ int opcode, cmd;
+#endif
+
+ /*
+ * Choose system call, flags, and file
+ */
+
+ sc = Syscall_List[random_range(0, Nsyscalls-1, 1, NULL)];
+ req->r_type = sc->m_value;
+
+#ifdef CRAY
+ if (sc->m_value == LISTIO ) {
+ opcode = random_range(0, 1, 1, NULL) ? LO_READ : LO_WRITE;
+ cmd = random_range(0, 1, 1, NULL) ? LC_START : LC_WAIT;
+ }
+#endif
+
+ if( sc->m_flags & SY_WRITE )
+ pattern = Byte_Patterns[random_range(0, sizeof(Byte_Patterns) - 1, 1, NULL)];
+ else
+ pattern = 0;
+
+#if CRAY
+ /*
+ * If sds io, simply choose a length (possibly pattern) and return
+ */
+
+ if (sc->m_flags & SY_SDS ) {
+ req->r_data.ssread.r_nbytes = random_range(Mintrans, Maxtrans, BSIZE, NULL);
+ if (sc->m_flags & SY_WRITE)
+ req->r_data.sswrite.r_pattern = pattern;
+
+ return 0;
+ }
+#endif
+
+ /*
+ * otherwise, we're doing file io. Choose starting offset, length,
+ * open flags, and possibly a pattern (for write/writea).
+ */
+
+ fptr = &File_List[random_range(0, Nfiles-1, 1, NULL)];
+ flags = Flag_List[random_range(0, Nflags-1, 1, NULL)];
+
+ /*
+ * Choose offset/length multiple. IO going to a device, or regular
+ * IO that is O_RAW or O_SSD must be aligned on the file r_iou. Otherwise
+ * it must be aligned on the regular iou (normally 1).
+ */
+
+ if ( fptr->f_type == S_IFREG && (flags->m_flags & FLG_RAW) )
+ mult = fptr->f_riou;
+ else
+ mult = fptr->f_iou;
+
+ /*
+ * Choose offset and length. Both must be a multiple of mult
+ */
+
+ /*
+ * Choose length first - it must be a multiple of mult
+ */
+
+ laststart = fptr->f_lastoffset;
+ lastend = fptr->f_lastoffset + fptr->f_lastlength - 1;
+
+ minlength = (Mintrans > mult) ? Mintrans : mult;
+
+ switch (Offset_Mode->m_value) {
+ case M_SEQUENTIAL:
+ if (o_opt && lastend > laststart)
+ offset = random_range(laststart, lastend, 1, NULL);
+ else
+ offset = lastend + 1;
+ if (offset && (offset%mult))
+ offset += mult - (offset % mult);
+
+ if (minlength > fptr->f_length - offset)
+ offset = 0;
+
+ maxlength = fptr->f_length - offset;
+ if (maxlength > Maxtrans)
+ maxlength = Maxtrans;
+
+ length = random_range(minlength, maxlength, mult, &errp);
+ if (errp != NULL) {
+ fprintf(stderr, "iogen%s: random_range(%d, %d, %d) failed\n",
+ TagName, minlength, maxlength, mult);
+ return -1;
+ }
+
+ break;
+
+ case M_REVERSE:
+ maxlength = laststart;
+
+ if (maxlength > Maxtrans)
+ maxlength = Maxtrans;
+
+ if (minlength > maxlength) {
+ laststart = fptr->f_length;
+ lastend = fptr->f_length;
+ maxlength = Maxtrans;
+ }
+
+ length = random_range(minlength, maxlength, mult, &errp);
+ if (errp != NULL) {
+ fprintf(stderr, "iogen%s: random_range(%d, %d, %d) failed\n",
+ TagName, minlength, maxlength, mult);
+ return -1;
+ }
+
+ offset = laststart - length;
+
+ if (o_opt && lastend > laststart)
+ offset += random_range(1, lastend - laststart, 1, NULL);
+
+ if (offset && (offset%mult))
+ offset -= offset % mult;
+
+ break;
+
+ case M_RANDOM:
+ length = random_range(Mintrans, Maxtrans, mult, NULL);
+
+ if (o_opt && lastend > laststart) {
+ minoffset = laststart - length + 1;
+ if (minoffset < 0) {
+ minoffset = 0;
+ }
+
+ if (lastend + length > fptr->f_length) {
+ maxoffset = fptr->f_length - length;
+ } else {
+ maxoffset = lastend;
+ }
+ } else {
+ minoffset = 0;
+ maxoffset = fptr->f_length - length;
+ }
+
+ if (minoffset < 0)
+ minoffset = 0;
+
+ offset = random_range(minoffset, maxoffset, mult, &errp);
+ if (errp != NULL) {
+ fprintf(stderr, "iogen%s: random_range(%d, %d, %d) failed\n",
+ TagName, minoffset, maxoffset, mult);
+ return -1;
+ }
+ }
+
+ fptr->f_lastoffset = offset;
+ fptr->f_lastlength = length;
+
+ /*
+ * Choose an async io completion strategy if necessary
+ */
+ if( sc->m_flags & SY_ASYNC )
+ aio_strat = Aio_Strat_List[random_range(0, Naio_Strat_Types - 1,
+ 1, NULL)];
+ else
+ aio_strat = NULL;
+
+ /*
+ * fill in specific syscall record data
+ */
+ switch (sc->m_value) {
+ case READ:
+ case READA:
+ strcpy(req->r_data.read.r_file, fptr->f_path);
+ req->r_data.read.r_oflags = O_RDONLY | flags->m_value;
+ req->r_data.read.r_offset = offset;
+ req->r_data.read.r_nbytes = length;
+ req->r_data.read.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+ req->r_data.read.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+ req->r_data.read.r_nstrides = 1;
+ req->r_data.read.r_nent = 1;
+ break;
+
+ case WRITE:
+ case WRITEA:
+ strcpy(req->r_data.write.r_file, fptr->f_path);
+ req->r_data.write.r_oflags = O_WRONLY | flags->m_value;
+ req->r_data.write.r_offset = offset;
+ req->r_data.write.r_nbytes = length;
+ req->r_data.write.r_pattern = pattern;
+ req->r_data.write.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+ req->r_data.write.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+ req->r_data.write.r_nstrides = 1;
+ req->r_data.write.r_nent = 1;
+ break;
+
+ case READV:
+ case AREAD:
+ case PREAD:
+ case WRITEV:
+ case AWRITE:
+ case PWRITE:
+
+ case LREAD:
+ case LREADA:
+ case LWRITE:
+ case LWRITEA:
+
+ case RESVSP:
+ case UNRESVSP:
+ case DFFSYNC:
+ case FSYNC2:
+ case FDATASYNC:
+
+ strcpy(req->r_data.io.r_file, fptr->f_path);
+ req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_WRONLY : O_RDONLY) | flags->m_value;
+ req->r_data.io.r_offset = offset;
+ req->r_data.io.r_nbytes = length;
+ req->r_data.io.r_pattern = pattern;
+ req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+ req->r_data.io.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+ req->r_data.io.r_nstrides = 1;
+ req->r_data.io.r_nent = 1;
+ break;
+
+ case MMAPR:
+ case MMAPW:
+ strcpy(req->r_data.io.r_file, fptr->f_path);
+ /* a subtle "feature" of mmap: a write-map requires
+ the file open read/write */
+ req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_RDWR : O_RDONLY) | flags->m_value;
+ req->r_data.io.r_offset = offset;
+ req->r_data.io.r_nbytes = length;
+ req->r_data.io.r_pattern = pattern;
+ req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+ req->r_data.io.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+ req->r_data.io.r_nstrides = 1;
+ req->r_data.io.r_nent = 1;
+ break;
+
+ case LSREAD:
+ case LSREADA:
+ case LEREAD:
+ case LEREADA:
+ case LSWRITE:
+ case LSWRITEA:
+ case LEWRITE:
+ case LEWRITEA:
+ /* multi-strided */
+ strcpy(req->r_data.io.r_file, fptr->f_path);
+ req->r_data.io.r_oflags = ((sc->m_flags & SY_WRITE) ? O_WRONLY : O_RDONLY) | flags->m_value;
+ req->r_data.io.r_offset = offset;
+ req->r_data.io.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+ req->r_data.io.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+ req->r_data.io.r_pattern = pattern;
+
+ /* multi-strided request...
+ * random number of strides (1...MaxStrides)
+ * length of stride must be > minlength
+ * length of stride must be % mult
+ *
+ * maxstrides = min(length / mult, overall.max#strides)
+ * nstrides = random #
+ * while( length / nstrides < minlength )
+ * nstrides = new random #
+ */
+ maxstride = length / mult;
+ if(maxstride > Maxstrides)
+ maxstride = Maxstrides;
+
+ if(!Minstrides)
+ Minstrides=1;
+ nstrides = random_range(Minstrides, maxstride, 1, &errp);
+ if (errp != NULL) {
+ fprintf(stderr, "iogen%s: random_range(%d, %d, %d) failed\n",
+ TagName, Minstrides, maxstride, 1);
+ return -1;
+ }
+
+ slength = length / nstrides;
+ if(slength % mult != 0) {
+ if( mult > slength) {
+ slength = mult;
+ } else {
+ slength -= slength % mult;
+ }
+ nstrides = length / slength;
+ if(nstrides > Maxstrides)
+ nstrides = Maxstrides;
+ }
+
+ req->r_data.io.r_nbytes = slength;
+ if( sc->m_flags & SY_NENT ) {
+ req->r_data.io.r_nstrides = 1;
+ req->r_data.io.r_nent = nstrides;
+ } else {
+ req->r_data.io.r_nstrides = nstrides;
+ req->r_data.io.r_nent = 1;
+ }
+ break;
+
+ case LISTIO:
+#ifdef CRAY
+ strcpy(req->r_data.listio.r_file, fptr->f_path);
+ req->r_data.listio.r_offset = offset;
+ req->r_data.listio.r_cmd = cmd;
+ req->r_data.listio.r_aio_strat = (aio_strat==NULL) ? 0 : aio_strat->m_value;
+ req->r_data.listio.r_filestride = 0;
+ req->r_data.listio.r_memstride = 0;
+ req->r_data.listio.r_opcode = opcode;
+ req->r_data.listio.r_nstrides = 1;
+ req->r_data.listio.r_nbytes = length;
+ req->r_data.listio.r_uflags = (flags->m_flags & FLG_RAW) ? F_WORD_ALIGNED : 0;
+
+ if (opcode == LO_WRITE) {
+ req->r_data.listio.r_pattern = pattern;
+ req->r_data.listio.r_oflags = O_WRONLY | flags->m_value;
+ } else {
+ req->r_data.listio.r_oflags = O_RDONLY | flags->m_value;
+ }
+#endif
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Get information about a file that iogen uses to choose io length and
+ * offset. Information gathered is file length, iounit, and raw iounit.
+ * For regurlar files, iounit is 1, and raw iounit is the iounit of the
+ * device on which the file resides. For block/character special files
+ * the iounit and raw iounit are both the iounit of the device.
+ *
+ * Note: buffered and osync io must be iounit aligned
+ * raw and ossd io must be raw iounit aligned
+ */
+
+int
+get_file_info(rec)
+struct file_info *rec;
+{
+ struct stat sbuf;
+#ifdef CRAY
+ struct lk_device_info dinfo;
+#endif
+#ifdef sgi
+ int fd;
+ struct dioattr finfo;
+#endif
+
+ /*
+ * Figure out if the files is regular, block or character special. Any
+ * other type is an error.
+ */
+
+ if (stat(rec->f_path, &sbuf) == -1) {
+ fprintf(stderr, "iogen%s: get_file_info(): Could not stat() %s: %s\n",
+ TagName, rec->f_path, SYSERR);
+ return -1;
+ }
+
+#if _CRAY2
+ if ((! S_ISREG(sbuf.st_mode)) || strncmp(rec->f_path, "/dev/", 5) == 0) {
+ fprintf(stderr, "iogen%s: device level io not supported on cray2\n", TagName);
+ return -1;
+ }
+#endif
+
+ rec->f_type = sbuf.st_mode & S_IFMT;
+
+ /*
+ * If regular, iou is 1, and we must figure out the device on
+ * which the file resides. riou is the iou (logical sector size) of
+ * this device.
+ */
+
+ if (S_ISREG(sbuf.st_mode)) {
+ rec->f_iou = 1;
+ rec->f_length = sbuf.st_size;
+
+ /*
+ * If -r used, take Rawmult as the raw/ssd io multiple. Otherwise
+ * attempt to determine it by looking at the device the file
+ * resides on.
+ */
+
+ if (r_opt) {
+ rec->f_riou = Rawmult;
+ return 0;
+ }
+
+#ifdef CRAY
+ if (lk_rawdev(rec->f_path, dinfo.path, sizeof(dinfo.path), 0) == -1)
+ return -1;
+
+ if (lk_devinfo(&dinfo, 0) == -1) {
+ /* can't get raw I/O unit -- use stat to fudge it */
+ rec->f_riou = sbuf.st_blksize;
+ } else {
+ rec->f_riou = ctob(dinfo.iou);
+ }
+#endif
+#ifdef linux
+ rec->f_riou = BSIZE;
+#endif
+#ifdef sgi
+ if( (fd = open(rec->f_path, O_RDWR|O_DIRECT, 0)) != -1 ) {
+ if(fcntl(fd, F_DIOINFO, &finfo) != -1) {
+ rec->f_riou = finfo.d_miniosz;
+ } else {
+ fprintf(stderr,
+ "iogen%s: Error %s (%d) getting direct I/O info of file %s\n",
+ TagName, sys_errlist[errno], errno, rec->f_path);
+ }
+ close(fd);
+ } else {
+ rec->f_riou = BBSIZE;
+ }
+#endif /* SGI */
+
+ } else {
+
+#ifdef CRAY
+ /*
+ * Otherwise, file is a device. Use lk_devinfo() to get its logical
+ * sector size. This is the iou and riou
+ */
+
+ strcpy(dinfo.path, rec->f_path);
+
+ if (lk_devinfo(&dinfo, 0) == -1) {
+ fprintf(stderr, "iogen%s: %s: %s\n", TagName, Lk_err_func, Lk_err_mesg);
+ return -1;
+ }
+
+ rec->f_iou = ctob(dinfo.iou);
+ rec->f_riou = ctob(dinfo.iou);
+ rec->f_length = ctob(dinfo.length);
+#else
+#ifdef sgi
+ rec->f_riou = BBSIZE;
+ rec->f_length = BBSIZE;
+#else
+ rec->f_riou = BSIZE;
+ rec->f_length = BSIZE;
+#endif /* sgi */
+#endif /* CRAY */
+ }
+
+ return 0;
+}
+
+/*
+ * Create file path as nbytes long. If path exists, the file will either be
+ * extended or truncated to be nbytes long. Returns final size of file,
+ * or -1 if there was a failure.
+ */
+
+int
+create_file(path, nbytes)
+char *path;
+int nbytes;
+{
+ int fd, rval, nb;
+ char c;
+ struct stat sbuf;
+#ifdef sgi
+ struct flock f;
+ struct fsxattr xattr;
+ struct dioattr finfo;
+ char *b, *buf;
+#endif
+
+ errno = 0;
+ rval = stat(path, &sbuf);
+
+ if (rval == -1) {
+ if (errno == ENOENT) {
+ sbuf.st_size = 0;
+ } else {
+ fprintf(stderr, "iogen%s: Could not stat file %s: %s (%d)\n",
+ TagName, path, SYSERR, errno);
+ return -1;
+ }
+ } else {
+ if (! S_ISREG(sbuf.st_mode)) {
+ fprintf(stderr,
+ "iogen%s: file %s exists, but is not a regular file - cannot modify length\n",
+ TagName, path);
+ return -1;
+ }
+ }
+
+ if (sbuf.st_size == nbytes)
+ return nbytes;
+
+ Oflags |= O_CREAT | O_WRONLY;
+
+ if ((fd = open(path, Oflags, 0666, Ocbits, Ocblks)) == -1) {
+ fprintf(stderr, "iogen%s: Could not create/open file %s: %s (%d)\n",
+ TagName, path, SYSERR, errno);
+ return -1;
+ }
+
+ /*
+ * Truncate file if it is longer than nbytes, otherwise attempt to
+ * pre-allocate file blocks.
+ */
+
+ if (sbuf.st_size > nbytes) {
+ if (ftruncate(fd, nbytes) == -1) {
+ fprintf(stderr,
+ "iogen%s: Could not ftruncate() %s to %d bytes: %s (%d)\n",
+ TagName, path, nbytes, SYSERR, errno);
+ close(fd);
+ return -1;
+ }
+ } else {
+
+#ifdef sgi
+ /*
+ * The file must be designated as Real-Time before any data
+ * is allocated to it.
+ *
+ */
+ if(Orealtime != 0) {
+ bzero(&xattr, sizeof(xattr));
+ xattr.fsx_xflags = XFS_XFLAG_REALTIME;
+ /*fprintf(stderr, "set: fsx_xflags = 0x%x\n", xattr.fsx_xflags);*/
+ if( fcntl(fd, F_FSSETXATTR, &xattr) == -1 ) {
+ fprintf(stderr, "iogen%s: Error %s (%d) setting XFS XATTR->Realtime on file %s\n",
+ TagName, SYSERR, errno, path);
+ close(fd);
+ return -1;
+ }
+
+#ifdef DEBUG
+ if( fcntl(fd, F_FSGETXATTR, &xattr) == -1 ) {
+ fprintf(stderr, "iogen%s: Error getting realtime flag %s (%d)\n",
+ TagName, SYSERR, errno);
+ close(fd);
+ return -1;
+ } else {
+ fprintf(stderr, "get: fsx_xflags = 0x%x\n",
+ xattr.fsx_xflags);
+ }
+#endif
+ }
+
+ /*
+ * Reserve space with F_RESVSP
+ *
+ * Failure is ignored since F_RESVSP only works on XFS and the
+ * filesystem could be on EFS or NFS
+ */
+ if( Oreserve ) {
+ f.l_whence = SEEK_SET;
+ f.l_start = 0;
+ f.l_len = nbytes;
+
+ /*fprintf(stderr,
+ "create_file: fcntl(%d, F_RESVSP, { %d, %lld, %lld })\n",
+ fd, f.l_whence, (long long)f.l_start, (long long)f.l_len);*/
+
+ /* non-zeroing reservation */
+ if( fcntl( fd, F_RESVSP, &f ) == -1) {
+ fprintf(stderr,
+ "iogen%s: Could not fcntl(F_RESVSP) %d bytes in file %s: %s (%d)\n",
+ TagName, nbytes, path, SYSERR, errno);
+ close(fd);
+ return -1;
+ }
+ }
+
+ if( Oallocate ) {
+ /* F_ALLOCSP allocates from the start of the file to l_start */
+ f.l_whence = SEEK_SET;
+ f.l_start = nbytes;
+ f.l_len = 0;
+ /*fprintf(stderr,
+ "create_file: fcntl(%d, F_ALLOCSP, { %d, %lld, %lld })\n",
+ fd, f.l_whence, (long long)f.l_start,
+ (long long)f.l_len);*/
+
+ /* zeroing reservation */
+ if( fcntl( fd, F_ALLOCSP, &f ) == -1) {
+ fprintf(stderr,
+ "iogen%s: Could not fcntl(F_ALLOCSP) %d bytes in file %s: %s (%d)\n",
+ TagName, nbytes, path, SYSERR, errno);
+ close(fd);
+ return -1;
+ }
+ }
+#endif /* sgi */
+
+ /*
+ * Write a byte at the end of file so that stat() sets the right
+ * file size.
+ */
+
+#ifdef sgi
+ if(Owrite == 2) {
+ close(fd);
+ if( (fd = open(path, O_CREAT|O_RDWR|O_DIRECT, 0)) != -1 ) {
+ if(fcntl(fd, F_DIOINFO, &finfo) == -1) {
+ fprintf(stderr,
+ "iogen%s: Error %s (%d) getting direct I/O info for file %s\n",
+ TagName, SYSERR, errno, path);
+ return -1;
+ } else {
+ /*fprintf(stderr, "%s: miniosz=%d\n",
+ path, finfo.d_miniosz);*/
+ }
+ } else {
+ fprintf(stderr, "iogen%s: Error %s (%d) opening file %s with flags O_CREAT|O_RDWR|O_DIRECT\n",
+ TagName, SYSERR, errno, path);
+ return -1;
+ }
+
+ /*
+ * nb is nbytes adjusted down by an even d_miniosz block
+ *
+ * Note: the first adjustment can cause iogen to print a warning
+ * about not being able to create a file of <nbytes> length,
+ * since the file will be shorter.
+ */
+ nb = nbytes-finfo.d_miniosz;
+ nb = nb-nb%finfo.d_miniosz;
+
+ /*fprintf(stderr,
+ "create_file_ow2: lseek(%d, %d {%d %d}, SEEK_SET)\n",
+ fd, nb, nbytes, finfo.d_miniosz);*/
+
+ if (lseek(fd, nb, SEEK_SET) == -1) {
+ fprintf(stderr,
+ "iogen%s: Could not lseek() to EOF of file %s: %s (%d)\n\tactual offset %d file size goal %d miniosz %lld\n",
+ TagName, path, SYSERR, errno,
+ nb, nbytes, (long long)finfo.d_miniosz);
+ close(fd);
+ return -1;
+ }
+
+ b = buf = (char *)malloc(finfo.d_miniosz+finfo.d_mem);
+
+ if( ((long)buf % finfo.d_mem != 0) ) {
+ buf += finfo.d_mem - ((long)buf % finfo.d_mem);
+ }
+
+ memset(buf, 0, finfo.d_miniosz);
+
+ if ( (rval=write(fd, buf, finfo.d_miniosz)) != finfo.d_miniosz) {
+ fprintf(stderr,
+ "iogen%s: Could not write %d byte length file %s: %s (%d)\n",
+ TagName, nb, path, SYSERR, errno);
+ fprintf(stderr,
+ "\twrite(%d, 0x%lx, %d) = %d\n",
+ fd, (long)buf, finfo.d_miniosz, rval);
+ fprintf(stderr,
+ "\toffset %d file size goal %d, miniosz=%d\n",
+ nb, nbytes, finfo.d_miniosz);
+ close(fd);
+ return -1;
+ }
+ free(b);
+ } else
+#endif /* sgi */
+ if(Owrite) {
+ /*fprintf(stderr,
+ "create_file_Owrite: lseek(%d, %d {%d}, SEEK_SET)\n",
+ fd, nbytes-1, nbytes);*/
+
+ if (lseek(fd, nbytes-1, SEEK_SET) == -1) {
+ fprintf(stderr,
+ "iogen%s: Could not lseek() to EOF in file %s: %s (%d)\n\toffset goal %d\n",
+ TagName, path, SYSERR, errno,
+ nbytes-1);
+ close(fd);
+ return -1;
+ }
+
+ if ( (rval=write(fd, &c, 1)) != 1) {
+ fprintf(stderr,
+ "iogen%s: Could not create a %d byte length file %s: %s (%d)\n",
+ TagName, nbytes, path, SYSERR, errno);
+ fprintf(stderr,
+ "\twrite(%d, 0x%lx, %d) = %d\n",
+ fd, (long)&c, 1, rval);
+ fprintf(stderr,
+ "\toffset %d file size goal %d\n",
+ nbytes-1, nbytes);
+ close(fd);
+ return -1;
+ }
+ }
+ }
+
+ fstat(fd, &sbuf);
+ close(fd);
+
+ return sbuf.st_size;
+}
+
+/*
+ * Function to convert a string to its corresponding value in a strmap array.
+ * If the string is not found in the array, the value corresponding to the
+ * NULL string (the last element in the array) is returned.
+ */
+
+int
+str_to_value(map, str)
+struct strmap *map;
+char *str;
+{
+ struct strmap *mp;
+
+ for (mp = map; mp->m_string != NULL; mp++)
+ if (strcmp(mp->m_string, str) == 0)
+ break;
+
+ return mp->m_value;
+}
+
+/*
+ * Function to convert a string to its corresponding entry in a strmap array.
+ * If the string is not found in the array, a NULL is returned.
+ */
+
+struct strmap *
+str_lookup(map, str)
+struct strmap *map;
+char *str;
+{
+ struct strmap *mp;
+
+ for (mp = map; mp->m_string != NULL; mp++)
+ if (strcmp(mp->m_string, str) == 0)
+ break;
+
+ return((mp->m_string == NULL) ? NULL : mp);
+}
+
+
+/*
+ * Function to convert a value to its corresponding string in a strmap array.
+ * If the value is not found in the array, NULL is returned.
+ */
+
+char *
+value_to_string(map, val)
+struct strmap *map;
+int val;
+{
+ struct strmap *mp;
+
+ for (mp = map; mp->m_string != NULL; mp++)
+ if (mp->m_value == val)
+ break;
+
+ return mp->m_string;
+}
+
+/*
+ * Interpret cmdline options/arguments. Exit with 1 if something on the
+ * cmdline isn't kosher.
+ */
+
+int
+parse_cmdline(argc, argv, opts)
+int argc;
+char **argv;
+char *opts;
+{
+ int o, len, nb, format_error;
+ struct strmap *flgs, *sc, *type;
+ char *file, *cp, ch;
+ extern int opterr;
+ extern int optind;
+ extern char *optarg;
+ struct strmap *mp;
+ struct file_info *fptr;
+ int nopenargs;
+ char *openargs[5]; /* Flags, cbits, cblks */
+ char *ranges, *errmsg;
+ int str_to_int();
+ opterr = 0;
+
+ while ((o = getopt(argc, argv, opts)) != EOF) {
+ switch ((char)o) {
+
+ case 'a':
+#ifdef linux
+ fprintf(stderr, "iogen%s: Unrecognized option -a on this platform\n", TagName);
+ exit(2);
+#else
+ cp = strtok(optarg, ",");
+ while (cp != NULL) {
+ if ((type = str_lookup(Aio_Strat_Map, cp)) == NULL) {
+ fprintf(stderr, "iogen%s: Unrecognized aio completion strategy: %s\n", TagName, cp);
+ exit(2);
+ }
+
+ Aio_Strat_List[Naio_Strat_Types++] = type;
+ cp = strtok(NULL, ",");
+ }
+ a_opt++;
+#endif
+ break;
+
+ case 'f':
+ cp = strtok(optarg, ",");
+ while (cp != NULL) {
+ if( (flgs = str_lookup(Flag_Map, cp)) == NULL ) {
+ fprintf(stderr, "iogen%s: Unrecognized flags: %s\n", TagName, cp);
+ exit(2);
+ }
+
+ cp = strtok(NULL, ",");
+
+#ifdef O_SSD
+ if (flgs->m_value & O_SSD && ! Sds_Avail) {
+ fprintf(stderr, "iogen%s: Warning - no sds available, ignoring ssd flag\n", TagName);
+ continue;
+ }
+#endif
+
+ Flag_List[Nflags++] = flgs;
+ }
+ f_opt++;
+ break;
+
+ case 'h':
+ help(stdout);
+ exit(0);
+ break;
+
+ case 'i':
+ format_error = 0;
+
+ switch (sscanf(optarg, "%i%c", &Iterations, &ch)) {
+ case 1:
+ Time_Mode = 0;
+ break;
+
+ case 2:
+ if (ch == 's')
+ Time_Mode = 1;
+ else
+ format_error = 1;
+ break;
+
+ default:
+ format_error = 1;
+ }
+
+ if (Iterations < 0)
+ format_error = 1;
+
+ if (format_error) {
+ fprintf(stderr, "iogen%s: Illegal -i arg (%s): Must be of the format: number[s]\n", TagName, optarg);
+ fprintf(stderr, " where 'number' is >= 0\n");
+ exit(1);
+ }
+
+ i_opt++;
+ break;
+
+ case 'L':
+#ifdef linux
+ fprintf(stderr, "iogen%s: Unrecognized option -L on this platform\n", TagName);
+ exit(2);
+#else
+ if( parse_ranges(optarg, 1, 255, 1, NULL, &ranges,
+ &errmsg ) == -1 ) {
+ fprintf(stderr, "iogen%s: error parsing listio range '%s': %s\n",
+ TagName, optarg, errmsg);
+ exit(1);
+ }
+
+ Minstrides = range_min(ranges, 0);
+ Maxstrides = range_max(ranges, 0);
+
+ free(ranges);
+ L_opt++;
+#endif
+ break;
+
+ case 'm':
+ if ((Offset_Mode = str_lookup(Omode_Map, optarg)) == NULL) {
+ fprintf(stderr, "iogen%s: Illegal -m arg (%s)\n", TagName, optarg);
+ exit(1);
+ }
+
+ m_opt++;
+ break;
+
+ case 'N':
+ sprintf( TagName, "(%.39s)", optarg );
+ break;
+
+ case 'o':
+ o_opt++;
+ break;
+
+ case 'O':
+
+ nopenargs = string_to_tokens(optarg, openargs, 4, ":/");
+
+#ifdef CRAY
+ if(nopenargs)
+ sscanf(openargs[1],"%i", &Ocbits);
+ if(nopenargs > 1)
+ sscanf(openargs[2],"%i", &Ocblks);
+
+ Oflags = parse_open_flags(openargs[0], &errmsg);
+ if(Oflags == -1) {
+ fprintf(stderr, "iogen%s: -O %s error: %s\n", TagName, optarg, errmsg);
+ exit(1);
+ }
+#endif
+#ifdef linux
+ Oflags = parse_open_flags(openargs[0], &errmsg);
+ if(Oflags == -1) {
+ fprintf(stderr, "iogen%s: -O %s error: %s\n", TagName, optarg, errmsg);
+ exit(1);
+ }
+#endif
+#ifdef sgi
+ if(!strcmp(openargs[0], "realtime")) {
+ /*
+ * -O realtime:extsize
+ */
+ Orealtime = 1;
+ if(nopenargs > 1)
+ sscanf(openargs[1],"%i", &Oextsize);
+ else
+ Oextsize=0;
+ } else if( !strcmp(openargs[0], "allocate") ||
+ !strcmp(openargs[0], "allocsp")) {
+ /*
+ * -O allocate
+ */
+ Oreserve=0;
+ Oallocate=1;
+ } else if(!strcmp(openargs[0], "reserve")) {
+ /*
+ * -O [no]reserve
+ */
+ Oallocate=0;
+ Oreserve=1;
+ } else if(!strcmp(openargs[0], "noreserve")) {
+ /* Oreserve=1 by default; this clears that default */
+ Oreserve=0;
+ } else if(!strcmp(openargs[0], "nowrite")) {
+ /* Owrite=1 by default; this clears that default */
+ Owrite=0;
+ } else if(!strcmp(openargs[0], "direct")) {
+ /* this means "use direct i/o to preallocate file" */
+ Owrite=2;
+ } else {
+ fprintf(stderr, "iogen%s: Error: -O %s error: unrecognized option\n",
+ TagName, openargs[0]);
+ exit(1);
+ }
+#endif
+
+ O_opt++;
+ break;
+
+ case 'p':
+ Outpipe = optarg;
+ p_opt++;
+ break;
+
+ case 'r':
+ if ((Rawmult = str_to_bytes(optarg)) == -1 ||
+ Rawmult < 11 || Rawmult % BSIZE) {
+ fprintf(stderr, "iogen%s: Illegal -r arg (%s). Must be > 0 and multipe of BSIZE (%d)\n",
+ TagName, optarg, BSIZE);
+ exit(1);
+ }
+
+ r_opt++;
+ break;
+
+ case 's':
+ cp = strtok(optarg, ",");
+ while (cp != NULL) {
+ if ((sc = str_lookup(Syscall_Map, cp)) == NULL) {
+ fprintf(stderr, "iogen%s: Unrecognized syscall: %s\n", TagName, cp);
+ exit(2);
+ }
+
+ do {
+ /* >>> sc->m_flags & FLG_SDS */
+ if (sc->m_value != SSREAD && sc->m_value != SSWRITE)
+ Fileio++;
+
+ Syscall_List[Nsyscalls++] = sc;
+ } while ( (sc = str_lookup(++sc, cp)) != NULL);
+
+ cp = strtok(NULL, ",");
+ }
+ s_opt++;
+ break;
+
+ case 't':
+ if ((Mintrans = str_to_bytes(optarg)) == -1) {
+ fprintf(stderr, "iogen%s: Illegal -t arg (%s): Must have the form num[bkm]\n", TagName, optarg);
+ exit(1);
+ }
+ t_opt++;
+ break;
+
+ case 'T':
+ if ((Maxtrans = str_to_bytes(optarg)) == -1) {
+ fprintf(stderr, "iogen%s: Illegal -T arg (%s): Must have the form num[bkm]\n", TagName, optarg);
+ exit(1);
+ }
+ T_opt++;
+ break;
+
+ case 'q':
+ q_opt++;
+ break;
+
+ case '?':
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ /*
+ * Supply defaults
+ */
+
+ if( ! L_opt ) {
+ Minstrides = 1;
+ Maxstrides = 255;
+ }
+
+ if (! m_opt)
+ Offset_Mode = str_lookup(Omode_Map, "sequential");
+
+ if (! i_opt)
+ Iterations = 0;
+
+ if (! t_opt)
+ Mintrans = 1;
+
+ if (! T_opt)
+ Maxtrans = 256 * BSIZE;
+
+ if( ! O_opt)
+ Oflags = Ocbits = Ocblks = 0;
+
+ /*
+ * Supply default async io completion strategy types.
+ */
+
+ if (! a_opt) {
+ for (mp = Aio_Strat_Map; mp->m_string != NULL; mp++) {
+ Aio_Strat_List[Naio_Strat_Types++] = mp;
+ }
+ }
+
+ /*
+ * Supply default syscalls. Default is read,write,reada,writea,listio.
+ */
+
+ if (! s_opt) {
+ Nsyscalls = 0;
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "read");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "write");
+#ifdef CRAY
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "reada");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "writea");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lread");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lreada");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lwrite");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "lwritea");
+#endif
+
+#ifdef sgi
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "pread");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "pwrite");
+ /*Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "aread");*/
+ /*Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "awrite");*/
+#endif
+
+#ifndef CRAY
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "readv");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "writev");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "mmread");
+ Syscall_List[Nsyscalls++] = str_lookup(Syscall_Map, "mmwrite");
+#endif
+
+ Fileio = 1;
+ }
+
+ if (Fileio && (argc - optind < 1)) {
+ fprintf(stderr, "iogen%s: No files specified on the cmdline\n", TagName);
+ exit(1);
+ }
+
+ /*
+ * Supply default file io flags - defaut is 'buffered,raw,sync,ldraw'.
+ */
+
+ if (! f_opt && Fileio) {
+ Nflags = 0;
+ Flag_List[Nflags++] = str_lookup(Flag_Map, "buffered");
+ Flag_List[Nflags++] = str_lookup(Flag_Map, "sync");
+#ifdef CRAY
+ Flag_List[Nflags++] = str_lookup(Flag_Map, "raw+wf");
+ Flag_List[Nflags++] = str_lookup(Flag_Map, "ldraw");
+#endif
+
+#ifdef sgi
+ /* Warning: cannot mix direct i/o with others! */
+ Flag_List[Nflags++] = str_lookup(Flag_Map, "dsync");
+ Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync");
+ /* Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync+sync");*/
+ /* Flag_List[Nflags++] = str_lookup(Flag_Map, "rsync+dsync");*/
+#endif
+ }
+
+ if (Fileio) {
+ if (optind >= argc) {
+ fprintf(stderr, "iogen%s: No files listed on the cmdline\n", TagName);
+ exit(1);
+ }
+
+ /*
+ * Initialize File_List[] - only necessary if doing file io. First
+ * space for the File_List array, then fill it in.
+ */
+
+ File_List = (struct file_info *)
+ malloc((argc-optind) * sizeof(struct file_info));
+
+ if (File_List == NULL) {
+ fprintf(stderr, "iogen%s: Could not malloc space for %d file_info structures\n", TagName, argc-optind);
+ exit(2);
+ }
+
+ memset(File_List, 0, (argc-optind) * sizeof(struct file_info));
+
+ Nfiles = 0;
+ while (optind < argc) {
+ len = -1;
+
+ /*
+ * Pick off leading len: if it's there and create/extend/trunc
+ * the file to the desired length. Otherwise, just make sure
+ * the file is accessable.
+ */
+
+ if ((cp = strchr(argv[optind], ':')) != NULL) {
+ *cp = '\0';
+ if ((len = str_to_bytes(argv[optind])) == -1) {
+ fprintf(stderr,
+ "iogen%s: illegal file length (%s) for file %s\n",
+ TagName, argv[optind], cp+1);
+ exit(2);
+ }
+ *cp = ':';
+ file = cp+1;
+
+ if (strlen(file) > MAX_FNAME_LENGTH) {
+ fprintf(stderr, "iogen%s: Max fname length is %d chars - ignoring file %s\n",
+ TagName, MAX_FNAME_LENGTH, file);
+ optind++;
+ continue;
+ }
+
+ nb = create_file(file, len);
+
+ if (nb < len) {
+ fprintf(stderr,
+ "iogen%s warning: Couldn't create file %s of %d bytes\n",
+ TagName, file, len);
+
+ if (nb <= 0) {
+ optind++;
+ continue;
+ }
+ }
+ } else {
+ file = argv[optind];
+ if (access(file, R_OK | W_OK) == -1) {
+ fprintf(stderr, "iogen%s: file %s cannot be accessed for reading and/or writing: %s (%d)\n",
+ TagName, file, SYSERR, errno);
+ exit(2);
+ }
+ }
+
+ /*
+ * get per-file information
+ */
+
+ fptr = &File_List[Nfiles];
+
+ if (file[0] == '/') {
+ strcpy(fptr->f_path, file);
+ } else {
+ getcwd(fptr->f_path,
+ sizeof(fptr->f_path)-1);
+ strcat(fptr->f_path, "/");
+ strcat(fptr->f_path, file);
+ }
+
+ if (get_file_info(fptr) == -1) {
+ fprintf(stderr, "iogen%s warning: Error getting file info for %s\n", TagName, file);
+ } else {
+
+ /*
+ * If the file length is smaller than our min transfer size,
+ * ignore it.
+ */
+
+ if (fptr->f_length < Mintrans) {
+ fprintf(stderr, "iogen%s warning: Ignoring file %s\n",
+ TagName, fptr->f_path);
+ fprintf(stderr, " length (%d) is < min transfer size (%d)\n",
+ fptr->f_length, Mintrans);
+ optind++;
+ continue;
+ }
+
+ /*
+ * If the file length is smaller than our max transfer size,
+ * ignore it.
+ */
+
+ if (fptr->f_length < Maxtrans) {
+ fprintf(stderr, "iogen%s warning: Ignoring file %s\n",
+ TagName, fptr->f_path);
+ fprintf(stderr, " length (%d) is < max transfer size (%d)\n",
+ fptr->f_length, Maxtrans);
+ optind++;
+ continue;
+ }
+
+ if (fptr->f_length > 0) {
+ switch (Offset_Mode->m_value) {
+ case M_SEQUENTIAL:
+ fptr->f_lastoffset = 0;
+ fptr->f_lastlength = 0;
+ break;
+
+ case M_REVERSE:
+ fptr->f_lastoffset = fptr->f_length;
+ fptr->f_lastlength = 0;
+ break;
+
+ case M_RANDOM:
+ fptr->f_lastoffset = fptr->f_length / 2;
+ fptr->f_lastlength = 0;
+ break;
+ }
+
+ Nfiles++;
+ }
+ }
+
+ optind++;
+ }
+
+ if (Nfiles == 0) {
+ fprintf(stderr, "iogen%s: Could not create, or gather info for any test files\n", TagName);
+ exit(2);
+ }
+ }
+
+ return 0;
+}
+
+int
+help(stream)
+FILE *stream;
+{
+ usage(stream);
+ fprintf(stream, "\n");
+#ifndef linux
+ fprintf(stream, "\t-a aio_type,... Async io completion types to choose. Supported types\n");
+#ifdef CRAY
+#if _UMK || RELEASE_LEVEL >= 8000
+ fprintf(stream, "\t are: poll, signal, recall, recalla, and recalls.\n");
+#else
+ fprintf(stream, "\t are: poll, signal, recalla, and recalls.\n");
+#endif
+#else
+ fprintf(stream, "\t are: poll, signal, suspend, and callback.\n");
+#endif
+ fprintf(stream, "\t Default is all of the above.\n");
+#else /* !linux */
+ fprintf(stream, "\t-a (Not used on Linux).\n");
+#endif /* !linux */
+ fprintf(stream, "\t-f flag,... Flags to use for file IO. Supported flags are\n");
+#ifdef CRAY
+ fprintf(stream, "\t raw, ssd, buffered, ldraw, sync,\n");
+ fprintf(stream, "\t raw+wf, raw+wf+ldraw, raw+wf+ldraw+sync,\n");
+ fprintf(stream, "\t and parallel (unicos/mk on MPP only).\n");
+ fprintf(stream, "\t Default is 'raw,ldraw,sync,buffered'.\n");
+#else
+#ifdef sgi
+ fprintf(stream, "\t buffered, direct, sync, dsync, rsync,\n");
+ fprintf(stream, "\t rsync+dsync.\n");
+ fprintf(stream, "\t Default is 'buffered,sync,dsync,rsync'.\n");
+#else
+ fprintf(stream, "\t buffered, sync.\n");
+ fprintf(stream, "\t Default is 'buffered,sync'.\n");
+#endif /* sgi */
+#endif /* CRAY */
+ fprintf(stream, "\t-h This help.\n");
+ fprintf(stream, "\t-i iterations[s] # of requests to generate. 0 means causes iogen\n");
+ fprintf(stream, "\t to run until it's killed. If iterations is suffixed\n");
+ fprintf(stream, "\t with 's', then iterations is the number of seconds\n");
+ fprintf(stream, "\t that iogen should run for. Default is '0'.\n");
+#ifndef linux
+ fprintf(stream, "\t-L min:max listio nstrides / nrequests range\n");
+#else
+ fprintf(stream, "\t-L (Not used on Linux).\n");
+#endif /* !linux */
+ fprintf(stream, "\t-m offset-mode The mode by which iogen chooses the offset for\n");
+ fprintf(stream, "\t consectutive transfers within a given file.\n");
+ fprintf(stream, "\t Allowed values are 'random', 'sequential',\n");
+ fprintf(stream, "\t and 'reverse'.\n");
+ fprintf(stream, "\t sequential is the default.\n");
+ fprintf(stream, "\t-N tagname Tag name, for Monster.\n");
+ fprintf(stream, "\t-o Form overlapping consecutive requests.\n");
+ fprintf(stream, "\t-O Open flags for creating files\n");
+#ifdef CRAY
+ fprintf(stream, "\t {O_PLACE,O_BIG,etc}[:CBITS[:CBLKS]]\n");
+#endif
+#ifdef sgi
+ fprintf(stream, "\t realtime:extsize - put file on real-time volume\n");
+ fprintf(stream, "\t allocate - allocate space with F_ALLOCSP\n");
+ fprintf(stream, "\t reserve - reserve space with F_RESVSP (default)\n");
+ fprintf(stream, "\t noreserve - do not reserve with F_RESVSP\n");
+ fprintf(stream, "\t direct - use O_DIRECT I/O to write to the file\n");
+#endif
+#ifdef linux
+ fprintf(stream, "\t {O_SYNC,etc}\n");
+#endif
+ fprintf(stream, "\t-p Output pipe. Default is stdout.\n");
+ fprintf(stream, "\t-q Quiet mode. Normally iogen spits out info\n");
+ fprintf(stream, "\t about test files, options, etc. before starting.\n");
+ fprintf(stream, "\t-s syscall,... Syscalls to do. Supported syscalls are\n");
+#ifdef sgi
+ fprintf(stream, "\t read, write, pread, pwrite, readv, writev\n");
+ fprintf(stream, "\t aread, awrite, resvsp, unresvsp, ffsync,\n");
+ fprintf(stream, "\t mmread, mmwrite, fsync2, fdatasync,\n");
+ fprintf(stream, "\t Default is 'read,write,pread,pwrite,readv,writev,mmread,mmwrite'.\n");
+#endif
+#ifdef CRAY
+ fprintf(stream, "\t read, write, reada, writea, listio,\n");
+ fprintf(stream, "\t ssread (PVP only), and sswrite (PVP only).\n");
+ fprintf(stream, "\t Default is 'read,write,reada,writea,listio'.\n");
+#endif
+#ifdef linux
+ fprintf(stream, "\t read, write, readv, writev,\n");
+ fprintf(stream, "\t mmread, mmwrite, fsync2, fdatasync,\n");
+ fprintf(stream, "\t Default is 'read,write,readv,writev,mmread,mmwrite'.\n");
+#endif
+ fprintf(stream, "\t-t mintrans Min transfer length\n");
+ fprintf(stream, "\t-T maxtrans Max transfer length\n");
+ fprintf(stream, "\n");
+ fprintf(stream, "\t[len:]file,... Test files to do IO against (note ssread/sswrite\n");
+ fprintf(stream, "\t don't need a test file). The len: syntax\n");
+ fprintf(stream, "\t informs iogen to first create/expand/truncate the\n");
+ fprintf(stream, "\t to the desired length.\n");
+ fprintf(stream, "\n");
+ fprintf(stream, "\tNote: The ssd flag causes sds transfers to also be done.\n");
+ fprintf(stream, "\t To totally eliminate sds transfers, you must eleminate sds\n");
+ fprintf(stream, "\t from the flags (-f) and ssread,ssrite from the syscalls (-s)\n");
+ fprintf(stream, "\tThe mintrans, maxtrans, and len: parameters are numbers of the\n");
+ fprintf(stream, "\tform [0-9]+[bkm]. The optional trailing b, k, or m multiplies\n");
+ fprintf(stream, "\tthe number by blocks, kilobytes, or megabytes. If no trailing\n");
+ fprintf(stream, "\tmultiplier is present, the number is interpreted as bytes\n");
+
+ return 0;
+}
+
+/*
+ * Obvious - usage clause
+ */
+
+int
+usage(stream)
+FILE *stream;
+{
+ fprintf(stream, "usage%s: iogen [-hoq] [-a aio_type,...] [-f flag[,flag...]] [-i iterations] [-p outpipe] [-m offset-mode] [-s syscall[,syscall...]] [-t mintrans] [-T maxtrans] [ -O file-create-flags ] [[len:]file ...]\n", TagName);
+ return 0;
+}
diff --git a/doio/rwtest.ks b/doio/rwtest.ks
new file mode 100644
index 0000000..333b848
--- /dev/null
+++ b/doio/rwtest.ks
@@ -0,0 +1,392 @@
+#! /bin/ksh
+
+# Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA 94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+
+#
+# rwtest - a shell wrapper around iogen and doio
+#
+
+trap "exit 0" INT # Until the smarter signal handler is engaged, below.
+
+Prog=$(basename $0)
+
+iOpts=""
+dOpts=""
+LockRegion=""
+Nprocs=1
+Files=""
+Remove_Test_Files=""
+Files_To_Remove=""
+MPPrun=""
+
+usage()
+{
+ echo "$Prog: [-chq] [-N name] [ iogen options ] [ doio options ] files" >&2
+}
+
+help()
+{
+ echo "\
+ -c Cleanup test files created by this invocation on exit.
+ Default is to leave them.
+ -h This help - ignore all other options/arguments
+ -F Only process filenames - does not run iogen & doio.
+ -P Places Not used
+ -S Scenario Execute an internal scenario.
+ -N Name Pan-style name to be printed with error messages.
+
+ Options passed through to iogen:
+ -[afiLmOstT] arg
+ -o
+ -q Set rwtest to be quiet and pass the flag on to iogen.
+
+ Options passed through to doio:
+ -D[rmMVUC] arg
+ -D[aekv]
+ -n nprocs # procs to do simultanious io to the test files.
+ Default is 1. If -n is non-zero, doio's -k option (use
+ file locking) is forced.
+
+ files Files to test on. File names have the following fomat:
+ [ size: ] path
+ [ free% [ max size ] : ] path
+ If no size is specified, the files must exist
+ and the contents will be overwritten if testing write or
+ writea system calls. If a size is supplied, an attempt to
+ create/grow/shrink path to the desired size will be made.
+ size is an integer which defaults to bytes, but may be
+ suffixed by 'b', 'k', or 'm' for blocks (4096 byte units),
+ kilobytes (1024 byte units), or megabytes (2^20 byte units).
+
+ If the size is a percentage, df is used to find how much
+ free space there is (in blocks), and uses that. The maximum
+ size is implied to be in blocks.
+" >&2
+}
+
+killkids()
+{
+ trap "killkids" INT
+ if [[ -z $didkids ]]
+ then
+ didkids=done
+ kill -INT -$$
+ fi
+}
+
+
+cleanup_and_exit()
+{
+ if [ -n "$Remove_Test_Files" ]
+ then
+ if [ -n "$Files_To_Remove" ]
+ then
+ rm -f $Files_To_Remove
+ fi
+ fi
+
+ exit $1
+}
+
+while (( $# > 0 ))
+do case $1 in
+ -c) Remove_Test_Files=yes
+ ;;
+
+ -d) debug=$2
+ shift
+ ;;
+
+ -h) help
+ exit 0
+ ;;
+
+ -F)
+ opt_F="-F" # only process filenames
+ ;;
+
+ -P)
+ PLACES=$2
+ shift
+ ;;
+
+ -S) Scenario=$2
+ shift
+ opt_S="-S"
+ ;;
+
+ -N) Name="($2)"
+ iOpts="$iOpts -N $2"
+ dOpts="$dOpts -N $2"
+ shift
+ ;;
+
+ # iogen Options to pass thru ... options with an argument
+ -[afiLmOstT] )
+ iOpts="$iOpts $1 $2"
+ shift
+ ;;
+
+ # iogen Options to pass thru ... just the option
+ -[o] )
+ iOpts="$iOpts $1"
+ ;;
+
+ # iogen options to look at
+ -q)
+ iOpts="$iOpts $1"
+ Quiet=$1
+ ;;
+
+ # doio Options to pass thru ... options with an argument
+ -D[rmMVUC] )
+ o=${1#-D}
+ dOpts="$dOpts -$o $2"
+ shift
+ ;;
+
+ # doio options to pass thru ... just the options
+ -D[aekv] )
+ o=${1#-D}
+ dOpts="$dOpts -$o"
+ ;;
+
+ # doio options to look at
+ -n | -Dn )
+ dOpts="$dOpts $1 $2"
+ # force file locking with > 1 process
+ if [[ $2 > 1 ]]
+ then
+ dOpts="$dOpts -k"
+ fi
+ opt_n="-n"
+ shift
+ ;;
+
+ \? | -*)
+ echo "$Prog: Illegal option $1" >&2
+ exit 1
+ ;;
+
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+
+if [[ $TOUTPUT = "NOPASS" ]]; then
+ iOpts="$iOpts -q"
+ Quiet="-q"
+fi
+
+#
+# Hard-Coded Scenario Specifications
+#
+# FSA RAW I/O via FSA
+# MPPnnn Run as a <nnn> sized MPP application
+# userstripe create files using user striping
+#
+
+if [[ -n "$opt_S" ]]
+then
+ case $Scenario in
+
+ FSA )
+ # I/O via FSA
+ Flags="parallel"
+ Nprocs=1
+ LockRegion=""
+ ;;
+
+ MPP* )
+ # use mpprun... to cooperate with a batch system, this
+ # requires scanning mppview, etc.
+
+ NPE=${Scenario#MPP}
+ MPPrun=" mpprun -n $NPE "
+ ;;
+ userstripe)
+ #create files using user striping
+ Oflags=O_PLACE,0xffffffffffffffff,1000
+ ;;
+
+ places*)
+ FSIZE=${Scenario#places-}
+ oi="$IFS"
+ IFS=":"
+ set -- $PLACES
+ if [ $# -eq 0 ]
+ then
+ # "this isn't supposed to happen"
+ Files="25%:rwtest.$$"
+ else
+ IFS="$oi"
+ PL=${*}
+ for p in $PL
+ do
+ f="$f "${FSIZE}":"${p}"/rwtest$$"
+ done
+ set -- $f
+ fi
+ ;;
+ esac
+fi
+
+#
+# If no files are specified ...
+# check if PLACES is set; if so, put one file in each place
+# otherwise generate one filename in the current directory.
+#
+
+if [ $# -eq 0 ]
+then
+ # put one file in each of $PLACES
+ Files="25%:rwtest.file"
+else
+ Files=$*
+fi
+
+#
+# use 'df -PB' to see how many blocks are available, apply a hard limit of
+# 1,000,000 blocks if no limit is specified
+#
+
+case $(uname -s) in
+ IRIX | IRIX64 ) dfOpts="-Pb" ;;
+ Linux) dfOpts="-P" ;;
+ *) dfOpts="-PB" ;;
+esac
+
+for f in $Files
+do
+ file=${f##*:}
+ if [ ! -f "$file" ]
+ then
+ Files_To_Remove="$Files_To_Remove $file"
+ fi
+
+ dir=$(dirname $file)
+ size=${f%%:*}
+ if [[ $size = *%* ]]
+ then
+
+ typeset -i n=0
+ while (( n < ${#szcache[*]} ))
+ do
+ if [[ szcache[$n] = $dir ]]; then
+ break;
+ fi
+ n=n+1
+ done
+
+ if (( n < ${#szcache[*]} ))
+ then
+ blks=${szblks[$n]}
+ else
+ blks=$(df $dfOpts $dir |
+ (while read fs blks used avail cap mountpoint
+ do
+ #echo $fs $blks $used $avail >&2
+ b=$avail
+ done
+ echo $b) )
+
+ case $(uname) in
+ Linux) blks=$( expr $blks / 2 ) ;;
+ esac
+
+ szcache[${#szcache[*]}+1]=$dir
+ szblks[${#szblks[*]}+1]=$blks
+ fi
+
+ max=${size##*\%}
+ if [[ "$max" = "" ]]
+ then
+ max=1000000
+ fi
+ size=${size%%\%*}
+
+ case $(uname) in
+ IRIX*)
+ sz=$( perl -le 'print int( '$blks' * '$size' / 100 )' )
+ ;;
+ *)
+ sz=$(expr \( $blks '*' $size \) / 100)
+ ;;
+ esac
+
+ if [[ $sz -gt $max ]]
+ then
+ sz=$max
+ fi
+ f=$sz"b:"$file
+ fi
+ F[${#F[*]}+1]=$f
+done
+
+Files=${F[*]}
+
+if [[ -z ${dOpts} ]]; then
+ dOpts=-av
+fi
+
+if [[ -n "$opt_F" ]]; then
+
+ echo $Files
+
+else
+
+ cmd="iogen ${iOpts} ${Files} | $MPPrun doio ${dOpts}"
+
+ if [[ -z "$Quiet" ]]; then
+ echo $cmd
+ fi
+
+ trap "killkids" INT
+ trap "cleanup_and_exit 2" HUP
+
+ ( iogen ${iOpts} ${Files}
+ r=$?
+ if [ $r -ne 0 ]
+ then
+ print -u2 "$Prog$Name : iogen reported errors (r=$r)"
+ kill -HUP $$
+ fi
+ ) | $MPPrun doio ${dOpts}
+ r=$?
+ if [ $r -ne 0 ]
+ then
+ print -u2 "$Prog$Name : doio reported errors (r=$r)"
+ fi
+
+ cleanup_and_exit $r
+fi
diff --git a/include/dataascii.h b/include/dataascii.h
new file mode 100644
index 0000000..cd75245
--- /dev/null
+++ b/include/dataascii.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#ifndef _DATAASCII_H_
+#define _DATAASCII_H_
+
+/***********************************************************************
+ * int dataasciigen(listofchars, buffer, size, offset)
+ *
+ * This function fills buffer with ascii characters.
+ * The ascii characters are obtained from listofchars or the CHARS array
+ * if listofchars is NULL.
+ * Each char is selected by an index. The index is the remainder
+ * of count divided by the array size.
+ * This method allows more than one process to write to a location
+ * in a file without corrupting it for another process' point of view.
+ *
+ * The return value will be the number of character written in buffer
+ * (size).
+ *
+ ***********************************************************************/
+int dataasciigen(char *, char *, int, int);
+
+/***********************************************************************
+ * int dataasciichk(listofchars, buffer, size, count, errmsg)
+ *
+ * This function checks the contents of a buffer produced by
+ * dataasciigen.
+ *
+ * return values:
+ * >= 0 : error at character count
+ * < 0 : no error
+ ***********************************************************************/
+
+int dataasciichk(char *, char *, int, int, char**);
+
+#endif
diff --git a/include/open_flags.h b/include/open_flags.h
new file mode 100644
index 0000000..87fe6ff
--- /dev/null
+++ b/include/open_flags.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#ifndef _OPEN_FLAGS_H_
+#define _OPEN_FLAGS_H_
+
+/***********************************************************************
+ * This function attempts to convert open flag bits into human readable
+ * symbols (i.e. O_TRUNC). If there are more than one symbol,
+ * the <sep> string will be placed as a separator between symbols.
+ * Commonly used separators would be a comma "," or pipe "|".
+ * If <mode> is one and not all <openflags> bits can be converted to
+ * symbols, the "UNKNOWN" symbol will be added to return string.
+ *
+ * Return Value
+ * openflags2symbols will return the indentified symbols.
+ * If no symbols are recognized the return value will be a empty
+ * string or the "UNKNOWN" symbol.
+ *
+ * Limitations
+ * Currently (05/96) all known symbols are coded into openflags2symbols.
+ * If new open flags are added this code will have to updated
+ * to know about them or they will not be recognized.
+ *
+ * The Open_symbols must be large enough to hold all possible symbols
+ * for a given system.
+ *
+ ***********************************************************************/
+char *openflags2symbols( int, char *, int );
+
+/***********************************************************************
+ * This function will take a string of comma separated open flags symbols
+ * and translate them into an open flag bitmask.
+ * If any symbol is not valid, -1 is returned. On this error condition
+ * the badname pointer is updated if not NULL. badname will point
+ * to the beginning location of where the invalid symbol was found.
+ * string will be returned unchanged.
+ *
+ * A signal received while parsing string could cause the string to
+ * contain a NULL character in the middle of it.
+ *
+ ***********************************************************************/
+int parse_open_flags( char *, char ** );
+
+#endif
diff --git a/include/random_range.h b/include/random_range.h
new file mode 100644
index 0000000..d3e1cce
--- /dev/null
+++ b/include/random_range.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#ifndef _RANDOM_RANGE_H_
+#define _RANDOM_RANGE_H_
+
+int parse_ranges ( char *, int, int, int, int (*)(), char **, char ** );
+int range_min ( char *, int );
+int range_max ( char *, int );
+int range_mult ( char *, int );
+long random_range ( int, int, int, char ** );
+long random_rangel ( long, long, long, char ** );
+long long random_rangell ( long long, long long, long long, char ** );
+void random_range_seed( long );
+long random_bit ( long );
+
+#endif
diff --git a/include/str_to_bytes.h b/include/str_to_bytes.h
new file mode 100644
index 0000000..100d37d
--- /dev/null
+++ b/include/str_to_bytes.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#ifndef _STR_TO_BYTES_
+#define _STR_TO_BYTES_
+
+int str_to_bytes ( char * );
+long str_to_lbytes ( char * );
+long long str_to_llbytes( char * );
+
+#endif
diff --git a/include/string_to_tokens.h b/include/string_to_tokens.h
new file mode 100644
index 0000000..9c0935e
--- /dev/null
+++ b/include/string_to_tokens.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#ifndef _STRING_TO_TOKENS_H_
+#define _STRING_TO_TOKENS_H_
+
+/*
+ * string_to_tokens()
+ *
+ * This function parses the string 'arg_string', placing pointers to
+ * the 'separator' separated tokens into the elements of 'arg_array'.
+ * The array is terminated with a null pointer.
+ *
+ * NOTE: This function uses strtok() to parse 'arg_string', and thus
+ * physically alters 'arg_string' by placing null characters where the
+ * separators originally were.
+ */
+int string_to_tokens(char *, char **, int, char *);
+
+#endif
diff --git a/include/tlibio.h b/include/tlibio.h
new file mode 100644
index 0000000..ac0d570
--- /dev/null
+++ b/include/tlibio.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+
+#define LIO_IO_SYNC 00001 /* read/write */
+#define LIO_IO_ASYNC 00002 /* reada/writea/aio_write/aio_read */
+#define LIO_IO_SLISTIO 00004 /* single stride sync listio */
+#define LIO_IO_ALISTIO 00010 /* single stride async listio */
+#define LIO_IO_SYNCV 00020 /* single-buffer readv/writev */
+#define LIO_IO_SYNCP 00040 /* pread/pwrite */
+
+#ifdef sgi
+#define LIO_IO_ATYPES 00077 /* all io types */
+#define LIO_IO_TYPES 00061 /* all io types, non-async */
+#endif /* sgi */
+#ifdef linux
+#define LIO_IO_TYPES 00021 /* all io types */
+#endif /* linux */
+#ifdef CRAY
+#define LIO_IO_TYPES 00017 /* all io types */
+#endif /* CRAY */
+
+#define LIO_WAIT_NONE 00010000 /* return asap -- use with care */
+#define LIO_WAIT_ACTIVE 00020000 /* spin looking at iosw fields, or EINPROGRESS */
+#define LIO_WAIT_RECALL 00040000 /* call recall(2)/aio_suspend(3) */
+#define LIO_WAIT_SIGPAUSE 00100000 /* call pause */
+#define LIO_WAIT_SIGACTIVE 00200000 /* spin waiting for signal */
+#ifdef sgi
+#define LIO_WAIT_CBSUSPEND 00400000 /* aio_suspend waiting for callback */
+#define LIO_WAIT_SIGSUSPEND 01000000 /* aio_suspend waiting for signal */
+#define LIO_WAIT_ATYPES 01760000 /* all async wait types, except nowait */
+#define LIO_WAIT_TYPES 00020000 /* all sync wait types (sorta) */
+#endif /* sgi */
+#ifdef linux
+#define LIO_WAIT_TYPES 00300000 /* all wait types, except nowait */
+#endif /* linux */
+#ifdef CRAY
+#define LIO_WAIT_TYPES 00360000 /* all wait types, except nowait */
+#endif /* CRAY */
+
+/* meta wait io */
+/* 00 000 0000 */
+
+#ifdef sgi
+/* all callback wait types */
+#define LIO_WAIT_CBTYPES (LIO_WAIT_CBSUSPEND)
+/* all signal wait types */
+#define LIO_WAIT_SIGTYPES (LIO_WAIT_SIGPAUSE|LIO_WAIT_SIGACTIVE|LIO_WAIT_SIGSUSPEND)
+/* all aio_{read,write} or lio_listio */
+#define LIO_IO_ASYNC_TYPES (LIO_IO_ASYNC|LIO_IO_SLISTIO|LIO_IO_ALISTIO)
+#endif /* sgi */
+#ifdef linux
+/* all signal wait types */
+#define LIO_WAIT_SIGTYPES (LIO_WAIT_SIGPAUSE)
+#endif /* linux */
+#ifdef CRAY
+/* all signal wait types */
+#define LIO_WAIT_SIGTYPES (LIO_WAIT_SIGPAUSE|LIO_WAIT_SIGACTIVE)
+#endif /* CRAY */
+
+/*
+ * This bit provides a way to randomly pick an io type and wait method.
+ * lio_read_buffer() and lio_write_buffer() functions will call
+ * lio_random_methods() with the given method.
+ */
+#define LIO_RANDOM 010000000
+
+/*
+ * This bit provides a way for the programmer to use async i/o with
+ * signals and to use their own signal handler. By default,
+ * the signal will only be given to the system call if the wait
+ * method is LIO_WAIT_SIGPAUSE or LIO_WAIT_SIGACTIVE.
+ * Whenever these wait methods are used, libio signal handler
+ * will be used.
+ */
+#define LIO_USE_SIGNAL 020000000
+
+/*
+ * prototypes/structures for functions in the libio.c module. See comments
+ * in that module, or man page entries for information on the individual
+ * functions.
+ */
+
+int stride_bounds(int offset, int stride, int nstrides,
+ int bytes_per_stride, int *min_byte, int *max_byte);
+
+int lio_set_debug(int level);
+int lio_parse_io_arg1(char *string);
+void lio_help1(char *prefex);
+int lio_parse_io_arg2(char *string, char **badtoken);
+void lio_help2(char *prefex);
+int lio_write_buffer(int fd, int method, char *buffer, int size,
+ int sig, char **errmsg, long wrd);
+
+int lio_read_buffer(int fd, int method, char *buffer, int size,
+ int sig, char **errmsg, long wrd);
+int lio_random_methods(long mask);
+
+#if CRAY
+#include <sys/iosw.h>
+int lio_wait4asyncio(int method, int fd, struct iosw **statptr);
+int lio_check_asyncio(char *io_type, int size, struct iosw *status);
+#endif /* CRAY */
+#ifdef sgi
+#include <aio.h>
+int lio_wait4asyncio(int method, int fd, aiocb_t *aiocbp);
+int lio_check_asyncio(char *io_type, int size, aiocb_t *aiocbp, int method);
+#endif /* sgi */
+
+/*
+ * Define the structure that contains the infomation that is used
+ * by the parsing and help functions.
+ */
+struct lio_info_type {
+ char *token;
+ int bits;
+ char *desc;
+};
+
+
diff --git a/include/write_log.h b/include/write_log.h
new file mode 100644
index 0000000..c97ef58
--- /dev/null
+++ b/include/write_log.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#ifndef _WRITE_LOG_H_
+#define _WRITE_LOG_H_
+
+/*
+ * Constants defining the max size of various wlog_rec fields. ANY SIZE
+ * CHANGES HERE MUST BE REFLECTED IN THE WLOG_REC_DISK STRUCTURE DEFINED
+ * BELOW.
+ */
+
+#define WLOG_MAX_PATH 128
+#define WLOG_MAX_PATTERN 64
+#define WLOG_MAX_HOST 8
+#define WLOG_REC_MAX_SIZE (sizeof(struct wlog_rec)+WLOG_MAX_PATH+WLOG_MAX_PATTERN+WLOG_MAX_HOST+2)
+
+/*
+ * User view of a write log record. Note that this is not necessiliary
+ * how the data is formatted on disk (signifigant compression occurrs), so
+ * don't expect to od the write log file and see things formatted this way.
+ */
+
+struct wlog_rec {
+ int w_pid; /* pid doing the write */
+ int w_offset; /* file offset */
+ int w_nbytes; /* # bytes written */
+ int w_oflags; /* low-order open() flags */
+ int w_done; /* 1 if io confirmed done */
+ int w_async; /* 1 if async write (writea) */
+
+ char w_host[WLOG_MAX_HOST+1]; /* host doing write - */
+ /* null terminated */
+ int w_hostlen; /* host name length */
+ char w_path[WLOG_MAX_PATH+1]; /* file written to - */
+ /* null terminated */
+ int w_pathlen; /* file name length */
+ char w_pattern[WLOG_MAX_PATTERN+1]; /* pattern written - */
+ /* null terminated */
+ int w_patternlen; /* pattern length */
+};
+
+#ifndef uint
+#define uint unsigned int
+#endif
+
+/*
+ * On-disk structure of a wlog_rec. Actually, the record consists of
+ * 3 parts: [wlog_rec_disk structure][variable length data][length]
+ * where length is a 2 byte field containing the total record length
+ * (including the 2 bytes). It is used for scanning the logfile in reverse
+ * order.
+ *
+ * The variable length data includes the path, host, and pattern (in that
+ * order). The lengths of these pieces of data are held in the
+ * wlog_rec_disk structure. Thus, the actual on-disk record looks like
+ * this (top is lower byte offset):
+ *
+ * struct wlog_rec_disk
+ * path (w_pathlen bytes - not null terminated)
+ * host (w_hostlen bytes - not null terminated)
+ * pattern (w_patternlen bytes - not null terminated)
+ * 2-byte record length
+ *
+ * Another way of looking at it is:
+ *
+ * <struct wlog_rec_disk><path (wpathlen bytes)>-->
+ * --><host (w_hostlen bytes)><pattern (w_patternlen bytes)><length (2 bytes)>
+ *
+ * The maximum length of this record is defined by the WLOG_REC_MAX_SIZE
+ * record. Note that the 2-byte record length forces this to be
+ * <= 64k bytes.
+ *
+ * Note that there is lots of bit-masking done here. The w_pathlen,
+ * w_hostlen, and w_patternlen fields MUST have enough bits to hold
+ * WLOG_MAX_PATH, WLOG_MAX_HOST, and WLOG_MAX_PATTERN bytes respectivly.
+ */
+
+struct wlog_rec_disk {
+#ifdef sgi /* sgi is pissy about fields > 32 bit, even cc -mips3 */
+ uint w_offset : 32; /* file offset */
+ uint w_extra0 : 32; /* EXTRA BITS IN WORD 0 */
+#endif
+#ifdef linux
+ uint w_offset : 32; /* file offset */
+ uint w_extra0 : 32; /* EXTRA BITS IN WORD 0 */
+#endif
+#ifdef CRAY
+ uint w_offset : 44; /* file offset */
+ uint w_extra0 : 20; /* EXTRA BITS IN WORD 0 */
+#endif
+
+ uint w_nbytes : 32; /* # bytes written */
+ uint w_oflags : 32; /* low-order open() flags */
+
+ uint w_pid : 17; /* pid doing the write */
+ uint w_pathlen : 7; /* length of file path */
+ uint w_patternlen: 6; /* length of pattern */
+ uint w_hostlen : 4; /* length of host */
+ uint w_done : 1; /* 1 if io confirmed done */
+ uint w_async : 1; /* 1 if async write (writea) */
+ uint w_extra2 : 28; /* EXTRA BITS IN WORD 2 */
+};
+
+/*
+ * write log file datatype. wlog_open() initializes this structure
+ * which is then passed around to the various wlog_xxx routines.
+ */
+
+struct wlog_file {
+ int w_afd; /* append fd */
+ int w_rfd; /* random-access fd */
+ char w_file[1024]; /* name of the write_log */
+};
+
+/*
+ * return value defines for the user-supplied function to
+ * wlog_scan_backward().
+ */
+
+#define WLOG_STOP_SCAN 0
+#define WLOG_CONTINUE_SCAN 1
+
+/*
+ * wlog prototypes
+ */
+
+#if __STDC__
+extern int wlog_open(struct wlog_file *wfile, int trunc, int mode);
+extern int wlog_close(struct wlog_file *wfile);
+extern int wlog_record_write(struct wlog_file *wfile,
+ struct wlog_rec *wrec, long offset);
+extern int wlog_scan_backward(struct wlog_file *wfile, int nrecs,
+ int (*func)(struct wlog_rec *rec),
+ long data);
+#else
+int wlog_open();
+int wlog_close();
+int wlog_record_write();
+int wlog_scan_backward();
+#endif
+
+extern char Wlog_Error_String[];
+
+#endif /* _WRITE_LOG_H_ */
+
diff --git a/lib/dataascii.c b/lib/dataascii.c
new file mode 100644
index 0000000..4b18e38
--- /dev/null
+++ b/lib/dataascii.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <stdio.h>
+#include <string.h>
+#include "dataascii.h"
+
+#define CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghjiklmnopqrstuvwxyz\n"
+#define CHARS_SIZE sizeof(CHARS)
+
+#ifdef UNIT_TEST
+#include <stdlib.h> /* malloc */
+#endif
+
+static char Errmsg[80];
+
+int
+dataasciigen(listofchars, buffer, bsize, offset)
+char *listofchars; /* a null terminated list of characters */
+char *buffer;
+int bsize;
+int offset;
+{
+ int cnt;
+ int total;
+ int ind; /* index into CHARS array */
+ char *chr;
+ int chars_size;
+ char *charlist;
+
+ chr=buffer;
+ total=offset+bsize;
+
+ if ( listofchars == NULL ) {
+ charlist=CHARS;
+ chars_size=CHARS_SIZE;
+ }
+ else {
+ charlist=listofchars;
+ chars_size=strlen(listofchars);
+ }
+
+ for(cnt=offset; cnt<total; cnt++) {
+ ind=cnt%chars_size;
+ *chr++=charlist[ind];
+ }
+
+ return bsize;
+
+} /* end of dataasciigen */
+
+int
+dataasciichk(listofchars, buffer, bsize, offset, errmsg)
+char *listofchars; /* a null terminated list of characters */
+char *buffer;
+int bsize;
+int offset;
+char **errmsg;
+{
+ int cnt;
+ int total;
+ int ind; /* index into CHARS array */
+ char *chr;
+ int chars_size;
+ char *charlist;
+
+ chr=buffer;
+ total=offset+bsize;
+
+ if ( listofchars == NULL ) {
+ charlist=CHARS;
+ chars_size=CHARS_SIZE;
+ }
+ else {
+ charlist=listofchars;
+ chars_size=strlen(listofchars);
+ }
+
+ if ( errmsg != NULL ) {
+ *errmsg = Errmsg;
+ }
+
+ for(cnt=offset; cnt<total; chr++, cnt++) {
+ ind=cnt%chars_size;
+ if ( *chr != charlist[ind] ) {
+ sprintf(Errmsg,
+ "data mismatch at offset %d, exp:%#o, act:%#o", cnt,
+ charlist[ind], *chr);
+ return cnt;
+ }
+ }
+
+ sprintf(Errmsg, "all %d bytes match desired pattern", bsize);
+ return -1; /* buffer is ok */
+
+} /* end of dataasciichk */
+
+
+#if UNIT_TEST
+
+/***********************************************************************
+ * main for doing unit testing
+ ***********************************************************************/
+int
+main(ac, ag)
+int ac;
+char **ag;
+{
+
+int size=1023;
+char *buffer;
+int ret;
+char *errmsg;
+
+ if ((buffer=(char *)malloc(size)) == NULL ) {
+ perror("malloc");
+ exit(2);
+ }
+
+ dataasciigen(NULL, buffer, size, 0);
+ printf("dataasciigen(NULL, buffer, %d, 0)\n", size);
+
+ ret=dataasciichk(NULL, buffer, size, 0, &errmsg);
+ printf("dataasciichk(NULL, buffer, %d, 0, &errmsg) returned %d %s\n",
+ size, ret, errmsg);
+
+ if ( ret == -1 )
+ printf("\tPASS return value is -1 as expected\n");
+ else
+ printf("\tFAIL return value is %d, expected -1\n", ret);
+
+ ret=dataasciichk(NULL, &buffer[1], size-1, 1, &errmsg);
+ printf("dataasciichk(NULL, &buffer[1], %d, 1, &errmsg) returned %d %s\n",
+ size-1, ret, errmsg);
+
+ if ( ret == -1 )
+ printf("\tPASS return value is -1 as expected\n");
+ else
+ printf("\tFAIL return value is %d, expected -1\n", ret);
+
+ buffer[25]= 0x0;
+ printf("changing char 25\n");
+
+ ret=dataasciichk(NULL, &buffer[1], size-1, 1, &errmsg);
+ printf("dataasciichk(NULL, &buffer[1], %d, 1, &errmsg) returned %d %s\n",
+ size-1, ret, errmsg);
+
+ if ( ret == 25 )
+ printf("\tPASS return value is 25 as expected\n");
+ else
+ printf("\tFAIL return value is %d, expected 25\n", ret);
+
+ dataasciigen("this is a test of the my string" , buffer, size, 0);
+ printf("dataasciigen(\"this is a test of the my string\", buffer, %d, 0)\n", size);
+
+ ret=dataasciichk("this is a test of the my string", buffer, size, 0, &errmsg);
+ printf("dataasciichk(\"this is a test of the my string\", buffer, %d, 0, &errmsg) returned %d %s\n",
+ size, ret, errmsg);
+
+ if ( ret == -1 )
+ printf("\tPASS return value is -1 as expected\n");
+ else
+ printf("\tFAIL return value is %d, expected -1\n", ret);
+
+ ret=dataasciichk("this is a test of the my string", &buffer[1], size-1, 1, &errmsg);
+ printf("dataasciichk(\"this is a test of the my string\", &buffer[1], %d, 1, &errmsg) returned %d %s\n",
+ size-1, ret, errmsg);
+
+ if ( ret == -1 )
+ printf("\tPASS return value is -1 as expected\n");
+ else
+ printf("\tFAIL return value is %d, expected -1\n", ret);
+
+ buffer[25]= 0x0;
+ printf("changing char 25\n");
+
+ ret=dataasciichk("this is a test of the my string", &buffer[1], size-1, 1, &errmsg);
+ printf("dataasciichk(\"this is a test of the my string\", &buffer[1], %d, 1, &errmsg) returned %d %s\n",
+ size-1, ret, errmsg);
+
+ if ( ret == 25 )
+ printf("\tPASS return value is 25 as expected\n");
+ else
+ printf("\tFAIL return value is %d, expected 25\n", ret);
+
+ exit(0);
+}
+
+#endif
+
diff --git a/lib/databin.c b/lib/databin.c
new file mode 100644
index 0000000..d11aa68
--- /dev/null
+++ b/lib/databin.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <stdio.h>
+#include <sys/param.h>
+#include <string.h> /* memset */
+#include <stdlib.h> /* rand */
+
+#if UNIT_TEST
+#include <malloc.h>
+#endif
+
+static char Errmsg[80];
+
+/*******************************************************************************
+* NAME
+* databingen - fill a buffer with a data pattern
+*
+* SYNOPSIS
+* (void) databingen(mode, buffer, bsize, offset)
+* int mode;
+* char *buffer;
+* int bsize;
+* int offset;
+*
+* DESCRIPTION
+* datagen fills the buffer pointed to by 'buffer' with 'bsize' bytes
+* of data of the form indicated by 'mode'.
+* All modes (expect r -random) are file offset based.
+* This allows more than process to do writing to the file without
+* corrupting it if the same modes were used.
+* They data modes to choose from, these are:
+*
+* 'a' - writes an alternating bit pattern (i.e. 0x5555555...)
+*
+* 'c' - writes a checkerboard pattern (i.e. 0xff00ff00ff00...)
+*
+* 'C' - writes counting pattern (i.e. 0 - 07, 0 - 07, ...);
+*
+* 'o' - writes all bits set (i.e. 0xffffffffffffff...)
+*
+* 'z' - writes all bits cleared (i.e. 0x000000000...);
+*
+* 'r' - writes random integers
+*
+* RETURN VALUE
+* None
+*
+*******************************************************************************/
+
+void
+databingen (mode, buffer, bsize, offset)
+int mode; /* either a, c, r, o, z or C */
+unsigned char *buffer; /* buffer pointer */
+int bsize; /* size of buffer */
+int offset; /* offset into the file where buffer starts */
+{
+int ind;
+
+ switch (mode)
+ {
+ default:
+ case 'a': /* alternating bit pattern */
+ memset(buffer,0x55,bsize);
+ break;
+
+ case 'c': /* checkerboard pattern */
+ memset(buffer,0xf0,bsize);
+ break;
+
+ case 'C': /* */
+ for (ind=0;ind< bsize;ind++) {
+ buffer[ind] = ((offset+ind)%8 & 0177);
+ }
+ break;
+
+ case 'o':
+ memset(buffer,0xff,bsize);
+ break;
+
+ case 'z':
+ memset(buffer,0x0,bsize);
+ break;
+
+ case 'r': /* random */
+ for (ind=0;ind< bsize;ind++) {
+ buffer[ind] = (rand () & 0177) | 0100;
+ }
+ }
+}
+
+/***********************************************************************
+ *
+ * return values:
+ * >= 0 : error at byte offset into the file, offset+buffer[0-(bsize-1)]
+ * < 0 : no error
+ ***********************************************************************/
+int
+databinchk(mode, buffer, bsize, offset, errmsg)
+int mode; /* either a, c, r, z, o, or C */
+unsigned char *buffer; /* buffer pointer */
+int bsize; /* size of buffer */
+int offset; /* offset into the file where buffer starts */
+char **errmsg;
+{
+ int cnt;
+ unsigned char *chr;
+ int total;
+ long expbits;
+ long actbits;
+
+ chr=buffer;
+ total=bsize;
+
+ if ( errmsg != NULL ) {
+ *errmsg = Errmsg;
+ }
+
+ switch (mode)
+ {
+ default:
+ case 'a': /* alternating bit pattern */
+ expbits=0x55;
+ break;
+
+ case 'c': /* checkerboard pattern */
+ expbits=0xf0;
+ break;
+
+ case 'C': /* counting pattern */
+ for (cnt=0;cnt< bsize;cnt++) {
+ expbits = ((offset+cnt)%8 & 0177);
+
+ if ( buffer[cnt] != expbits ) {
+ sprintf(Errmsg,
+ "data mismatch at offset %d, exp:%#lo, act:%#o",
+ offset+cnt, expbits, buffer[cnt]);
+ return offset+cnt;
+ }
+ }
+ sprintf(Errmsg, "all %d bytes match desired pattern", bsize);
+ return -1;
+
+ case 'o':
+ expbits=0xff;
+ break;
+
+ case 'z':
+ expbits=0;
+ break;
+
+ case 'r':
+ return -1; /* no check can be done for random */
+ }
+
+ for (cnt=0; cnt<bsize; chr++, cnt++) {
+ actbits = (long)*chr;
+
+ if ( actbits != expbits ) {
+ sprintf(Errmsg, "data mismatch at offset %d, exp:%#lo, act:%#lo",
+ offset+cnt, expbits, actbits);
+ return offset+cnt;
+ }
+ }
+
+ sprintf(Errmsg, "all %d bytes match desired pattern", bsize);
+ return -1; /* all ok */
+}
+
+#if UNIT_TEST
+
+/***********************************************************************
+ * main for doing unit testing
+ ***********************************************************************/
+int
+main(ac, ag)
+int ac;
+char **ag;
+{
+
+ int size=1023;
+ int offset;
+ int number;
+ unsigned char *buffer;
+ int ret;
+ char *errmsg;
+
+ if ((buffer=(unsigned char *)malloc(size)) == NULL ) {
+ perror("malloc");
+ exit(2);
+ }
+
+
+printf("***** for a ****************************\n");
+ databingen('a', buffer, size, 0);
+ printf("databingen('a', buffer, %d, 0)\n", size);
+
+ ret=databinchk('a', buffer, size, 0, &errmsg);
+ printf("databinchk('a', buffer, %d, 0, &errmsg) returned %d: %s\n",
+ size, ret, errmsg);
+ if ( ret == -1 )
+ printf("\tPASS return value of -1 as expected\n");
+ else
+ printf("\tFAIL return value %d, expected -1\n", ret);
+
+ offset=232400;
+ ret=databinchk('a', &buffer[1], size-1, offset, &errmsg);
+ printf("databinchk('a', &buffer[1], %d, %d, &errmsg) returned %d: %s\n",
+ size, offset, ret, errmsg);
+ if ( ret == -1 )
+ printf("\tPASS return value of -1 as expected\n");
+ else
+ printf("\tFAIL return value %d, expected -1\n", ret);
+
+ buffer[15]= 0x0;
+ printf("changing char 15 (offset (%d+15) = %d) to 0x0\n", offset, offset+15);
+ number=offset+15;
+
+ ret=databinchk('a', &buffer[1], size-1, offset+1, &errmsg);
+ printf("databinchk('a', &buffer[1], %d, %d, &errmsg) returned %d: %s\n",
+ size-1, offset+1, ret, errmsg);
+ if ( ret == number )
+ printf("\tPASS return value of %d as expected\n", number);
+ else
+ printf("\tFAIL return value %d, expected %d\n", ret, number);
+
+
+
+printf("***** for c ****************************\n");
+ databingen('c', buffer, size, 0);
+ printf("databingen('c', buffer, %d, 0)\n", size);
+
+ ret=databinchk('c', buffer, size, 0, &errmsg);
+ printf("databinchk('c', buffer, %d, 0, &errmsg) returned %d: %s\n",
+ size, ret, errmsg);
+ if ( ret == -1 )
+ printf("\tPASS return value of -1 as expected\n");
+ else
+ printf("\tFAIL return value %d, expected -1\n", ret);
+
+ offset=232400;
+ ret=databinchk('c', &buffer[1], size-1, offset, &errmsg);
+ printf("databinchk('c', &buffer[1], %d, %d, &errmsg) returned %d: %s\n",
+ size, offset, ret, errmsg);
+ if ( ret == -1 )
+ printf("\tPASS return value of -1 as expected\n");
+ else
+ printf("\tFAIL return value %d, expected -1\n", ret);
+
+ buffer[15]= 0x0;
+ printf("changing char 15 (offset (%d+15) = %d) to 0x0\n", offset, offset+15);
+ number=offset+15;
+
+ ret=databinchk('c', &buffer[1], size-1, offset+1, &errmsg);
+ printf("databinchk('c', &buffer[1], %d, %d, &errmsg) returned %d: %s\n",
+ size-1, offset+1, ret, errmsg);
+ if ( ret == number )
+ printf("\tPASS return value of %d as expected\n", number);
+ else
+ printf("\tFAIL return value %d, expected %d\n", ret, number);
+
+printf("***** for C ****************************\n");
+
+ databingen('C', buffer, size, 0);
+ printf("databingen('C', buffer, %d, 0)\n", size);
+
+ ret=databinchk('C', buffer, size, 0, &errmsg);
+ printf("databinchk('C', buffer, %d, 0, &errmsg) returned %d: %s\n",
+ size, ret, errmsg);
+ if ( ret == -1 )
+ printf("\tPASS return value of -1 as expected\n");
+ else
+ printf("\tFAIL return value %d, expected -1\n", ret);
+
+ offset=18;
+ ret=databinchk('C', &buffer[18], size-18, 18, &errmsg);
+ printf("databinchk('C', &buffer[18], %d, 18, &errmsg) returned %d: %s\n",
+ size-18, ret, errmsg);
+ if ( ret == -1 )
+ printf("\tPASS return value of -1 as expected\n");
+ else
+ printf("\tFAIL return value %d, expected -1\n", ret);
+
+ buffer[20]= 0x0;
+ buffer[21]= 0x0;
+ printf("changing char 20 and 21 to 0x0 (offset %d and %d)\n", 20,
+ 21);
+
+ ret=databinchk('C', &buffer[18], size-18, 18, &errmsg);
+ printf("databinchk('C', &buffer[18], %d, 18, &errmsg) returned %d: %s\n",
+ size-18, ret, errmsg);
+
+ if ( ret == 20 || ret == 21 )
+ printf("\tPASS return value of %d or %d as expected\n",
+ 20, 21);
+ else
+ printf("\tFAIL return value %d, expected %d or %d\n", ret,
+ 20, 21 );
+
+ exit(0);
+
+}
+
+#endif
+
diff --git a/lib/datapid.c b/lib/datapid.c
new file mode 100644
index 0000000..9414eae
--- /dev/null
+++ b/lib/datapid.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/************
+
+64 bits in a Cray word
+
+ 12345678901234567890123456789012
+1234567890123456789012345678901234567890123456789012345678901234
+________________________________________________________________
+< pid >< word-offset in file (same #) >< pid >
+
+1234567890123456789012345678901234567890123456789012345678901234
+________________________________________________________________
+< pid >< offset in file of this word >< pid >
+
+
+8 bits to a bytes == character
+ NBPW 8
+************/
+
+#include <stdio.h>
+#include <sys/param.h>
+#ifdef UNIT_TEST
+#include <unistd.h>
+#include <stdlib.h>
+#endif
+
+static char Errmsg[80];
+
+#define LOWER16BITS(X) (X & 0177777)
+#define LOWER32BITS(X) (X & 0xffffffff)
+
+/***
+#define HIGHBITS(WRD, bits) ( (-1 << (64-bits)) & WRD)
+#define LOWBITS(WRD, bits) ( (-1 >> (64-bits)) & WRD)
+****/
+
+#define NBPBYTE 8 /* number bits per byte */
+
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+/***********************************************************************
+ *
+ *
+ * 1 2 3 4 5 6 7 8 9 10 11 12 13 14 14 15 bytes
+ * 1234567890123456789012345678901234567890123456789012345678901234 bits
+ * ________________________________________________________________ 1 word
+ * < pid >< offset in file of this word >< pid >
+ *
+ * the words are put together where offset zero is the start.
+ * thus, offset 16 is the start of the second full word
+ * Thus, offset 8 is in middle of word 1
+ ***********************************************************************/
+int
+datapidgen(pid, buffer, bsize, offset)
+int pid;
+char *buffer;
+int bsize;
+int offset;
+{
+#if CRAY
+
+ int cnt;
+ int tmp;
+ char *chr;
+ long *wptr;
+ long word;
+ int woff; /* file offset for the word */
+ int boff; /* buffer offset or index */
+ int num_full_words;
+
+ num_full_words = bsize/NBPW;
+ boff = 0;
+
+ if ( cnt=(offset % NBPW) ) { /* partial word */
+
+ woff = offset - cnt;
+#if DEBUG
+printf("partial at beginning, cnt = %d, woff = %d\n", cnt, woff);
+#endif
+
+ word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+ chr = (char *)&word;
+
+ for (tmp=0; tmp<cnt; tmp++) { /* skip unused bytes */
+ chr++;
+ }
+
+ for (; boff<(NBPW-cnt) && boff<bsize; boff++, chr++) {
+ buffer[boff] = *chr;
+ }
+ }
+
+ /*
+ * full words
+ */
+
+ num_full_words = (bsize-boff)/NBPW;
+
+ woff = offset+boff;
+
+ for (cnt=0; cnt<num_full_words; woff += NBPW, cnt++ ) {
+
+ word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+ chr = (char *)&word;
+ for(tmp=0; tmp<NBPW; tmp++, chr++) {
+ buffer[boff++] = *chr;
+ }
+/****** Only if wptr is a word ellined
+ wptr = (long *)&buffer[boff];
+ *wptr = word;
+ boff += NBPW;
+*****/
+
+ }
+
+ /*
+ * partial word at end of buffer
+ */
+
+ if ( cnt=((bsize-boff) % NBPW) ) {
+#if DEBUG
+printf("partial at end\n");
+#endif
+ word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+ chr = (char *)&word;
+
+ for (tmp=0; tmp<cnt && boff<bsize; tmp++, chr++) {
+ buffer[boff++] = *chr;
+ }
+ }
+
+ return bsize;
+
+#else
+ return -1; /* not support on non-64 bits word machines */
+
+#endif
+
+}
+
+/***********************************************************************
+ *
+ *
+ ***********************************************************************/
+int
+datapidchk(pid, buffer, bsize, offset, errmsg)
+int pid;
+char *buffer;
+int bsize;
+int offset;
+char **errmsg;
+{
+#if CRAY
+
+ int cnt;
+ int tmp;
+ char *chr;
+ long *wptr;
+ long word;
+ int woff; /* file offset for the word */
+ int boff; /* buffer offset or index */
+ int num_full_words;
+
+
+ if ( errmsg != NULL ) {
+ *errmsg = Errmsg;
+ }
+
+
+ num_full_words = bsize/NBPW;
+ boff = 0;
+
+ if ( cnt=(offset % NBPW) ) { /* partial word */
+ woff = offset - cnt;
+ word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+ chr = (char *)&word;
+
+ for (tmp=0; tmp<cnt; tmp++) { /* skip unused bytes */
+ chr++;
+ }
+
+ for (; boff<(NBPW-cnt) && boff<bsize; boff++, chr++) {
+ if (buffer[boff] != *chr) {
+ sprintf(Errmsg, "Data mismatch at offset %d, exp:%#o, act:%#o",
+ offset+boff, *chr, buffer[boff]);
+ return offset+boff;
+ }
+ }
+ }
+
+ /*
+ * full words
+ */
+
+ num_full_words = (bsize-boff)/NBPW;
+
+ woff = offset+boff;
+
+ for (cnt=0; cnt<num_full_words; woff += NBPW, cnt++ ) {
+ word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+ chr = (char *)&word;
+ for(tmp=0; tmp<NBPW; tmp++, boff++, chr++) {
+ if ( buffer[boff] != *chr ) {
+ sprintf(Errmsg, "Data mismatch at offset %d, exp:%#o, act:%#o",
+ woff, *chr, buffer[boff]);
+ return woff;
+ }
+ }
+
+/****** only if a word elined
+ wptr = (long *)&buffer[boff];
+ if ( *wptr != word ) {
+ sprintf(Errmsg, "Data mismatch at offset %d, exp:%#o, act:%#o",
+ woff, word, *wptr);
+ return woff;
+ }
+ boff += NBPW;
+******/
+ }
+
+ /*
+ * partial word at end of buffer
+ */
+
+ if ( cnt=((bsize-boff) % NBPW) ) {
+#if DEBUG
+printf("partial at end\n");
+#endif
+ word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid));
+
+ chr = (char *)&word;
+
+
+ for (tmp=0; tmp<cnt && boff<bsize; boff++, tmp++, chr++) {
+ if ( buffer[boff] != *chr ) {
+ sprintf(Errmsg, "Data mismatch at offset %d, exp:%#o, act:%#o",
+ offset+boff, *chr, buffer[boff]);
+ return offset+boff;
+ }
+ }
+ }
+
+ sprintf(Errmsg, "all %d bytes match desired pattern", bsize);
+ return -1; /* buffer is ok */
+
+#else
+
+ if ( errmsg != NULL ) {
+ *errmsg = Errmsg;
+ }
+ sprintf(Errmsg, "Not supported on this OS.");
+ return 0;
+
+#endif
+
+
+} /* end of datapidchk */
+
+#if UNIT_TEST
+
+/***********************************************************************
+ * main for doing unit testing
+ ***********************************************************************/
+int
+main(ac, ag)
+int ac;
+char **ag;
+{
+
+int size=1234;
+char *buffer;
+int ret;
+char *errmsg;
+
+ if ((buffer=(char *)malloc(size)) == NULL ) {
+ perror("malloc");
+ exit(2);
+ }
+
+
+ datapidgen(-1, buffer, size, 3);
+
+/***
+fwrite(buffer, size, 1, stdout);
+fwrite("\n", 1, 1, stdout);
+****/
+
+ printf("datapidgen(-1, buffer, size, 3)\n");
+
+ ret=datapidchk(-1, buffer, size, 3, &errmsg);
+ printf("datapidchk(-1, buffer, %d, 3, &errmsg) returned %d %s\n",
+ size, ret, errmsg);
+ ret=datapidchk(-1, &buffer[1], size-1, 4, &errmsg);
+ printf("datapidchk(-1, &buffer[1], %d, 4, &errmsg) returned %d %s\n",
+ size-1, ret, errmsg);
+
+ buffer[25]= 0x0;
+ buffer[26]= 0x0;
+ buffer[27]= 0x0;
+ buffer[28]= 0x0;
+ printf("changing char 25-28\n");
+
+ ret=datapidchk(-1, &buffer[1], size-1, 4, &errmsg);
+ printf("datapidchk(-1, &buffer[1], %d, 4, &errmsg) returned %d %s\n",
+ size-1, ret, errmsg);
+
+printf("------------------------------------------\n");
+
+ datapidgen(getpid(), buffer, size, 5);
+
+/*******
+fwrite(buffer, size, 1, stdout);
+fwrite("\n", 1, 1, stdout);
+******/
+
+ printf("\ndatapidgen(getpid(), buffer, size, 5)\n");
+
+ ret=datapidchk(getpid(), buffer, size, 5, &errmsg);
+ printf("datapidchk(getpid(), buffer, %d, 5, &errmsg) returned %d %s\n",
+ size, ret, errmsg);
+
+ ret=datapidchk(getpid(), &buffer[1], size-1, 6, &errmsg);
+ printf("datapidchk(getpid(), &buffer[1], %d, 6, &errmsg) returned %d %s\n",
+ size-1, ret, errmsg);
+
+ buffer[25]= 0x0;
+ printf("changing char 25\n");
+
+ ret=datapidchk(getpid(), &buffer[1], size-1, 6, &errmsg);
+ printf("datapidchk(getpid(), &buffer[1], %d, 6, &errmsg) returned %d %s\n",
+ size-1, ret, errmsg);
+
+ exit(0);
+}
+
+#endif
+
diff --git a/lib/file_lock.c b/lib/file_lock.c
new file mode 100644
index 0000000..00eb582
--- /dev/null
+++ b/lib/file_lock.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/sysmacros.h>
+#include <string.h> /* memset, strerror */
+
+extern int errno;
+
+#ifndef EFSEXCLWR
+#define EFSEXCLWR 503
+#endif
+
+/*
+ * String containing the last system call.
+ *
+ */
+char Fl_syscall_str[128];
+
+static char errmsg[256];
+
+/***********************************************************************
+ *
+ * Test interface to the fcntl system call.
+ * It will loop if the LOCK_NB flags is NOT set.
+ ***********************************************************************/
+int
+file_lock(fd, flags, errormsg)
+int fd;
+int flags;
+char **errormsg;
+{
+ register int cmd, ret;
+ struct flock flocks;
+
+ memset(&flocks, 0, sizeof(struct flock));
+
+ if (flags&LOCK_NB)
+ cmd = F_SETLK;
+ else
+ cmd = F_SETLKW;
+
+ flocks.l_whence = 0;
+ flocks.l_start = 0;
+ flocks.l_len = 0;
+
+ if (flags&LOCK_UN)
+ flocks.l_type = F_UNLCK;
+ else if (flags&LOCK_EX)
+ flocks.l_type = F_WRLCK;
+ else if (flags&LOCK_SH)
+ flocks.l_type = F_RDLCK;
+ else {
+ errno = EINVAL;
+ if ( errormsg != NULL ) {
+ sprintf(errmsg,
+ "Programmer error, called file_lock with in valid flags\n");
+ *errormsg = errmsg;
+ }
+ return -1;
+ }
+
+ sprintf(Fl_syscall_str,
+ "fcntl(%d, %d, &flocks): type:%d whence:%d, start:%lld len:%lld\n",
+ fd, cmd, flocks.l_type, flocks.l_whence,
+ (long long)flocks.l_start, (long long)flocks.l_len);
+
+ while (1) {
+ ret = fcntl(fd, cmd, &flocks);
+
+ if ( ret < 0 ) {
+ if ( cmd == F_SETLK )
+ switch (errno) {
+ /* these errors are okay */
+ case EACCES: /* Permission denied */
+ case EINTR: /* interrupted system call */
+#ifdef EFILESH
+ case EFILESH: /* file shared */
+#endif
+ case EFSEXCLWR: /* File is write protected */
+ continue; /* retry getting lock */
+ }
+ if ( errormsg != NULL ) {
+ sprintf(errmsg, "fcntl(%d, %d, &flocks): errno:%d %s\n",
+ fd, cmd, errno, strerror(errno));
+ *errormsg = errmsg;
+ }
+ return -1;
+ }
+ break;
+ }
+
+ return ret;
+
+} /* end of file_lock */
+
+/***********************************************************************
+ *
+ * Test interface to the fcntl system call.
+ * It will loop if the LOCK_NB flags is NOT set.
+ ***********************************************************************/
+int
+record_lock(fd, flags, start, len, errormsg)
+int fd;
+int flags;
+int start;
+int len;
+char **errormsg;
+{
+ register int cmd, ret;
+ struct flock flocks;
+
+ memset(&flocks, 0, sizeof(struct flock));
+
+ if (flags&LOCK_NB)
+ cmd = F_SETLK;
+ else
+ cmd = F_SETLKW;
+
+ flocks.l_whence = 0;
+ flocks.l_start = start;
+ flocks.l_len = len;
+
+ if (flags&LOCK_UN)
+ flocks.l_type = F_UNLCK;
+ else if (flags&LOCK_EX)
+ flocks.l_type = F_WRLCK;
+ else if (flags&LOCK_SH)
+ flocks.l_type = F_RDLCK;
+ else {
+ errno = EINVAL;
+ if ( errormsg != NULL ) {
+ sprintf(errmsg,
+ "Programmer error, called record_lock with in valid flags\n");
+ *errormsg = errmsg;
+ }
+ return -1;
+ }
+
+ sprintf(Fl_syscall_str,
+ "fcntl(%d, %d, &flocks): type:%d whence:%d, start:%lld len:%lld\n",
+ fd, cmd, flocks.l_type, flocks.l_whence,
+ (long long)flocks.l_start, (long long)flocks.l_len);
+
+ while (1) {
+ ret = fcntl(fd, cmd, &flocks);
+
+ if ( ret < 0 ) {
+ if ( cmd == F_SETLK )
+ switch (errno) {
+ /* these errors are okay */
+ case EACCES: /* Permission denied */
+ case EINTR: /* interrupted system call */
+#ifdef EFILESH
+ case EFILESH: /* file shared */
+#endif
+ case EFSEXCLWR: /* File is write protected */
+ continue; /* retry getting lock */
+ }
+ if ( errormsg != NULL ) {
+ sprintf(errmsg, "fcntl(%d, %d, &flocks): errno:%d %s\n",
+ fd, cmd, errno, strerror(errno));
+ *errormsg = errmsg;
+ }
+ return -1;
+ }
+ break;
+ }
+
+ return ret;
+
+} /* end of record_lock */
+
+
diff --git a/lib/forker.c b/lib/forker.c
new file mode 100644
index 0000000..cc849a2
--- /dev/null
+++ b/lib/forker.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/**************************************************************
+ *
+ * OS Testing - Cray Research, Inc.
+ *
+ * FUNCTION NAME : forker
+ * background
+ *
+ * FUNCTION TITLE : fork desired number of copies of the current process
+ * fork a process and return control to caller
+ *
+ * SYNOPSIS:
+ * int forker(ncopies, mode, prefix)
+ * int ncopies;
+ * int mode;
+ * char *prefix;
+ *
+ * int background(prefix);
+ * char *prefix;
+ *
+ * extern int Forker_pids[];
+ * extern int Forker_npids;
+ *
+ * AUTHOR : Richard Logan
+ *
+ * CO-PILOT(s) : Dean Roehrich
+ *
+ * INITIAL RELEASE : UNICOS 8.0
+ *
+ * DESIGN DESCRIPTION
+ * The background function will do a fork of the current process.
+ * The parent process will then exit, thus orphaning the
+ * child process. Doing this will not nice the child process
+ * like executing a cmd in the background using "&" from the shell.
+ * If the fork fails and prefix is not NULL, a error message is printed
+ * to stderr and the process will exit with a value of errno.
+ *
+ * The forker function will fork <ncopies> minus one copies
+ * of the current process. There are two modes in how the forks
+ * will be done. Mode 0 (default) will have all new processes
+ * be childern of the parent process. Using Mode 1,
+ * the parent process will have one child and that child will
+ * fork the next process, if necessary, and on and on.
+ * The forker function will return the number of successful
+ * forks. This value will be different for the parent and each child.
+ * Using mode 0, the parent will get the total number of successful
+ * forks. Using mode 1, the newest child will get the total number
+ * of forks. The parent will get a return value of 1.
+ *
+ * The forker function also updates the global variables
+ * Forker_pids[] and Forker_npids. The Forker_pids array will
+ * be updated to contain the pid of each new process. The
+ * Forker_npids variable contains the number of entries
+ * in Forker_pids. Note, not all processes will have
+ * access to all pids via Forker_pids. If using mode 0, only the
+ * parent process and the last process will have all information.
+ * If using mode 1, only the last child process will have all information.
+ *
+ * If the prefix parameter is not NULL and the fork system call fails,
+ * a error message will be printed to stderr. The error message
+ * the be preceeded with prefix string. If prefix is NULL,
+ * no error message is printed.
+ *
+ * SPECIAL REQUIREMENTS
+ * None.
+ *
+ * UPDATE HISTORY
+ * This should contain the description, author, and date of any
+ * "interesting" modifications (i.e. info should helpful in
+ * maintaining/enhancing this module).
+ * username description
+ * ----------------------------------------------------------------
+ * rrl This functions will first written during
+ * the SFS testing days, 1993.
+ *
+ * BUGS/LIMITATIONS
+ * The child pids are stored in the fixed array, Forker_pids.
+ * The array only has space for 4098 pids. Only the first
+ * 4098 pids will be stored in the array.
+ *
+ **************************************************************/
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h> /* fork, getpid, sleep */
+#include <string.h>
+
+extern int errno;
+
+#define MAX_PIDS 4098
+
+int Forker_pids[MAX_PIDS]; /* holds pids of forked processes */
+int Forker_npids=0; /* number of entries in Forker_pids */
+
+/***********************************************************************
+ *
+ * This function will fork and the parent will exit zero and
+ * the child will return. This will orphan the returning process
+ * putting you in the background.
+ *
+ * Return Value
+ * 0 : if fork did not fail
+ * !0 : if fork failed, the return value will be the errno.
+ ***********************************************************************/
+int
+background(prefix)
+char *prefix;
+{
+ switch (fork()) {
+ case -1:
+ if ( prefix != NULL )
+ fprintf(stderr, "%s: In %s background(), fork() failed, errno:%d %s\n",
+ prefix, __FILE__, errno, strerror(errno));
+ exit(errno);
+
+ case 0: /* child process */
+ break;
+
+ default:
+ exit(0);
+ }
+
+ return 0;
+
+} /* end of background */
+
+/***********************************************************************
+ * Forker will fork ncopies-1 copies of self.
+ *
+ ***********************************************************************/
+int
+forker(ncopies, mode, prefix)
+int ncopies;
+int mode; /* 0 - all childern of parent, 1 - only 1 direct child */
+char *prefix; /* if ! NULL, an message will be printed to stderr */
+ /* if fork fails. The prefix (program name) will */
+ /* preceed the message */
+{
+ int cnt;
+ int pid;
+ static int ind = 0;
+
+ Forker_pids[ind]=0;
+
+ for ( cnt=1; cnt < ncopies; cnt++ ) {
+
+ switch ( mode ) {
+ case 1 : /* only 1 direct child */
+ if ( (pid = fork()) == -1 ) {
+ if ( prefix != NULL )
+ fprintf(stderr, "%s: %s,forker(): fork() failed, errno:%d %s\n",
+ prefix, __FILE__, errno, strerror(errno));
+ return 0;
+ }
+ Forker_npids++;
+
+ switch (pid ) {
+ case 0: /* child - continues the forking */
+
+ if ( Forker_npids < MAX_PIDS )
+ Forker_pids[Forker_npids-1]=getpid();
+ break;
+
+ default: /* parent - stop the forking */
+ if ( Forker_npids < MAX_PIDS )
+ Forker_pids[Forker_npids-1]=pid;
+ return cnt-1;
+ }
+
+ break;
+
+ default : /* all new processes are childern of parent */
+ if ( (pid = fork()) == -1 ) {
+ if ( prefix != NULL )
+ fprintf(stderr, "%s: %s,forker(): fork() failed, errno:%d %s\n",
+ prefix, __FILE__, errno, strerror(errno));
+ return cnt-1;
+ }
+ Forker_npids++;
+
+ switch (pid ) {
+ case 0: /* child - stops the forking */
+ if ( Forker_npids < MAX_PIDS )
+ Forker_pids[Forker_npids-1]=getpid();
+ return cnt;
+
+ default: /* parent - continues the forking */
+ if ( Forker_npids < MAX_PIDS )
+ Forker_pids[Forker_npids-1]=pid;
+ break;
+ }
+ break;
+ }
+ }
+
+ if ( Forker_npids < MAX_PIDS )
+ Forker_pids[Forker_npids]=0;
+ return cnt-1;
+
+} /* end of forker */
+
+
+#if UNIT_TEST
+
+/*
+ * The following is a unit test main for the background and forker
+ * functions.
+ */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ int ncopies=1;
+ int mode=0;
+ int ret;
+ int ind;
+
+ if ( argc == 1 ) {
+ printf("Usage: %s ncopies [mode]\n", argv[0]);
+ exit(1);
+ }
+
+ if ( sscanf(argv[1], "%i", &ncopies) != 1 ) {
+ printf("%s: ncopies argument must be integer\n", argv[0]);
+ exit(1);
+ }
+
+ if ( argc == 3 )
+ if ( sscanf(argv[2], "%i", &mode) != 1 ) {
+ printf("%s: mode argument must be integer\n", argv[0]);
+ exit(1);
+ }
+
+ printf("Starting Pid = %d\n", getpid());
+ ret=background(argv[0]);
+ printf("After background() ret:%d, pid = %d\n", ret, getpid());
+
+ ret=forker(ncopies, mode, argv[0]);
+
+ printf("forker(%d, %d, %s) ret:%d, pid = %d, sleeping 30 seconds.\n",
+ ncopies, mode, argv[0], ret, getpid());
+
+ printf("%d My version of Forker_pids[], Forker_npids = %d\n",
+ getpid(), Forker_npids);
+
+ for (ind=0; ind<Forker_npids; ind++){
+ printf("%d ind:%-2d pid:%d\n", getpid(), ind, Forker_pids[ind]);
+ }
+
+ sleep(30);
+ exit(0);
+}
+
+#endif /* UNIT_TEST */
diff --git a/lib/open_flags.c b/lib/open_flags.c
new file mode 100644
index 0000000..0fa76e9
--- /dev/null
+++ b/lib/open_flags.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/**************************************************************
+ *
+ * OS Testing - Cray Research, Inc.
+ *
+ * FUNCTION NAME : parse_open_flags
+ * openflags2symbols
+ *
+ * FUNCTION TITLE : converts open flag symbols into bitmask
+ * converts open flag bitmask into symbols
+ *
+ * SYNOPSIS:
+ * int parse_open_flags(symbols, badname)
+ * char *symbols;
+ * char **badname;
+ *
+ * char *openflags2symbols(openflags, sep, mode)
+ * int openflags;
+ * char *sep;
+ * int mode;
+ *
+ * AUTHOR : Richard Logan
+ *
+ * CO-PILOT(s) : Dean Roehrich
+ *
+ * INITIAL RELEASE : UNICOS 8.0
+ *
+ * DESIGN DESCRIPTION
+ * The parse_open_flags function can be used to convert
+ * a list of comma separated open(2) flag symbols (i.e. O_TRUNC)
+ * into the bitmask that can be used by open(2).
+ * If a symbol is unknown and <badname> is not NULL, <badname>
+ * will updated to point that symbol in <string>.
+ * Parse_open_flags will return -1 on this error.
+ * Otherwise parse_open_flags will return the open flag bitmask.
+ * If parse_open_flags returns, <string> will left unchanged.
+ *
+ * The openflags2symbols function attempts to convert open flag
+ * bits into human readable symbols (i.e. O_TRUNC). If there
+ * are more than one symbol, the <sep> string will be placed as
+ * a separator between symbols. Commonly used separators would
+ * be a comma "," or pipe "|". If <mode> is one and not all
+ * <openflags> bits can be converted to symbols, the "UNKNOWN"
+ * symbol will be added to return string.
+ * Openflags2symbols will return the indentified symbols.
+ * If no symbols are recognized the return value will be a empty
+ * string or the "UNKNOWN" symbol.
+ *
+ * SPECIAL REQUIREMENTS
+ * None.
+ *
+ * UPDATE HISTORY
+ * This should contain the description, author, and date of any
+ * "interesting" modifications (i.e. info should helpful in
+ * maintaining/enhancing this module).
+ * username description
+ * ----------------------------------------------------------------
+ * rrl This code was first created during the beginning
+ * of the SFS testing days. I think that was in 1993.
+ * This code was updated in 05/96.
+ * (05/96) openflags2symbols was written.
+ *
+ * BUGS/LIMITATIONS
+ * Currently (05/96) all known symbols are coded into openflags2symbols.
+ * If new open flags are added this code will have to updated
+ * to know about them or they will not be recognized.
+ *
+ **************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <string.h> /* strcat */
+#include "open_flags.h"
+
+#define UNKNOWN_SYMBOL "UNKNOWN"
+
+static char Open_symbols[512]; /* space for openflags2symbols return value */
+
+struct open_flag_t {
+ char *symbol;
+ int flag;
+};
+
+static struct open_flag_t Open_flags[] = {
+ { "O_RDONLY", O_RDONLY },
+ { "O_WRONLY", O_WRONLY },
+ { "O_RDWR", O_RDWR },
+ { "O_SYNC", O_SYNC },
+ { "O_CREAT", O_CREAT },
+ { "O_TRUNC", O_TRUNC },
+ { "O_EXCL", O_EXCL },
+ { "O_APPEND", O_APPEND },
+ { "O_NONBLOCK", O_NONBLOCK },
+#if O_NOCTTY
+ { "O_NOCTTY", O_NOCTTY },
+#endif
+#if O_DSYNC
+ { "O_DSYNC", O_DSYNC },
+#endif
+#if O_RSYNC
+ { "O_RSYNC", O_RSYNC },
+#endif
+#if O_ASYNC
+ { "O_ASYNC", O_ASYNC },
+#endif
+#if O_PTYIGN
+ { "O_PTYIGN", O_PTYIGN },
+#endif
+#if O_NDELAY
+ { "O_NDELAY", O_NDELAY },
+#endif
+#if O_RAW
+ { "O_RAW", O_RAW },
+#endif
+#ifdef O_SSD
+ { "O_SSD", O_SSD },
+#endif
+#if O_BIG
+ { "O_BIG", O_BIG },
+#endif
+#if O_PLACE
+ { "O_PLACE", O_PLACE },
+#endif
+#if O_RESTART
+ { "O_RESTART", O_RESTART },
+#endif
+#if O_SFSXOP
+ { "O_SFSXOP", O_SFSXOP },
+#endif
+#if O_SFS_DEFER_TM
+ { "O_SFS_DEFER_TM", O_SFS_DEFER_TM },
+#endif
+#if O_WELLFORMED
+ { "O_WELLFORMED", O_WELLFORMED },
+#endif
+#if O_LDRAW
+ { "O_LDRAW", O_LDRAW },
+#endif
+#if O_T3D
+ { "O_T3D", O_T3D },
+#endif /* O_T3D */
+#if O_PARALLEL
+ { "O_PARALLEL", O_PARALLEL },
+ { "O_FSA", O_PARALLEL|O_WELLFORMED|O_RAW }, /* short cut */
+#endif /* O_PARALLEL */
+#ifdef O_LARGEFILE
+ { "O_LARGEFILE", O_LARGEFILE },
+#endif
+#ifdef O_DIRECT
+ { "O_DIRECT", O_DIRECT },
+#endif
+#ifdef O_PRIV
+ { "O_PRIV", O_PRIV },
+#endif
+
+};
+
+int
+parse_open_flags(char *string, char **badname)
+{
+ int bits = 0;
+ char *name;
+ char *cc;
+ char savecc;
+ int found;
+ int ind;
+
+ name=string;
+ cc=name;
+
+ while ( 1 ) {
+
+ for(; ((*cc != ',') && (*cc != '\0')); cc++);
+ savecc = *cc;
+ *cc = '\0';
+
+ found = 0;
+
+ for(ind=0; ind < sizeof(Open_flags)/sizeof(struct open_flag_t); ind++) {
+ if ( strcmp(name, Open_flags[ind].symbol) == 0 ) {
+ bits |= Open_flags[ind].flag;
+ found=1;
+ break;
+ }
+ }
+
+ *cc = savecc; /* restore string */
+
+ if ( found == 0 ) { /* invalid name */
+ if ( badname != NULL )
+ *badname = name;
+ return -1;
+ }
+
+ if ( savecc == '\0' )
+ break;
+
+ name = ++cc;
+
+ } /* end while */
+
+ return bits;
+
+} /* end of parse_open_flags */
+
+
+char *
+openflags2symbols(int openflags, char *sep, int mode)
+{
+ int ind;
+ int size;
+ int bits = openflags;
+ int havesome=0;
+
+ Open_symbols[0]='\0';
+
+ size=sizeof(Open_flags)/sizeof(struct open_flag_t);
+
+ /*
+ * Deal with special case of O_RDONLY. If O_WRONLY nor O_RDWR
+ * bits are not set, assume O_RDONLY.
+ */
+
+ if ( (bits & (O_WRONLY | O_RDWR)) == 0 ) {
+ strcat(Open_symbols, "O_RDONLY");
+ havesome=1;
+ }
+
+ /*
+ * Loop through all but O_RDONLY elments of Open_flags
+ */
+ for(ind=1; ind < size; ind++) {
+
+ if ( (bits & Open_flags[ind].flag) == Open_flags[ind].flag ) {
+ if ( havesome )
+ strcat(Open_symbols, sep);
+
+ strcat(Open_symbols, Open_flags[ind].symbol);
+ havesome++;
+
+ /* remove flag bits from bits */
+ bits = bits & (~Open_flags[ind].flag);
+ }
+ }
+
+ /*
+ * If not all bits were identified and mode was equal to 1,
+ * added UNKNOWN_SYMBOL to return string
+ */
+ if ( bits && mode == 1 ) { /* not all bits were identified */
+ if ( havesome )
+ strcat(Open_symbols, sep);
+ strcat(Open_symbols, UNKNOWN_SYMBOL);
+ }
+
+ return Open_symbols;
+
+} /* end of openflags2symbols */
+
+
+#ifdef UNIT_TEST
+
+/*
+ * The following code provides a UNIT test main for
+ * parse_open_flags and openflags2symbols functions.
+ */
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ int bits;
+ int ret;
+ char *err;
+
+ if (argc == 1 ) {
+ printf("Usage: %s openflagsbits\n\t%s symbols\n", argv[0], argv[0]);
+ exit(1);
+ }
+
+ if ( sscanf(argv[1], "%i", &bits) == 1 ) {
+ printf("openflags2symbols(%#o, \",\", 1) returned %s\n",
+ bits, openflags2symbols(bits, ",", 1));
+
+ } else {
+ ret=parse_open_flags(argv[1], &err);
+ if ( ret == -1 )
+ printf("parse_open_flags(%s, &err) returned -1, err = %s\n",
+ argv[0], err);
+ else
+ printf("parse_open_flags(%s, &err) returned %#o\n", argv[0], ret);
+ }
+
+ exit(0);
+}
+
+#endif /* end of UNIT_TEST */
diff --git a/lib/pattern.c b/lib/pattern.c
new file mode 100644
index 0000000..43528ae
--- /dev/null
+++ b/lib/pattern.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ * The routines in this module are used to fill/check a data buffer
+ * with/against a known pattern.
+ */
+
+/*
+ * pattern_check(buf, buflen, pat, patlen, patshift)
+ *
+ * Check a buffer of length buflen against repeated occurrances of
+ * a pattern whose length is patlen. Patshift can be used to rotate
+ * the pattern by patshift bytes to the left.
+ *
+ * Patshift may be greater than patlen, the pattern will be rotated by
+ * (patshift % patshift) bytes.
+ *
+ * pattern_check returns -1 if the buffer does not contain repeated
+ * occurrances of the indicated pattern (shifted by patshift).
+ *
+ * The algorithm used to check the buffer relies on the fact that buf is
+ * supposed to be repeated copies of pattern. The basic algorithm is
+ * to validate the first patlen bytes of buf against the pat argument
+ * passed in - then validate the next patlen bytes against the 1st patlen
+ * bytes - the next (2*patlen) bytes against the 1st (2*pathen) bytes, and
+ * so on. This algorithm only works when the assumption of a buffer full
+ * of repeated copies of a pattern holds, and gives MUCH better results
+ * then walking the buffer byte by byte.
+ *
+ * Performance wise, It appears to be about 5% slower than doing a straight
+ * memcmp of 2 buffers, but the big win is that it does not require a
+ * 2nd comparison buffer, only the pattern.
+ */
+
+#include <string.h>
+
+int
+pattern_check(buf, buflen, pat, patlen, patshift)
+char *buf;
+int buflen;
+char *pat;
+int patlen;
+int patshift;
+{
+ int nb, ncmp, nleft;
+ char *cp;
+
+ if (patlen)
+ patshift = patshift % patlen;
+
+ cp = buf;
+ nleft = buflen;
+
+ /*
+ * The following 2 blocks of code are to compare the first patlen
+ * bytes of buf. We need 2 checks if patshift is > 0 since we
+ * must check the last (patlen - patshift) bytes, and then the
+ * first (patshift) bytes.
+ */
+
+ nb = patlen - patshift;
+ if (nleft < nb) {
+ return (memcmp(cp, pat + patshift, nleft) ? -1 : 0);
+ } else {
+ if (memcmp(cp, pat + patshift, nb))
+ return -1;
+
+ nleft -= nb;
+ cp += nb;
+ }
+
+ if (patshift > 0) {
+ nb = patshift;
+ if (nleft < nb) {
+ return (memcmp(cp, pat, nleft) ? -1 : 0);
+ } else {
+ if (memcmp(cp, pat, nb))
+ return -1;
+
+ nleft -= nb;
+ cp += nb;
+ }
+ }
+
+ /*
+ * Now, verify the rest of the buffer using the algorithm described
+ * in the function header.
+ */
+
+ ncmp = cp - buf;
+ while (ncmp < buflen) {
+ nb = (ncmp < nleft) ? ncmp : nleft;
+ if (memcmp(buf, cp, nb))
+ return -1;
+
+ cp += nb;
+ ncmp += nb;
+ nleft -= nb;
+ }
+
+ return 0;
+}
+
+/*
+ * pattern_fill(buf, buflen, pat, patlen, patshift)
+ *
+ * Fill a buffer of length buflen with repeated occurrances of
+ * a pattern whose length is patlen. Patshift can be used to rotate
+ * the pattern by patshift bytes to the left.
+ *
+ * Patshift may be greater than patlen, the pattern will be rotated by
+ * (patshift % patlen) bytes.
+ *
+ * If buflen is not a multiple of patlen, a partial pattern will be written
+ * in the last part of the buffer. This implies that a buffer which is
+ * shorter than the pattern length will receive only a partial pattern ...
+ *
+ * pattern_fill always returns 0 - no validation of arguments is done.
+ *
+ * The algorithm used to fill the buffer relies on the fact that buf is
+ * supposed to be repeated copies of pattern. The basic algorithm is
+ * to fill the first patlen bytes of buf with the pat argument
+ * passed in - then copy the next patlen bytes with the 1st patlen
+ * bytes - the next (2*patlen) bytes with the 1st (2*pathen) bytes, and
+ * so on. This algorithm only works when the assumption of a buffer full
+ * of repeated copies of a pattern holds, and gives MUCH better results
+ * then filling the buffer 1 byte at a time.
+ */
+
+int
+pattern_fill(buf, buflen, pat, patlen, patshift)
+char *buf;
+int buflen;
+char *pat;
+int patlen;
+int patshift;
+{
+ int trans, ncopied, nleft;
+ char *cp;
+
+ if (patlen)
+ patshift = patshift % patlen;
+
+ cp = buf;
+ nleft = buflen;
+
+ /*
+ * The following 2 blocks of code are to fill the first patlen
+ * bytes of buf. We need 2 sections if patshift is > 0 since we
+ * must first copy the last (patlen - patshift) bytes into buf[0]...,
+ * and then the first (patshift) bytes of pattern following them.
+ */
+
+ trans = patlen - patshift;
+ if (nleft < trans) {
+ memcpy(cp, pat + patshift, nleft);
+ return 0;
+ } else {
+ memcpy(cp, pat + patshift, trans);
+ nleft -= trans;
+ cp += trans;
+ }
+
+ if (patshift > 0) {
+ trans = patshift;
+ if (nleft < trans) {
+ memcpy(cp, pat, nleft);
+ return 0;
+ } else {
+ memcpy(cp, pat, trans);
+ nleft -= trans;
+ cp += trans;
+ }
+ }
+
+ /*
+ * Now, fill the rest of the buffer using the algorithm described
+ * in the function header comment.
+ */
+
+ ncopied = cp - buf;
+ while (ncopied < buflen) {
+ trans = (ncopied < nleft) ? ncopied : nleft;
+ memcpy(cp, buf, trans);
+ cp += trans;
+ ncopied += trans;
+ nleft -= trans;
+ }
+
+ return(0);
+}
diff --git a/lib/random_range.c b/lib/random_range.c
new file mode 100644
index 0000000..124cd79
--- /dev/null
+++ b/lib/random_range.c
@@ -0,0 +1,917 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include "random_range.h"
+
+/*
+ * Internal format of the range array set up by parse_range()
+ */
+
+struct range {
+ int min;
+ int max;
+ int mult;
+};
+
+/*
+ * parse_ranges() is a function to parse a comma-separated list of range
+ * tokens each having the following form:
+ *
+ * num
+ * or
+ * min:max[:mult]
+ *
+ * any of the values may be blank (ie. min::mult, :max, etc.) and default
+ * values for missing arguments may be supplied by the caller.
+ *
+ * The special first form is short hand for 'num:num'.
+ *
+ * After parsing the string, the ranges are put into an array of integers,
+ * which is malloc'd by the routine. The min, max, and mult entries of each
+ * range can be extracted from the array using the range_min(), range_max(),
+ * and range_mult() functions.
+ *
+ * It is the responsibility of the caller to free the space allocated by
+ * parse_ranges() - a single call to free() will free the space.
+ *
+ * str The string to parse - assumed to be a comma-separated
+ * list of tokens having the above format.
+ * defmin default value to plug in for min, if it is missing
+ * defmax default value to plug in for max, if it is missing
+ * defmult default value to plug in for mult, if missing
+ * parse_func A user-supplied function pointer, which parse_ranges()
+ * can call to parse the min, max, and mult strings. This
+ * allows for customized number formats. The function
+ * MUST have the following prototype:
+ * parse_func(char *str, int *val)
+ * The function should return -1 if str cannot be parsed
+ * into an integer, or >= 0 if it was successfully
+ * parsed. The resulting integer will be stored in
+ * *val. If parse_func is NULL, parse_ranges will parse
+ * the tokens in a manner consistent with the the sscanf
+ * %i format.
+ * range_ptr A user-supplied char **, which will be set to point
+ * at malloc'd space which holds the parsed range
+ * values. If range_ptr is NULL, parse_ranges() just
+ * parses the string. The data returned in range_ptr
+ * should not be processed directly - use the functions
+ * range_min(), range_max(), and range_mult() to access
+ * data for a given range.
+ * errptr user-supplied char ** which can be set to point to a
+ * static error string. If errptr is NULL, it is ignored.
+ *
+ * parse_range() returns -1 on error, or the number of ranges parsed.
+ */
+
+static int str_to_int();
+static long long divider(long long, long long, long long, long long);
+
+int
+parse_ranges(str, defmin, defmax, defmult, parse_func, rangeptr, errptr)
+char *str;
+int defmin;
+int defmax;
+int defmult;
+int (*parse_func)();
+char **rangeptr;
+char **errptr;
+{
+ int ncommas;
+ char *tmpstr, *cp, *tok, *n1str, *n2str, *multstr;
+ struct range *rp, *ranges;
+ static char errmsg[256];
+
+ if (errptr != NULL) {
+ *errptr = errmsg;
+ }
+
+ for (ncommas = 0, cp = str; *cp != '\0'; cp++) {
+ if (*cp == ',') {
+ ncommas++;
+ }
+ }
+
+ if (parse_func == NULL) {
+ parse_func = str_to_int;
+ }
+
+ tmpstr = strdup(str);
+ ranges = (struct range *)malloc((ncommas+1) * sizeof(struct range));
+ rp = ranges;
+
+ tok = strtok(tmpstr, ",");
+ while (tok != NULL) {
+ n1str = tok;
+ n2str = NULL;
+ multstr = NULL;
+
+ rp->min = defmin;
+ rp->max = defmax;
+ rp->mult = defmult;
+
+ if ((cp = strchr(n1str, ':')) != NULL) {
+ *cp = '\0';
+ n2str = cp+1;
+
+ if ((cp = strchr(n2str, ':')) != NULL) {
+ *cp = '\0';
+ multstr = cp+1;
+ }
+ }
+
+ /*
+ * Parse the 'min' field - if it is zero length (:n2[:mult]
+ * format), retain the default value, otherwise, pass the
+ * string to the parse function.
+ */
+
+ if ((int)strlen(n1str) > 0) {
+ if ((*parse_func)(n1str, &rp->min) < 0) {
+ sprintf(errmsg, "error parsing string %s into an integer", n1str);
+ free(tmpstr);
+ free(ranges);
+ return -1;
+ }
+ }
+
+ /*
+ * Process the 'max' field - if one was not present (n1 format)
+ * set max equal to min. If the field was present, but
+ * zero length (n1: format), retain the default. Otherwise
+ * pass the string to the parse function.
+ */
+
+ if (n2str == NULL) {
+ rp->max = rp->min;
+ } else if ((int)strlen(n2str) > 0) {
+ if ((*parse_func)(n2str, &rp->max) < 0) {
+ sprintf(errmsg, "error parsing string %s into an integer", n2str);
+ free(tmpstr);
+ free(ranges);
+ return -1;
+ }
+ }
+
+ /*
+ * Process the 'mult' field - if one was not present
+ * (n1:n2 format), or the field was zero length (n1:n2: format)
+ * then set the mult field to defmult - otherwise pass then
+ * mult field to the parse function.
+ */
+
+ if (multstr != NULL && (int)strlen(multstr) > 0) {
+ if ((*parse_func)(multstr, &rp->mult) < 0) {
+ sprintf(errmsg, "error parsing string %s into an integer", multstr);
+ free(tmpstr);
+ free(ranges);
+ return -1;
+ }
+ }
+
+ rp++;
+ tok = strtok(NULL, ",");
+ }
+
+ free(tmpstr);
+
+ if (rangeptr != NULL) {
+ *rangeptr = (char *)ranges;
+ } else {
+ free(ranges); /* just running in parse mode */
+ }
+
+ return (rp - ranges);
+}
+
+/*
+ * The default integer-parsing function
+ */
+
+static int
+str_to_int(str, ip)
+char *str;
+int *ip;
+{
+ char c;
+
+ if (sscanf(str, "%i%c", ip, &c) != 1) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Three simple functions to return the min, max, and mult values for a given
+ * range. It is assumed that rbuf is a range buffer set up by parse_ranges(),
+ * and that r is a valid range within that buffer.
+ */
+
+int
+range_min(rbuf, r)
+char *rbuf;
+int r;
+{
+ return ((struct range *)rbuf)[r].min;
+}
+
+int
+range_max(rbuf, r)
+char *rbuf;
+int r;
+{
+ return ((struct range *)rbuf)[r].max;
+}
+
+int
+range_mult(rbuf, r)
+char *rbuf;
+int r;
+{
+ return ((struct range *)rbuf)[r].mult;
+}
+
+/*****************************************************************************
+ * random_range(int start, int end, int mult, char **errp)
+ *
+ * Returns a psuedo-random number which is >= 'start', <= 'end', and a multiple
+ * of 'mult'. Start and end may be any valid integer, but mult must be an
+ * integer > 0. errp is a char ** which will be set to point to a static
+ * error message buffer if it is not NULL, and an error occurs.
+ *
+ * The errp is the only way to check if the routine fails - currently the only
+ * failure conditions are:
+ *
+ * mult < 1
+ * no numbers in the start-end range that are a multiple of 'mult'
+ *
+ * If random_range_fails, and errp is a valid pointer, it will point to an
+ * internal error buffer. If errp is a vaild pointer, and random_range
+ * is successful, errp will be set to NULL.
+ *
+ * Note - if mult is 1 (the most common case), there are error conditions
+ * possible, and errp need not be used.
+ *
+ * Note: Uses lrand48(), assuming that set_random_seed() uses srand48() when
+ * setting the seed.
+ *****************************************************************************/
+
+long
+random_range(min, max, mult, errp)
+int min;
+int max;
+int mult;
+char **errp;
+{
+ int r, nmults, orig_min, orig_max, orig_mult, tmp;
+ extern long lrand48();
+ static char errbuf[128];
+
+ /*
+ * Sanity check
+ */
+
+ if (mult < 1) {
+ if (errp != NULL) {
+ sprintf(errbuf, "mult arg must be greater than 0");
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ /*
+ * Save original parameter values for use in error message
+ */
+
+ orig_min = min;
+ orig_max = max;
+ orig_mult = mult;
+
+ /*
+ * switch min/max if max < min
+ */
+
+ if (max < min) {
+ tmp = max;
+ max = min;
+ min = tmp;
+ }
+
+ /*
+ * select the random number
+ */
+
+ if ((r = min % mult)) /* bump to the next higher 'mult' multiple */
+ min += mult - r;
+
+ if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */
+ max -= r;
+
+ if (min > max) { /* no 'mult' multiples between min & max */
+ if (errp != NULL) {
+ sprintf(errbuf, "no numbers in the range %d:%d that are a multiple of %d", orig_min, orig_max, orig_mult);
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ if (errp != NULL) {
+ *errp = NULL;
+ }
+
+ nmults = ((max - min) / mult) + 1;
+#if CRAY
+ /*
+ * If max is less than 2gb, then the value can fit in 32 bits
+ * and the standard lrand48() routine can be used.
+ */
+ if ( max <= (long)2147483647 ) {
+ return (long) (min + (((long)lrand48() % nmults) * mult));
+ } else {
+ /*
+ * max is greater than 2gb - meeds more than 32 bits.
+ * Since lrand48 only will get a number up to 32bits.
+ */
+ long randnum;
+ randnum=divider(min, max, 0, -1);
+ return (long) (min + ((randnum % nmults) * mult));
+ }
+
+#else
+ return (min + ((lrand48() % nmults) * mult));
+#endif
+
+}
+
+/*
+ * Just like random_range, but all values are longs.
+ */
+long
+random_rangel(min, max, mult, errp)
+long min;
+long max;
+long mult;
+char **errp;
+{
+ long r, nmults, orig_min, orig_max, orig_mult, tmp;
+ extern long lrand48();
+ static char errbuf[128];
+
+ /*
+ * Sanity check
+ */
+
+ if (mult < 1) {
+ if (errp != NULL) {
+ sprintf(errbuf, "mult arg must be greater than 0");
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ /*
+ * Save original parameter values for use in error message
+ */
+
+ orig_min = min;
+ orig_max = max;
+ orig_mult = mult;
+
+ /*
+ * switch min/max if max < min
+ */
+
+ if (max < min) {
+ tmp = max;
+ max = min;
+ min = tmp;
+ }
+
+ /*
+ * select the random number
+ */
+
+ if ((r = min % mult)) /* bump to the next higher 'mult' multiple */
+ min += mult - r;
+
+ if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */
+ max -= r;
+
+ if (min > max) { /* no 'mult' multiples between min & max */
+ if (errp != NULL) {
+ sprintf(errbuf,
+ "no numbers in the range %ld:%ld that are a multiple of %ld",
+ orig_min, orig_max, orig_mult);
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ if (errp != NULL) {
+ *errp = NULL;
+ }
+
+ nmults = ((max - min) / mult) + 1;
+#if CRAY || (_MIPS_SZLONG == 64)
+ /*
+ * If max is less than 2gb, then the value can fit in 32 bits
+ * and the standard lrand48() routine can be used.
+ */
+ if ( max <= (long)2147483647 ) {
+ return (long) (min + (((long)lrand48() % nmults) * mult));
+ } else {
+ /*
+ * max is greater than 2gb - meeds more than 32 bits.
+ * Since lrand48 only will get a number up to 32bits.
+ */
+ long randnum;
+ randnum=divider(min, max, 0, -1);
+ return (long) (min + ((randnum % nmults) * mult));
+ }
+
+#else
+ return (min + ((lrand48() % nmults) * mult));
+#endif
+}
+
+/*
+ * Attempts to be just like random_range, but everything is long long (64 bit)
+ */
+long long
+random_rangell(min, max, mult, errp)
+long long min;
+long long max;
+long long mult;
+char **errp;
+{
+ long long r, nmults, orig_min, orig_max, orig_mult, tmp;
+ long long randnum;
+ extern long lrand48();
+ static char errbuf[128];
+
+ /*
+ * Sanity check
+ */
+
+ if (mult < 1) {
+ if (errp != NULL) {
+ sprintf(errbuf, "mult arg must be greater than 0");
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ /*
+ * Save original parameter values for use in error message
+ */
+
+ orig_min = min;
+ orig_max = max;
+ orig_mult = mult;
+
+ /*
+ * switch min/max if max < min
+ */
+
+ if (max < min) {
+ tmp = max;
+ max = min;
+ min = tmp;
+ }
+
+ /*
+ * select the random number
+ */
+
+ if ((r = min % mult)) /* bump to the next higher 'mult' multiple */
+ min += mult - r;
+
+ if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */
+ max -= r;
+
+ if (min > max) { /* no 'mult' multiples between min & max */
+ if (errp != NULL) {
+ sprintf(errbuf,
+ "no numbers in the range %lld:%lld that are a multiple of %lld",
+ orig_min, orig_max, orig_mult);
+ *errp = errbuf;
+ }
+ return -1;
+ }
+
+ if (errp != NULL) {
+ *errp = NULL;
+ }
+
+ nmults = ((max - min) / mult) + 1;
+ /*
+ * If max is less than 2gb, then the value can fit in 32 bits
+ * and the standard lrand48() routine can be used.
+ */
+ if ( max <= (long)2147483647 ) {
+ return (long long) (min + (((long long)lrand48() % nmults) * mult));
+ } else {
+ /*
+ * max is greater than 2gb - meeds more than 32 bits.
+ * Since lrand48 only will get a number up to 32bits.
+ */
+ randnum=divider(min, max, 0, -1);
+ return (long long) (min + ((randnum % nmults) * mult));
+ }
+
+}
+
+/*
+ * This functional will recusively call itself to return a random
+ * number min and max. It was designed to work the 64bit numbers
+ * even when compiled as 32 bit process.
+ * algorithm: to use the official lrand48() routine - limited to 32 bits.
+ * find the difference between min and max (max-min).
+ * if the difference is 2g or less, use the random number gotton from lrand48().
+ * Determine the midway point between min and max.
+ * if the midway point is less than 2g from min or max,
+ * randomly add the random number gotton from lrand48() to
+ * either min or the midpoint.
+ * Otherwise, call outself with min and max being min and midway value or
+ * midway value and max. This will reduce the range in half.
+ */
+static long long
+divider(long long min, long long max, long long cnt, long long rand)
+{
+ long long med, half, diff;
+
+ /*
+ * prevent run away code. We are dividing by two each count.
+ * if we get to a count of more than 32, we should have gotten
+ * to 2gb.
+ */
+ if ( cnt > 32 )
+ return -1;
+
+ /*
+ * Only get a random number the first time.
+ */
+ if ( cnt == 0 || rand < -1 ) {
+ rand = (long long)lrand48(); /* 32 bit random number */
+ }
+
+ diff = max - min;
+
+ if ( diff <= 2147483647 )
+ return min + rand;
+
+ half = diff/(long long)2; /* half the distance between min and max */
+ med = min + half; /* med way point between min and max */
+
+#if DEBUG
+printf("divider: min=%lld, max=%lld, cnt=%lld, rand=%lld\n", min, max, cnt, rand);
+printf(" diff = %lld, half = %lld, med = %lld\n", diff, half, med);
+#endif
+
+ if ( half <= 2147483647 ) {
+ /*
+ * If half is smaller than 2gb, we can use the random number
+ * to pick the number within the min to med or med to max
+ * if the cnt bit of rand is zero or one, respectively.
+ */
+ if ( rand & (1<<cnt) )
+ return med + rand;
+ else
+ return min + rand;
+ } else {
+ /*
+ * recursively call ourself to reduce the value to the bottom half
+ * or top half (bit cnt is set).
+ */
+ if ( rand & (1<<cnt) ) {
+ return divider(med, max, cnt+1, rand);
+ } else {
+ return divider(min, med, cnt+1, rand);
+ }
+
+ }
+
+}
+
+
+/*****************************************************************************
+ * random_range_seed(s)
+ *
+ * Sets the random seed to s. Uses srand48(), assuming that lrand48() will
+ * be used in random_range().
+ *****************************************************************************/
+
+void
+random_range_seed(s)
+long s;
+{
+ extern void srand48();
+
+ srand48(s);
+}
+
+/****************************************************************************
+ * random_bit(mask)
+ *
+ * This function randomly returns a single bit from the bits
+ * set in mask. If mask is zero, zero is returned.
+ *
+ ****************************************************************************/
+long
+random_bit(long mask)
+{
+ int nbits = 0; /* number of set bits in mask */
+ long bit; /* used to count bits and num of set bits choosen */
+ int nshift; /* used to count bit shifts */
+
+ if ( mask == 0 )
+ return 0;
+
+ /*
+ * get the number of bits set in mask
+ */
+#ifndef CRAY
+
+ bit=1L;
+ for ( nshift=0; nshift<sizeof(long)*8; nshift++) {
+ if ( mask & bit )
+ nbits++;
+ bit=bit<<1;
+ }
+
+#else
+ nbits=_popcnt(mask);
+#endif /* if CRAY */
+
+ /*
+ * randomly choose a bit.
+ */
+ bit=random_range(1, nbits, 1, NULL);
+
+ /*
+ * shift bits until you determine which bit was randomly choosen.
+ * nshift will hold the number of shifts to make.
+ */
+
+ nshift=0;
+ while (bit) {
+ /* check if the current one's bit is set */
+ if ( mask & 1L ) {
+ bit--;
+ }
+ mask = mask >> 1;
+ nshift++;
+ }
+
+ return 01L << (nshift-1);
+
+}
+
+
+#if RANDOM_BIT_UNITTEST
+/*
+ * The following is a unit test main function for random_bit().
+ */
+main(argc, argv)
+int argc;
+char **argv;
+{
+ int ind;
+ int cnt, iter;
+ long mask, ret;
+
+ printf("test for first and last bit set\n");
+ mask=1L;
+ ret=random_bit(mask);
+ printf("random_bit(%#o) returned %#o\n", mask, ret);
+
+ mask=1L<<(sizeof(long)*8-1);
+ ret=random_bit(mask);
+ printf("random_bit(%#o) returned %#o\n", mask, ret);
+
+ if ( argc >= 3 ) {
+ iter=atoi(argv[1]);
+ for (ind=2; ind<argc; ind++) {
+ printf("Calling random_bit %d times for mask %#o\n", iter, mask);
+ sscanf(argv[ind], "%i", &mask);
+ for (cnt=0; cnt<iter; cnt++) {
+ ret=random_bit(mask);
+ printf("random_bit(%#o) returned %#o\n", mask, ret);
+ }
+ }
+ }
+ exit(0);
+}
+
+#endif /* end if RANDOM_BIT_UNITTEST */
+
+
+#if UNIT_TEST
+/*
+ * The following is a unit test main function for random_range*().
+ */
+
+#define PARTNUM 10 /* used to determine even distribution of random numbers */
+#define MEG 1024*1024*1024
+#define GIG 1073741824
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ int ind;
+ int cnt, iter=10;
+ int imin=0, imult=1, itmin, itmax=0;
+#if CRAY
+ int imax=6*GIG; /* higher than 32 bits */
+#else
+ int imax=1048576;
+#endif
+
+ long lret, lmin=0, lmult=1, ltmin, ltmax=0;
+#if CRAY || (_MIPS_SZLONG == 64)
+ long lmax=6*(long)GIG; /* higher than 32 bits */
+#else
+ long lmax=1048576;
+#endif
+ long long llret, llmin=0, llmult=1, lltmin, lltmax=0;
+ long long llmax=(long long)80*(long long)GIG;
+
+ long part;
+ long long lpart;
+ long cntarr[PARTNUM];
+ long valbound[PARTNUM];
+ long long lvalbound[PARTNUM];
+
+ for (ind=0; ind<PARTNUM; ind++ )
+ cntarr[ind]=0;
+
+ if ( argc < 2 ) {
+ printf("Usage: %s func [iterations] \n", argv[0]);
+ printf("func can be random_range, random_rangel, random_rangell\n");
+ exit(1);
+ }
+
+ if ( argc >= 3 ) {
+ if ( sscanf(argv[2], "%i", &iter) != 1 ) {
+ printf("Usage: %s [func iterations] \n", argv[0]);
+ printf("argv[2] is not a number\n");
+ exit(1);
+ }
+ }
+
+
+ /*
+ * random_rangel ()
+ */
+ if ( strcmp(argv[1], "random_rangel") == 0 ) {
+ ltmin=lmax;
+ part = lmax/PARTNUM;
+ for(ind=0; ind<PARTNUM; ind++) {
+ valbound[ind]=part*ind;
+ }
+
+ for(cnt=0; cnt<iter; cnt++) {
+ lret=random_rangel(lmin, lmax, lmult, NULL);
+ if ( iter < 100 )
+ printf("%ld\n", lret);
+ if ( lret < ltmin )
+ ltmin = lret;
+ if ( lret > ltmax )
+ ltmax = lret;
+ for(ind=0; ind<PARTNUM-1; ind++) {
+ if ( valbound[ind] < lret && lret <= valbound[ind+1] ) {
+ cntarr[ind]++;
+ break;
+ }
+ }
+ if ( lret > valbound[PARTNUM-1] ) {
+ cntarr[PARTNUM-1]++;
+ }
+ }
+ for(ind=0; ind<PARTNUM-1; ind++) {
+ printf("%2d %-13ld to %-13ld %5ld %4.4f\n", ind+1,
+ valbound[ind], valbound[ind+1], cntarr[ind],
+ (float)(cntarr[ind]/(float)iter));
+ }
+ printf("%2d %-13ld to %-13ld %5ld %4.4f\n", PARTNUM,
+ valbound[PARTNUM-1], lmax, cntarr[PARTNUM-1],
+ (float)(cntarr[PARTNUM-1]/(float)iter));
+ printf(" min=%ld, max=%ld\n", ltmin, ltmax);
+
+ } else if ( strcmp(argv[1], "random_rangell") == 0 ) {
+ /*
+ * random_rangell() unit test
+ */
+ lltmin=llmax;
+ lpart = llmax/PARTNUM;
+ for(ind=0; ind<PARTNUM; ind++) {
+ lvalbound[ind]=(long long)(lpart*ind);
+ }
+
+ for(cnt=0; cnt<iter; cnt++) {
+ llret=random_rangell(llmin, llmax, llmult, NULL);
+ if ( iter < 100 )
+ printf("random_rangell returned %lld\n", llret);
+ if ( llret < lltmin )
+ lltmin = llret;
+ if ( llret > lltmax )
+ lltmax = llret;
+
+ for(ind=0; ind<PARTNUM-1; ind++) {
+ if ( lvalbound[ind] < llret && llret <= lvalbound[ind+1] ) {
+ cntarr[ind]++;
+ break;
+ }
+ }
+ if ( llret > lvalbound[PARTNUM-1] ) {
+ cntarr[PARTNUM-1]++;
+ }
+ }
+ for(ind=0; ind<PARTNUM-1; ind++) {
+ printf("%2d %-13lld to %-13lld %5ld %4.4f\n", ind+1,
+ lvalbound[ind], lvalbound[ind+1], cntarr[ind],
+ (float)(cntarr[ind]/(float)iter));
+ }
+ printf("%2d %-13lld to %-13lld %5ld %4.4f\n", PARTNUM,
+ lvalbound[PARTNUM-1], llmax, cntarr[PARTNUM-1],
+ (float)(cntarr[PARTNUM-1]/(float)iter));
+ printf(" min=%lld, max=%lld\n", lltmin, lltmax);
+
+ } else {
+ /*
+ * random_range() unit test
+ */
+ itmin=imax;
+ part = imax/PARTNUM;
+ for(ind=0; ind<PARTNUM; ind++) {
+ valbound[ind]=part*ind;
+ }
+
+ for(cnt=0; cnt<iter; cnt++) {
+ lret=random_range(imin, imax, imult, NULL);
+ if ( iter < 100 )
+ printf("%ld\n", lret);
+ if ( lret < itmin )
+ itmin = lret;
+ if ( lret > itmax )
+ itmax = lret;
+
+ for(ind=0; ind<PARTNUM-1; ind++) {
+ if ( valbound[ind] < lret && lret <= valbound[ind+1] ) {
+ cntarr[ind]++;
+ break;
+ }
+ }
+ if ( lret > valbound[PARTNUM-1] ) {
+ cntarr[PARTNUM-1]++;
+ }
+ }
+ for(ind=0; ind<PARTNUM-1; ind++) {
+ printf("%2d %-13ld to %-13ld %5ld %4.4f\n", ind+1,
+ valbound[ind], valbound[ind+1], cntarr[ind],
+ (float)(cntarr[ind]/(float)iter));
+ }
+ printf("%2d %-13ld to %-13ld %5ld %4.4f\n", PARTNUM,
+ valbound[PARTNUM-1], (long)imax, cntarr[PARTNUM-1],
+ (float)(cntarr[PARTNUM-1]/(float)iter));
+ printf(" min=%d, max=%d\n", itmin, itmax);
+
+ }
+
+ exit(0);
+}
+
+#endif
diff --git a/lib/str_to_bytes.c b/lib/str_to_bytes.c
new file mode 100644
index 0000000..af63a1b
--- /dev/null
+++ b/lib/str_to_bytes.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+#include <stdio.h>
+#include <sys/param.h>
+#include "str_to_bytes.h"
+
+/****************************************************************************
+ * str_to_bytes(s)
+ *
+ * Computes the number of bytes described by string s. s is assumed to be
+ * a base 10 positive (ie. >= 0) number followed by an optional single
+ * character multiplier. The following multipliers are supported:
+ *
+ * char mult
+ * -----------------
+ * b BSIZE or BBSIZE
+ * k 1024 bytes
+ * K 1024 * sizeof(long)
+ * m 2^20 (1048576)
+ * M 2^20 (1048576 * sizeof(long)
+ * g 2^30 (1073741824)
+ * G 2^30 (1073741824) * sizeof(long)
+ *
+ * for instance, "1k" and "1024" would both cause str_to_bytes to return 1024.
+ *
+ * Returns -1 if mult is an invalid character, or if the integer portion of
+ * s is not a positive integer.
+ *
+ ****************************************************************************/
+
+#if CRAY
+#define B_MULT BSIZE /* block size */
+#elif sgi
+#define B_MULT BBSIZE /* block size */
+#elif linux
+#define B_MULT DEV_BSIZE /* block size */
+#endif
+
+
+#define K_MULT 1024 /* Kilo or 2^10 */
+#define M_MULT 1048576 /* Mega or 2^20 */
+#define G_MULT 1073741824 /* Giga or 2^30 */
+#define T_MULT 1099511627776 /* tera or 2^40 */
+
+int
+str_to_bytes(s)
+char *s;
+{
+ char mult, junk;
+ int nconv;
+ float num;
+
+ nconv = sscanf(s, "%f%c%c", &num, &mult, &junk);
+ if (nconv == 0 || nconv == 3 )
+ return -1;
+
+ if (nconv == 1)
+ return num;
+
+ switch (mult) {
+ case 'b':
+ return (int)(num * (float)B_MULT);
+ case 'k':
+ return (int)(num * (float)K_MULT);
+ case 'K':
+ return (int)((num * (float)K_MULT) * sizeof(long));
+ case 'm':
+ return (int)(num * (float)M_MULT);
+ case 'M':
+ return (int)((num * (float)M_MULT) * sizeof(long));
+ case 'g':
+ return (int)(num * (float)G_MULT);
+ case 'G':
+ return (int)((num * (float)G_MULT) * sizeof(long));
+ default:
+ return -1;
+ }
+}
+
+long
+str_to_lbytes(s)
+char *s;
+{
+ char mult, junk;
+ long nconv;
+ float num;
+
+ nconv = sscanf(s, "%f%c%c", &num, &mult, &junk);
+ if (nconv == 0 || nconv == 3 )
+ return -1;
+
+ if (nconv == 1)
+ return (long)num;
+
+ switch (mult) {
+ case 'b':
+ return (long)(num * (float)B_MULT);
+ case 'k':
+ return (long)(num * (float)K_MULT);
+ case 'K':
+ return (long)((num * (float)K_MULT) * sizeof(long));
+ case 'm':
+ return (long)(num * (float)M_MULT);
+ case 'M':
+ return (long)((num * (float)M_MULT) * sizeof(long));
+ case 'g':
+ return (long)(num * (float)G_MULT);
+ case 'G':
+ return (long)((num * (float)G_MULT) * sizeof(long));
+ default:
+ return -1;
+ }
+}
+
+/*
+ * Force 64 bits number when compiled as 32 IRIX binary.
+ * This allows for a number bigger than 2G.
+ */
+
+long long
+str_to_llbytes(s)
+char *s;
+{
+ char mult, junk;
+ long nconv;
+ double num;
+
+ nconv = sscanf(s, "%lf%c%c", &num, &mult, &junk);
+ if (nconv == 0 || nconv == 3 )
+ return -1;
+
+ if (nconv == 1)
+ return (long long)num;
+
+ switch (mult) {
+ case 'b':
+ return (long long)(num * (float)B_MULT);
+ case 'k':
+ return (long long)(num * (float)K_MULT);
+ case 'K':
+ return (long long)((num * (float)K_MULT) * sizeof(long long));
+ case 'm':
+ return (long long)(num * (float)M_MULT);
+ case 'M':
+ return (long long)((num * (float)M_MULT) * sizeof(long long));
+ case 'g':
+ return (long long)(num * (float)G_MULT);
+ case 'G':
+ return (long long)((num * (float)G_MULT) * sizeof(long long));
+ default:
+ return -1;
+ }
+}
+
+#ifdef UNIT_TEST
+
+main(int argc, char **argv)
+{
+ int ind;
+
+ if (argc == 1 ) {
+ fprintf(stderr, "missing str_to_bytes() parameteres\n");
+ exit(1);
+ }
+
+ for (ind=1; ind<argc; ind++) {
+
+ printf("str_to_bytes(%s) returned %d\n",
+ argv[ind], str_to_bytes(argv[ind]));
+
+ printf("str_to_lbytes(%s) returned %ld\n",
+ argv[ind], str_to_lbytes(argv[ind]));
+
+ printf("str_to_llbytes(%s) returned %lld\n",
+ argv[ind], str_to_llbytes(argv[ind]));
+ }
+}
+
+#endif
diff --git a/lib/string_to_tokens.c b/lib/string_to_tokens.c
new file mode 100644
index 0000000..5200aec
--- /dev/null
+++ b/lib/string_to_tokens.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/**********************************************************
+ *
+ * UNICOS Testing - Cray Research, Inc.
+ *
+ * FUNCTION NAME : string_to_tokens
+ *
+ * FUNCTION TITLE : Break a string into its tokens
+ *
+ * SYNOPSIS:
+ *
+ * int string_to_tokens(arg_string, arg_array, array_size, separator)
+ * char *arg_string;
+ * char *arg_array[];
+ * int array_size;
+ * char *separator;
+ *
+ * AUTHOR : Richard Logan
+ *
+ * DATE : 10/94
+ *
+ * INITIAL RELEASE : UNICOS 7.0
+ *
+ * DESCRIPTION
+ * This function parses the string 'arg_string', placing pointers to
+ * the 'separator' separated tokens into the elements of 'arg_array'.
+ * The array is terminated with a null pointer.
+ * 'arg_array' must contains at least 'array_size' elements.
+ * Only the first 'array_size' minus one tokens will be placed into
+ * 'arg_array'. If there are more than 'array_size'-1 tokens, the rest are
+ * ignored by this routine.
+ *
+ * RETURN VALUE
+ * This function returns the number of 'separator' separated tokens that
+ * were found in 'arg_string'.
+ * If 'arg_array' or 'separator' is NULL or 'array_size' is less than 2, -1 is returned.
+ *
+ * WARNING
+ * This function uses strtok() to parse 'arg_string', and thus
+ * physically alters 'arg_string' by placing null characters where the
+ * separators originally were.
+ *
+ *
+ *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/
+#include <stdio.h>
+#include <string.h> /* for string functions */
+#include "string_to_tokens.h"
+
+int
+string_to_tokens(char *arg_string, char *arg_array[], int array_size, char *separator)
+{
+ int num_toks = 0; /* number of tokens found */
+ char *strtok();
+
+ if ( arg_array == NULL || array_size <= 1 || separator == NULL )
+ return -1;
+
+ /*
+ * Use strtok() to parse 'arg_string', placing pointers to the
+ * individual tokens into the elements of 'arg_array'.
+ */
+ if ( (arg_array[num_toks] = strtok(arg_string, separator)) == NULL ) {
+ return 0;
+ }
+
+ for (num_toks=1;num_toks<array_size; num_toks++) {
+ if ( (arg_array[num_toks] = strtok(NULL, separator)) == NULL )
+ break;
+ }
+
+ if ( num_toks == array_size )
+ arg_array[num_toks] = NULL;
+
+ /*
+ * Return the number of tokens that were found in 'arg_string'.
+ */
+ return(num_toks);
+
+} /* end of string_to_tokens */
diff --git a/lib/tlibio.c b/lib/tlibio.c
new file mode 100644
index 0000000..a73aa39
--- /dev/null
+++ b/lib/tlibio.c
@@ -0,0 +1,1999 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ *
+ * Lib i/o
+ *
+ * This file contains several functions to doing reads and writes.
+ * It was written so that a single function could be called in a test
+ * program and only a io type field value would have to change to
+ * do different types of io. There is even a couple of functions that
+ * will allow you to parse a string to determine the iotype.
+ *
+ * This file contains functions for writing/reading to/from open files
+ * Prototypes:
+ *
+ * Functions declared in this module - see individual function code for
+ * usage comments:
+ *
+ * int stride_bounds(int offset, int stride, int nstrides,
+ * int bytes_per_stride, int *min, int *max);
+
+ * int lio_write_buffer(int fd, int method, char *buffer, int size,
+ * char **errmsg, long wrd);
+ * int lio_read_buffer(int fd, int method, char *buffer, int size,
+ * char **errmsg, long wrd);
+ *
+ * #ifdef CRAY
+ * int lio_wait4asyncio(int method, int fd, struct iosw **statptr)
+ * int lio_check_asyncio(char *io_type, int size, struct iosw *status)
+ * #endif
+ * #ifdef sgi
+ * int lio_wait4asyncio(int method, int fd, aiocb_t *aiocbp)
+ * int lio_check_asyncio(char *io_type, int size, aiocb_t *aiocbp, int method)
+ * #endif
+ *
+ * int lio_parse_io_arg1(char *string)
+ * void lio_help1(char *prefix);
+ *
+ * int lio_parse_io_arg2(char *string, char **badtoken)
+ * void lio_help2(char *prefix);
+ *
+ * int lio_set_debug(int level);
+ *
+ * char Lio_SysCall[];
+ * struct lio_info_type Lio_info1[];
+ * struct lio_info_type Lio_info2[];
+ *
+ * Author : Richard Logan
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#ifdef CRAY
+#include <sys/unistd.h>
+#else
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <signal.h>
+#ifdef CRAY
+#include <sys/secparm.h>
+#include <sys/iosw.h>
+#include <sys/listio.h>
+#else
+/* for linux or sgi */
+#include <sys/uio.h> /* readv(2)/writev(2) */
+#include <string.h> /* bzero */
+#endif
+#ifdef sgi
+#include <aio.h>
+#endif
+#if UNIT_TEST
+#include <stdlib.h> /* atoi */
+#endif
+
+#include "tlibio.h" /* defines LIO* marcos */
+
+#ifndef PATH_MAX
+#define PATH_MAX MAXPATHLEN
+#endif
+
+#if 0 /* disabled until it's needed -- roehrich 6/11/97 */
+#define BUG1_workaround 1 /* Work around a condition where aio_return gives
+ * a value of zero but there is no errno followup
+ * and the read/write operation actually did its
+ * job. spr/pv 705244
+ */
+#endif
+
+extern int errno;
+
+#ifndef linux
+static void lio_async_signal_handler();
+#endif
+#ifdef sgi
+static void lio_async_callback_handler();
+#endif
+
+/*
+ * Define the structure as used in lio_parse_arg1 and lio_help1
+ */
+struct lio_info_type Lio_info1[] = {
+ { "s", LIO_IO_SYNC, "sync i/o" },
+ { "p", LIO_IO_ASYNC|LIO_WAIT_SIGACTIVE, "async i/o using a loop to wait for a signal" },
+ { "b", LIO_IO_ASYNC|LIO_WAIT_SIGPAUSE, "async i/o using pause" },
+ { "a", LIO_IO_ASYNC|LIO_WAIT_RECALL, "async i/o using recall/aio_suspend" },
+#ifdef sgi
+ { "r",
+ LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, "random sync i/o types and wait methods" },
+ { "R",
+ LIO_RANDOM|LIO_IO_ATYPES|LIO_WAIT_ATYPES, "random i/o types and wait methods" },
+#else
+ { "r",
+ LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, "random i/o types and wait methods" },
+ { "R",
+ LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, "random i/o types and wait methods" },
+#endif
+ { "l", LIO_IO_SLISTIO|LIO_WAIT_RECALL, "single stride sync listio" },
+ { "L", LIO_IO_ALISTIO|LIO_WAIT_RECALL, "single stride async listio using recall" },
+ { "X", LIO_IO_ALISTIO|LIO_WAIT_SIGPAUSE, "single stride async listio using pause" },
+ { "v", LIO_IO_SYNCV, "single buffer sync readv/writev" },
+ { "P", LIO_IO_SYNCP, "sync pread/pwrite" },
+};
+
+/*
+ * Define the structure used by lio_parse_arg2 and lio_help2
+ */
+struct lio_info_type Lio_info2[] = {
+ { "sync", LIO_IO_SYNC, "sync i/o (read/write)"},
+ { "async", LIO_IO_ASYNC, "async i/o (reada/writea/aio_read/aio_write)" },
+ { "slistio", LIO_IO_SLISTIO, "single stride sync listio" },
+ { "alistio", LIO_IO_ALISTIO, "single stride async listio" },
+ { "syncv", LIO_IO_SYNCV, "single buffer sync readv/writev"},
+ { "syncp", LIO_IO_SYNCP, "pread/pwrite"},
+ { "active", LIO_WAIT_ACTIVE, "spin on status/control values" },
+ { "recall", LIO_WAIT_RECALL, "use recall(2)/aio_suspend(3) to wait for i/o to complete" },
+ { "sigactive", LIO_WAIT_SIGACTIVE, "spin waiting for signal" },
+ { "sigpause", LIO_WAIT_SIGPAUSE, "call pause(2) to wait for signal" },
+/* nowait is a touchy thing, it's an accident that this implementation worked at all. 6/27/97 roehrich */
+/* { "nowait", LIO_WAIT_NONE, "do not wait for async io to complete" },*/
+ { "random", LIO_RANDOM, "set random bit" },
+ { "randomall",
+ LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES,
+ "all random i/o types and wait methods (except nowait)" },
+};
+
+char Lio_SysCall[PATH_MAX]; /* string containing last i/o system call */
+
+static volatile int Received_signal = 0; /* number of signals received */
+static volatile int Rec_signal;
+#ifdef sgi
+static volatile int Received_callback = 0; /* number of callbacks received */
+static volatile int Rec_callback;
+#endif
+static char Errormsg[500];
+static int Debug_level = 0;
+
+
+
+/***********************************************************************
+ * stride_bounds()
+ *
+ * Determine the bounds of a strided request, normalized to offset. Returns
+ * the number of bytes needed to satisfy the request, and optionally sets
+ * *min and *max to the mininum and maximum bytes referenced, normalized
+ * around offset.
+ *
+ * Returns -1 on error - the only possible error conditions are illegal values
+ * for nstrides and/or bytes_per_stride - both parameters must be >= 0.
+ *
+ * (maule, 11/16/95)
+ ***********************************************************************/
+
+int
+stride_bounds(offset, stride, nstrides, bytes_per_stride, min, max)
+int offset;
+int stride;
+int nstrides;
+int bytes_per_stride;
+int *min;
+int *max;
+{
+ int nbytes, min_byte, max_byte;
+
+ /*
+ * sanity checks ...
+ */
+
+ if (nstrides < 0 || bytes_per_stride < 0) {
+ return -1;
+ }
+
+ if (stride == 0) {
+ stride = bytes_per_stride;
+ }
+
+ /*
+ * Determine the # of bytes needed to satisfy the request. This
+ * value, along with the offset argument, determines the min and max
+ * bytes referenced.
+ */
+
+
+ nbytes = abs(stride) * (nstrides-1) + bytes_per_stride;
+
+ if (stride < 0) {
+ max_byte = offset + bytes_per_stride - 1;
+ min_byte = max_byte - nbytes + 1;
+ } else {
+ min_byte = offset;
+ max_byte = min_byte + nbytes - 1;
+ }
+
+ if (min != NULL) {
+ *min = min_byte;
+ }
+
+ if (max != NULL) {
+ *max = max_byte;
+ }
+
+ return nbytes;
+}
+
+/***********************************************************************
+ * This function will allow someone to set the debug level.
+ ***********************************************************************/
+int
+lio_set_debug(level)
+{
+ int old;
+
+ old = Debug_level;
+ Debug_level = level;
+ return old;
+}
+
+/***********************************************************************
+ * This function will parse a string and return desired io-method.
+ * Only the first character of the string is used.
+ *
+ * This function does not provide for meaningful option arguments,
+ * but it supports current growfiles/btlk interface.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+lio_parse_io_arg1(char *string)
+{
+ int ind;
+ int found=0;
+ int mask=0;
+
+ /*
+ * Determine if token is a valid string.
+ */
+ for(ind=0; ind<sizeof(Lio_info1)/sizeof(struct lio_info_type); ind++) {
+ if ( strcmp(string, Lio_info1[ind].token) == 0 ) {
+ mask |= Lio_info1[ind].bits;
+ found = 1;
+ break;
+ }
+ }
+
+ if ( found == 0 ) {
+ return -1;
+ }
+
+ return mask;
+
+}
+
+/***********************************************************************
+ * This function will print a help message describing the characters
+ * that can be parsed by lio_parse_io_arg1().
+ * They will be printed one per line.
+ * (rrl 04/96)
+ ***********************************************************************/
+void
+lio_help1(char *prefix)
+{
+ int ind;
+
+ for(ind=0; ind<sizeof(Lio_info1)/sizeof(struct lio_info_type); ind++) {
+ printf("%s %s : %s\n", prefix,
+ Lio_info1[ind].token, Lio_info1[ind].desc);
+ }
+
+ return;
+}
+
+/***********************************************************************
+ * This function will parse a string and return the desired io-method.
+ * This function will take a comma separated list of io type and wait
+ * method tokens as defined in Lio_info2[]. If a token does not match
+ * any of the tokens in Lio_info2[], it will be coverted to a number.
+ * If it was a number, those bits are also set.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+lio_parse_io_arg2(char *string, char **badtoken)
+{
+ char *token = string;
+ char *cc = token;
+ char savecc;
+ int found;
+ int mask=0;
+
+ int tmp;
+ int ind;
+ char chr;
+
+ if ( token == NULL )
+ return -1;
+
+ for (;;) {
+ for (; ((*cc != ',') && (*cc != '\0')); cc++);
+ savecc = *cc;
+ *cc = '\0';
+
+ found = 0;
+
+ /*
+ * Determine if token is a valid string or number and if
+ * so, add the bits to the mask.
+ */
+ for(ind=0; ind<sizeof(Lio_info2)/sizeof(struct lio_info_type); ind++) {
+ if ( strcmp(token, Lio_info2[ind].token) == 0 ) {
+ mask |= Lio_info2[ind].bits;
+ found = 1;
+ break;
+ }
+ }
+
+ /*
+ * If token does not match one of the defined tokens, determine
+ * if it is a number, if so, add the bits.
+ */
+ if ( !found ) {
+ if (sscanf(token, "%i%c", &tmp, &chr) == 1 ) {
+ mask |= tmp;
+ found=1;
+ }
+ }
+
+ *cc = savecc;
+
+ if (!found) { /* token is not valid */
+ if ( badtoken != NULL)
+ *badtoken = token;
+ return(-1);
+ }
+
+ if (savecc == '\0')
+ break;
+
+ token = ++cc;
+ }
+
+ return mask;
+}
+
+/***********************************************************************
+ * This function will print a help message describing the tokens
+ * that can be parsed by lio_parse_io_arg2().
+ * It will print them one per line.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+void
+lio_help2(char *prefix)
+{
+ int ind;
+
+ for(ind=0; ind<sizeof(Lio_info2)/sizeof(struct lio_info_type); ind++) {
+ printf("%s %s : %s\n", prefix,
+ Lio_info2[ind].token, Lio_info2[ind].desc);
+ }
+ return;
+}
+
+#ifndef linux
+/***********************************************************************
+ * This is an internal signal handler.
+ * If the handler is called, it will increment the Received_signal
+ * global variable.
+ ***********************************************************************/
+static void
+lio_async_signal_handler(int sig)
+{
+ if ( Debug_level )
+ printf("DEBUG %s/%d: received signal %d, a signal caught %d times\n",
+ __FILE__, __LINE__, sig, Received_signal+1);
+
+ Received_signal++;
+
+ return;
+}
+#endif
+
+#ifdef sgi
+/***********************************************************************
+ * This is an internal callback handler.
+ * If the handler is called, it will increment the Received_callback
+ * global variable.
+ ***********************************************************************/
+static void
+lio_async_callback_handler(sigval_t sigval)
+{
+ if ( Debug_level )
+ printf("DEBUG %s/%d: received callback, nbytes=%ld, a callback called %d times\n",
+ __FILE__, __LINE__, sigval.sival_int, Received_callback+1);
+
+ Received_callback++;
+
+ return;
+}
+#endif /* sgi */
+
+/***********************************************************************
+ * lio_random_methods
+ * This function will randomly choose an io type and wait method
+ * from set of io types and wait methods. Since this information
+ * is stored in a bitmask, it randomly chooses an io type from
+ * the io type bits specified and does the same for wait methods.
+ *
+ * Return Value
+ * This function will return a value with all non choosen io type
+ * and wait method bits cleared. The LIO_RANDOM bit is also
+ * cleared. All other bits are left unchanged.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+lio_random_methods(long curr_mask)
+{
+ int mask=0;
+ long random_bit();
+
+ /* remove random select, io type, and wait method bits from curr_mask */
+ mask = curr_mask & (~(LIO_IO_TYPES | LIO_WAIT_TYPES | LIO_RANDOM));
+
+ /* randomly select io type from specified io types */
+ mask = mask | random_bit(curr_mask & LIO_IO_TYPES);
+
+ /* randomly select wait methods from specified wait methods */
+ mask = mask | random_bit(curr_mask & LIO_WAIT_TYPES);
+
+ return mask;
+}
+
+/***********************************************************************
+ * Generic write function
+ * This function can be used to do a write using write(2), writea(2),
+ * aio_write(3), writev(2), pwrite(2),
+ * or single stride listio(2)/lio_listio(3).
+ * By setting the desired bits in the method
+ * bitmask, the caller can control the type of write and the wait method
+ * that will be used. If no io type bits are set, write will be used.
+ *
+ * If async io was attempted and no wait method bits are set then the
+ * wait method is: recall(2) for writea(2) and listio(2); aio_suspend(3) for
+ * aio_write(3) and lio_listio(3).
+ *
+ * If multiple wait methods are specified,
+ * only one wait method will be used. The order is predetermined.
+ *
+ * If the call specifies a signal and one of the two signal wait methods,
+ * a signal handler for the signal is set. This will reset an already
+ * set handler for this signal.
+ *
+ * If the LIO_RANDOM method bit is set, this function will randomly
+ * choose a io type and wait method from bits in the method argument.
+ *
+ * If an error is encountered, an error message will be generated
+ * in a internal static buffer. If errmsg is not NULL, it will
+ * be updated to point to the static buffer, allowing the caller
+ * to print the error message.
+ *
+ * Return Value
+ * If a system call fails, -errno is returned.
+ * If LIO_WAIT_NONE bit is set, the return value is the return value
+ * of the system call.
+ * If the io did not fail, the amount of data written is returned.
+ * If the size the system call say was written is different
+ * then what was asked to be written, errmsg is updated for
+ * this error condition. The return value is still the amount
+ * the system call says was written.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+lio_write_buffer(fd, method, buffer, size, sig, errmsg, wrd)
+int fd; /* open file descriptor */
+int method; /* contains io type and wait method bitmask */
+char *buffer; /* pointer to buffer */
+int size; /* the size of the io */
+int sig; /* signal to use if async io */
+char **errmsg; /* char pointer that will be updated to point to err message */
+long wrd; /* to allow future features, use zero for now */
+{
+ int ret; /* syscall return or used to get random method */
+ char *io_type; /* Holds string of type of io */
+#ifndef linux
+ int omethod = method;
+ int listio_cmd; /* Holds the listio/lio_listio cmd */
+#endif
+#ifdef CRAY
+ struct listreq request; /* Used when a listio is wanted */
+ struct iosw status, *statptr[1];
+#else
+ /* for linux or sgi */
+ struct iovec iov; /* iovec for writev(2) */
+#endif
+#ifdef sgi
+ aiocb_t aiocbp; /* POSIX aio control block */
+ aiocb_t *aiolist[1]; /* list of aio control blocks for lio_listio */
+ off64_t poffset; /* pwrite(2) offset */
+#endif
+
+ /*
+ * If LIO_RANDOM bit specified, get new method randomly.
+ */
+ if ( method & LIO_RANDOM ) {
+ if( Debug_level > 3 )
+ printf("DEBUG %s/%d: method mask to choose from: %#o\n", __FILE__, __LINE__, method );
+ method = lio_random_methods(method);
+ if ( Debug_level > 2 )
+ printf("DEBUG %s/%d: random chosen method %#o\n", __FILE__, __LINE__, method);
+ }
+
+ if ( errmsg != NULL )
+ *errmsg = Errormsg;
+
+ Rec_signal=Received_signal; /* get the current number of signals received */
+#ifdef sgi
+ Rec_callback=Received_callback; /* get the current number of callbacks received */
+#endif
+
+#ifdef CRAY
+ bzero(&status, sizeof(struct iosw));
+ bzero(&request, sizeof(struct listreq));
+ statptr[0] = &status;
+#else
+ /* for linux or sgi */
+ bzero(&iov, sizeof(struct iovec));
+ iov.iov_base = buffer;
+ iov.iov_len = size;
+#endif
+#ifdef sgi
+ bzero(&aiocbp, sizeof(aiocb_t));
+ aiocbp.aio_fildes = fd;
+ aiocbp.aio_nbytes = size;
+ aiocbp.aio_buf = buffer;
+/* aiocbp.aio_offset = lseek( fd, 0, SEEK_CUR ); -- set below */
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_NONE;
+ aiocbp.aio_sigevent.sigev_signo = 0;
+ aiocbp.aio_sigevent.sigev_func = NULL;
+ aiocbp.aio_sigevent.sigev_value.sival_int = 0;
+ aiolist[0] = &aiocbp;
+
+ if( (ret = lseek( fd, 0, SEEK_CUR )) == -1 ){
+ ret = 0;
+ /* If there is an error and it is not ESPIPE then kick out the error.
+ * If the fd is a fifo then we have to make sure that
+ * lio_random_methods() didn't select pwrite/pread; if it did then
+ * switch to write/read.
+ */
+ if( errno == ESPIPE ){
+ if( method & LIO_IO_SYNCP ){
+ if( omethod & LIO_RANDOM ){
+ method &= ~LIO_IO_SYNCP;
+ method |= LIO_IO_SYNC;
+ if( Debug_level > 2 )
+ printf("DEBUG %s/%d: random chosen method switched to %#o for fifo\n", __FILE__, __LINE__, method );
+ }
+ else if( Debug_level ){
+ printf("DEBUG %s/%d: pwrite will fail when it writes to a fifo\n",
+ __FILE__, __LINE__ );
+ }
+ }
+ /* else: let it ride */
+ }
+ else{
+ sprintf(Errormsg, "%s/%d lseek(fd=%d,0,SEEK_CUR) failed, errno=%d %s",
+ __FILE__, __LINE__, fd, errno, sys_errlist[errno]);
+ return -errno;
+ }
+ }
+ poffset = (off64_t)ret;
+ aiocbp.aio_offset = ret;
+
+#endif
+
+ /*
+ * If the LIO_USE_SIGNAL bit is not set, only use the signal
+ * if the LIO_WAIT_SIGPAUSE or the LIO_WAIT_SIGACTIVE bits are bit.
+ * Otherwise there is not necessary a signal handler to trap
+ * the signal.
+ */
+ if ( sig && !(method & LIO_USE_SIGNAL) &&
+ ! (method & LIO_WAIT_SIGTYPES) ){
+
+ sig=0; /* ignore signal parameter */
+ }
+
+#ifdef sgi
+ if ( sig && (method & LIO_WAIT_CBTYPES) )
+ sig=0; /* ignore signal parameter */
+#endif
+
+ /*
+ * only setup signal hander if sig was specified and
+ * a sig wait method was specified.
+ * Doing this will change the handler for this signal. The
+ * old signal handler will not be restored.
+ *** restoring the signal handler could be added ***
+ */
+
+ if ( sig && (method & LIO_WAIT_SIGTYPES) ){
+#ifdef CRAY
+ sigctl(SCTL_REG, sig, lio_async_signal_handler);
+#endif
+#ifdef sgi
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+ aiocbp.aio_sigevent.sigev_signo = sig;
+ sigset(sig, lio_async_signal_handler);
+#endif /* sgi */
+ }
+#ifdef sgi
+ else if( method & LIO_WAIT_CBTYPES ){
+ /* sival_int just has to be something that I can use
+ * to identify the callback, and "size" happens to be handy...
+ */
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_CALLBACK;
+ aiocbp.aio_sigevent.sigev_func = lio_async_callback_handler;
+ aiocbp.aio_sigevent.sigev_value.sival_int = size;
+ }
+#endif
+
+ /*
+ * Determine the system call that will be called and produce
+ * the string of the system call and place it in Lio_SysCall.
+ * Also update the io_type char pointer to give brief description
+ * of system call. Execute the system call and check for
+ * system call failure. If sync i/o, return the number of
+ * bytes written/read.
+ */
+
+ if ( (method & LIO_IO_SYNC) || (method & LIO_IO_TYPES) == 0 ){
+ /*
+ * write(2) is used if LIO_IO_SYNC bit is set or not none
+ * of the LIO_IO_TYPES bits are set (default).
+ */
+
+ sprintf(Lio_SysCall,
+ "write(%d, buf, %d)", fd, size);
+ io_type="write";
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ if ((ret = write(fd, buffer, size)) == -1) {
+ sprintf(Errormsg, "%s/%d write(%d, buf, %d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, errno, sys_errlist[errno]);
+ return -errno;
+ }
+
+ if ( ret != size ) {
+ sprintf(Errormsg,
+ "%s/%d write(%d, buf, %d) returned=%d",
+ __FILE__, __LINE__,
+ fd, size, ret);
+ }
+ else if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: write completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+
+ }
+
+ else if ( method & LIO_IO_ASYNC ) {
+#ifdef CRAY
+ sprintf(Lio_SysCall,
+ "writea(%d, buf, %d, &status, %d)", fd, size, sig);
+ io_type="writea";
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ sigoff();
+ if ((ret = writea(fd, buffer, size, &status, sig)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d writea(%d, buf, %d, &stat, %d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, sig, errno, sys_errlist[errno]);
+ sigon();
+ return -errno;
+ }
+#endif
+#ifdef sgi
+ sprintf(Lio_SysCall,
+ "aio_write(fildes=%d, buf, nbytes=%d, signo=%d)", fd, size, sig);
+ io_type="aio_write";
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ if( sig )
+ sighold( sig );
+ if ((ret = aio_write(&aiocbp)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d aio_write(fildes=%d, buf, nbytes=%d, signo=%d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, sig, errno, sys_errlist[errno]);
+ if( sig )
+ sigrelse( sig );
+ return -errno;
+ }
+#endif
+ } /* LIO_IO_ASYNC */
+
+ else if ( method & LIO_IO_SLISTIO ) {
+#ifdef CRAY
+ request.li_opcode = LO_WRITE;
+ request.li_fildes = fd;
+ request.li_buf = buffer;
+ request.li_nbyte = size;
+ request.li_status = &status;
+ request.li_signo = sig;
+ request.li_nstride = 0;
+ request.li_filstride = 0;
+ request.li_memstride = 0;
+
+ listio_cmd=LC_WAIT;
+ io_type="listio(2) sync write";
+
+ sprintf(Lio_SysCall,
+ "listio(LC_WAIT, &req, 1) LO_WRITE, fd:%d, nbyte:%d",
+ fd, size);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ sigoff();
+ if ( listio(listio_cmd, &request, 1) == -1 ) {
+ sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__,
+ Lio_SysCall, fd, size, errno, sys_errlist[errno]);
+ sigon();
+ return -errno;
+ }
+
+ if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: %s did not return -1\n",
+ __FILE__, __LINE__, Lio_SysCall);
+
+ ret=lio_check_asyncio(io_type, size, &status);
+ return ret;
+
+#endif
+#ifdef sgi
+
+ aiocbp.aio_lio_opcode = LIO_WRITE;
+ listio_cmd=LIO_WAIT;
+ io_type="lio_listio(3) sync write";
+
+ sprintf(Lio_SysCall,
+ "lio_listio(LIO_WAIT, aiolist, 1, NULL) LIO_WRITE, fd:%d, nbyte:%d, sig:%d",
+ fd, size, sig );
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ if( sig )
+ sighold( sig );
+ if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) {
+ sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__,
+ Lio_SysCall, fd, size, errno, sys_errlist[errno]);
+ if( sig )
+ sigrelse( sig );
+ return -errno;
+ }
+
+ if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: %s did not return -1\n",
+ __FILE__, __LINE__, Lio_SysCall);
+
+ ret=lio_check_asyncio(io_type, size, &aiocbp, method);
+ return ret;
+#endif
+ } /* LIO_IO_SLISTIO */
+
+ else if ( method & LIO_IO_ALISTIO ) {
+#ifdef CRAY
+ request.li_opcode = LO_WRITE;
+ request.li_fildes = fd;
+ request.li_buf = buffer;
+ request.li_nbyte = size;
+ request.li_status = &status;
+ request.li_signo = sig;
+ request.li_nstride = 0;
+ request.li_filstride = 0;
+ request.li_memstride = 0;
+
+ listio_cmd=LC_START;
+ io_type="listio(2) async write";
+
+ sprintf(Lio_SysCall,
+ "listio(LC_START, &req, 1) LO_WRITE, fd:%d, nbyte:%d",
+ fd, size);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ sigoff();
+ if ( listio(listio_cmd, &request, 1) == -1 ) {
+ sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__,
+ Lio_SysCall, fd, size, errno, sys_errlist[errno]);
+ sigon();
+ return -errno;
+ }
+#endif
+#ifdef sgi
+ aiocbp.aio_lio_opcode = LIO_WRITE;
+ listio_cmd=LIO_NOWAIT;
+ io_type="lio_listio(3) async write";
+
+ sprintf(Lio_SysCall,
+ "lio_listio(LIO_NOWAIT, aiolist, 1, NULL) LIO_WRITE, fd:%d, nbyte:%d",
+ fd, size);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ if( sig )
+ sighold( sig );
+ if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) {
+ sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__,
+ Lio_SysCall, fd, size, errno, sys_errlist[errno]);
+ if( sig )
+ sigrelse( sig );
+ return -errno;
+ }
+#endif
+ }/* LIO_IO_ALISTIO */
+
+#ifndef CRAY
+ else if ( method & LIO_IO_SYNCV ) {
+ io_type="writev(2)";
+
+ sprintf(Lio_SysCall,
+ "writev(%d, &iov, 1) nbyte:%d", fd, size);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+ if ((ret = writev(fd, &iov, 1)) == -1) {
+ sprintf(Errormsg, "%s/%d writev(%d, iov, 1) nbyte:%d ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, errno, sys_errlist[errno]);
+ return -errno;
+ }
+
+ if ( ret != size ) {
+ sprintf(Errormsg,
+ "%s/%d writev(%d, iov, 1) nbyte:%d returned=%d",
+ __FILE__, __LINE__,
+ fd, size, ret);
+ }
+ else if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: writev completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ } /* LIO_IO_SYNCV */
+#endif
+
+#ifdef sgi
+ else if ( method & LIO_IO_SYNCP ) {
+ io_type="pwrite(2)";
+
+ sprintf(Lio_SysCall,
+ "pwrite(%d, buf, %d, %lld)", fd, size, poffset);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+ if ((ret = pwrite(fd, buffer, size, poffset)) == -1) {
+ sprintf(Errormsg, "%s/%d pwrite(%d, buf, %d, %lld) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, poffset, errno, sys_errlist[errno]);
+ return -errno;
+ }
+
+ if ( ret != size ) {
+ sprintf(Errormsg,
+ "%s/%d pwrite(%d, buf, %d, %lld) returned=%d",
+ __FILE__, __LINE__,
+ fd, size, poffset, ret);
+ }
+ else if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: pwrite completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ } /* LIO_IO_SYNCP */
+#endif
+
+ else {
+ printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__, __LINE__ );
+ return -1;
+ }
+
+ /*
+ * wait for async io to complete.
+ */
+#ifdef CRAY
+ ret=lio_wait4asyncio(method, fd, statptr);
+#endif
+#ifdef sgi
+ ret=lio_wait4asyncio(method, fd, &aiocbp);
+#endif
+
+ /*
+ * If there was an error waiting for async i/o to complete,
+ * return the error value (errno) to the caller.
+ * Note: Errormsg should already have been updated.
+ */
+ if ( ret < 0 ) {
+ return ret;
+ }
+
+ /*
+ * If i/o was not waited for (may not have been completed at this time),
+ * return the size that was requested.
+ */
+ if ( ret == 1 )
+ return size;
+
+ /*
+ * check that async io was successful.
+ * Note: if the there was an system call failure, -errno
+ * was returned and Errormsg should already have been updated.
+ * If amount i/o was different than size, Errormsg should already
+ * have been updated but the actual i/o size if returned.
+ */
+
+#ifdef CRAY
+ ret=lio_check_asyncio(io_type, size, &status);
+#endif
+#ifdef sgi
+ ret=lio_check_asyncio(io_type, size, &aiocbp, method);
+#endif
+
+ return ret;
+} /* end of lio_write_buffer */
+
+/***********************************************************************
+ * Generic read function
+ * This function can be used to do a read using read(2), reada(2),
+ * aio_read(3), readv(2), pread(2),
+ * or single stride listio(2)/lio_listio(3).
+ * By setting the desired bits in the method
+ * bitmask, the caller can control the type of read and the wait method
+ * that will be used. If no io type bits are set, read will be used.
+ *
+ * If async io was attempted and no wait method bits are set then the
+ * wait method is: recall(2) for reada(2) and listio(2); aio_suspend(3) for
+ * aio_read(3) and lio_listio(3).
+ *
+ * If multiple wait methods are specified,
+ * only one wait method will be used. The order is predetermined.
+ *
+ * If the call specifies a signal and one of the two signal wait methods,
+ * a signal handler for the signal is set. This will reset an already
+ * set handler for this signal.
+ *
+ * If the LIO_RANDOM method bit is set, this function will randomly
+ * choose a io type and wait method from bits in the method argument.
+ *
+ * If an error is encountered, an error message will be generated
+ * in a internal static buffer. If errmsg is not NULL, it will
+ * be updated to point to the static buffer, allowing the caller
+ * to print the error message.
+ *
+ * Return Value
+ * If a system call fails, -errno is returned.
+ * If LIO_WAIT_NONE bit is set, the return value is the return value
+ * of the system call.
+ * If the io did not fail, the amount of data written is returned.
+ * If the size the system call say was written is different
+ * then what was asked to be written, errmsg is updated for
+ * this error condition. The return value is still the amount
+ * the system call says was written.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+lio_read_buffer(fd, method, buffer, size, sig, errmsg, wrd)
+int fd; /* open file descriptor */
+int method; /* contains io type and wait method bitmask */
+char *buffer; /* pointer to buffer */
+int size; /* the size of the io */
+int sig; /* signal to use if async io */
+char **errmsg; /* char pointer that will be updated to point to err message */
+long wrd; /* to allow future features, use zero for now */
+{
+ int ret; /* syscall return or used to get random method */
+ char *io_type; /* Holds string of type of io */
+#ifndef linux
+ int listio_cmd; /* Holds the listio/lio_listio cmd */
+ int omethod = method;
+#endif
+#ifdef CRAY
+ struct listreq request; /* Used when a listio is wanted */
+ struct iosw status, *statptr[1];
+#else
+ /* for linux or sgi */
+ struct iovec iov; /* iovec for readv(2) */
+#endif
+#ifdef sgi
+ aiocb_t aiocbp; /* POSIX aio control block */
+ aiocb_t *aiolist[1]; /* list of aio control blocks for lio_listio */
+ off64_t poffset; /* pread(2) offset */
+#endif
+
+ /*
+ * If LIO_RANDOM bit specified, get new method randomly.
+ */
+ if ( method & LIO_RANDOM ) {
+ if( Debug_level > 3 )
+ printf("DEBUG %s/%d: method mask to choose from: %#o\n", __FILE__, __LINE__, method );
+ method = lio_random_methods(method);
+ if ( Debug_level > 2 )
+ printf("DEBUG %s/%d: random chosen method %#o\n", __FILE__, __LINE__, method);
+ }
+
+ if ( errmsg != NULL )
+ *errmsg = Errormsg;
+
+ Rec_signal=Received_signal; /* get the current number of signals received */
+#ifdef sgi
+ Rec_callback=Received_callback; /* get the current number of callbacks received */
+#endif
+
+#ifdef CRAY
+ bzero(&status, sizeof(struct iosw));
+ bzero(&request, sizeof(struct listreq));
+ statptr[0] = &status;
+#else
+ /* for linux or sgi */
+ bzero(&iov, sizeof(struct iovec));
+ iov.iov_base = buffer;
+ iov.iov_len = size;
+#endif
+#ifdef sgi
+ bzero(&aiocbp, sizeof(aiocb_t));
+ aiocbp.aio_fildes = fd;
+ aiocbp.aio_nbytes = size;
+ aiocbp.aio_buf = buffer;
+/* aiocbp.aio_offset = lseek( fd, 0, SEEK_CUR ); -- set below */
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_NONE;
+ aiocbp.aio_sigevent.sigev_signo = 0;
+ aiocbp.aio_sigevent.sigev_func = NULL;
+ aiocbp.aio_sigevent.sigev_value.sival_int = 0;
+ aiolist[0] = &aiocbp;
+
+ if( (ret = lseek( fd, 0, SEEK_CUR )) == -1 ){
+ ret = 0;
+ /* If there is an error and it is not ESPIPE then kick out the error.
+ * If the fd is a fifo then we have to make sure that
+ * lio_random_methods() didn't select pwrite/pread; if it did then
+ * switch to write/read.
+ */
+ if( errno == ESPIPE ){
+ if( method & LIO_IO_SYNCP ){
+ if( omethod & LIO_RANDOM ){
+ method &= ~LIO_IO_SYNCP;
+ method |= LIO_IO_SYNC;
+ if( Debug_level > 2 )
+ printf("DEBUG %s/%d: random chosen method switched to %#o for fifo\n", __FILE__, __LINE__, method );
+ }
+ else if( Debug_level ){
+ printf("DEBUG %s/%d: pread will fail when it reads from a fifo\n",
+ __FILE__, __LINE__ );
+ }
+ }
+ /* else: let it ride */
+ }
+ else{
+ sprintf(Errormsg, "%s/%d lseek(fd=%d,0,SEEK_CUR) failed, errno=%d %s",
+ __FILE__, __LINE__, fd, errno, strerror(errno));
+ return -errno;
+ }
+ }
+ poffset = (off64_t)ret;
+ aiocbp.aio_offset = ret;
+
+#endif
+
+ /*
+ * If the LIO_USE_SIGNAL bit is not set, only use the signal
+ * if the LIO_WAIT_SIGPAUSE or the LIO_WAIT_SIGACTIVE bits are set.
+ * Otherwise there is not necessarily a signal handler to trap
+ * the signal.
+ */
+ if ( sig && !(method & LIO_USE_SIGNAL) &&
+ ! (method & LIO_WAIT_SIGTYPES) ){
+
+ sig=0; /* ignore signal parameter */
+ }
+
+#ifdef sgi
+ if ( sig && (method & LIO_WAIT_CBTYPES) )
+ sig=0; /* ignore signal parameter */
+#endif
+
+ /*
+ * only setup signal hander if sig was specified and
+ * a sig wait method was specified.
+ * Doing this will change the handler for this signal. The
+ * old signal handler will not be restored.
+ *** restoring the signal handler could be added ***
+ */
+
+ if ( sig && (method & LIO_WAIT_SIGTYPES) ){
+#ifdef CRAY
+ sigctl(SCTL_REG, sig, lio_async_signal_handler);
+#endif
+#ifdef sgi
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+ aiocbp.aio_sigevent.sigev_signo = sig;
+ sigset(sig, lio_async_signal_handler);
+#endif /* CRAY */
+ }
+#ifdef sgi
+ else if( method & LIO_WAIT_CBTYPES ){
+ aiocbp.aio_sigevent.sigev_notify = SIGEV_CALLBACK;
+ aiocbp.aio_sigevent.sigev_func = lio_async_callback_handler;
+ /* sival_int just has to be something that I can use
+ * to identify the callback, and "size" happens to be handy...
+ */
+ aiocbp.aio_sigevent.sigev_value.sival_int = size;
+ }
+#endif
+
+ /*
+ * Determine the system call that will be called and produce
+ * the string of the system call and place it in Lio_SysCall.
+ * Also update the io_type char pointer to give brief description
+ * of system call. Execute the system call and check for
+ * system call failure. If sync i/o, return the number of
+ * bytes written/read.
+ */
+
+ if ( (method & LIO_IO_SYNC) || (method & LIO_IO_TYPES) == 0 ){
+ /*
+ * read(2) is used if LIO_IO_SYNC bit is set or not none
+ * of the LIO_IO_TYPES bits are set (default).
+ */
+
+ sprintf(Lio_SysCall,
+ "read(%d, buf, %d)", fd, size);
+ io_type="read";
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ if ((ret = read(fd, buffer, size)) == -1) {
+ sprintf(Errormsg, "%s/%d read(%d, buf, %d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, errno, strerror(errno));
+ return -errno;
+ }
+
+ if ( ret != size ) {
+ sprintf(Errormsg,
+ "%s/%d read(%d, buf, %d) returned=%d",
+ __FILE__, __LINE__,
+ fd, size, ret);
+ }
+ else if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: read completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+
+ }
+
+ else if ( method & LIO_IO_ASYNC ) {
+#ifdef CRAY
+ sprintf(Lio_SysCall,
+ "reada(%d, buf, %d, &status, %d)", fd, size, sig);
+ io_type="reada";
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ sigoff();
+ if ((ret = reada(fd, buffer, size, &status, sig)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d reada(%d, buf, %d, &stat, %d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, sig, errno, strerror(errno));
+ sigon();
+ return -errno;
+ }
+#endif
+#ifdef sgi
+ sprintf(Lio_SysCall,
+ "aio_read(fildes=%d, buf, nbytes=%d, signo=%d)", fd, size, sig);
+ io_type="aio_read";
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ if( sig )
+ sighold( sig );
+ if ((ret = aio_read(&aiocbp)) == -1) {
+ sprintf(Errormsg,
+ "%s/%d aio_read(fildes=%d, buf, nbytes=%d, signo=%d) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, sig, errno, strerror(errno));
+ if( sig )
+ sigrelse( sig );
+ return -errno;
+ }
+#endif
+ } /* LIO_IO_ASYNC */
+
+ else if ( method & LIO_IO_SLISTIO ) {
+#ifdef CRAY
+ request.li_opcode = LO_READ;
+ request.li_fildes = fd;
+ request.li_buf = buffer;
+ request.li_nbyte = size;
+ request.li_status = &status;
+ request.li_signo = sig;
+ request.li_nstride = 0;
+ request.li_filstride = 0;
+ request.li_memstride = 0;
+
+ listio_cmd=LC_WAIT;
+ io_type="listio(2) sync read";
+
+ sprintf(Lio_SysCall,
+ "listio(LC_WAIT, &req, 1) LO_READ, fd:%d, nbyte:%d",
+ fd, size);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ sigoff();
+ if ( listio(listio_cmd, &request, 1) == -1 ) {
+ sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__,
+ Lio_SysCall, fd, size, errno, strerror(errno));
+ sigon();
+ return -errno;
+ }
+
+ if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: %s did not return -1\n",
+ __FILE__, __LINE__, Lio_SysCall);
+
+ ret=lio_check_asyncio(io_type, size, &status);
+ return ret;
+#endif
+#ifdef sgi
+ aiocbp.aio_lio_opcode = LIO_READ;
+ listio_cmd=LIO_WAIT;
+ io_type="lio_listio(3) sync read";
+
+ sprintf(Lio_SysCall,
+ "lio_listio(LIO_WAIT, aiolist, 1, NULL) LIO_READ, fd:%d, nbyte:%d",
+ fd, size);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ if( sig )
+ sighold( sig );
+ if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) {
+ sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__,
+ Lio_SysCall, fd, size, errno, strerror(errno));
+ if( sig )
+ sigrelse( sig );
+ return -errno;
+ }
+
+ if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: %s did not return -1\n",
+ __FILE__, __LINE__, Lio_SysCall);
+
+ ret=lio_check_asyncio(io_type, size, &aiocbp, method);
+ return ret;
+#endif
+ }/* LIO_IO_SLISTIO */
+
+ else if ( method & LIO_IO_ALISTIO ) {
+#ifdef CRAY
+ request.li_opcode = LO_READ;
+ request.li_fildes = fd;
+ request.li_buf = buffer;
+ request.li_nbyte = size;
+ request.li_status = &status;
+ request.li_signo = sig;
+ request.li_nstride = 0;
+ request.li_filstride = 0;
+ request.li_memstride = 0;
+
+ listio_cmd=LC_START;
+ io_type="listio(2) async read";
+
+ sprintf(Lio_SysCall,
+ "listio(LC_START, &req, 1) LO_READ, fd:%d, nbyte:%d",
+ fd, size);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ sigoff();
+ if ( listio(listio_cmd, &request, 1) == -1 ) {
+ sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__,
+ Lio_SysCall, fd, size, errno, strerror(errno));
+ sigon();
+ return -errno;
+ }
+#endif
+#ifdef sgi
+ aiocbp.aio_lio_opcode = LIO_READ;
+ listio_cmd=LIO_NOWAIT;
+ io_type="lio_listio(3) async read";
+
+ sprintf(Lio_SysCall,
+ "lio_listio(LIO_NOWAIT, aiolist, 1, NULL) LIO_READ, fd:%d, nbyte:%d",
+ fd, size);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+
+ if( sig )
+ sighold( sig );
+ if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) {
+ sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s",
+ __FILE__, __LINE__,
+ Lio_SysCall, fd, size, errno, strerror(errno));
+ if( sig )
+ sigrelse( sig );
+ return -errno;
+ }
+#endif
+ } /* LIO_IO_ALISTIO */
+
+#ifndef CRAY
+ else if ( method & LIO_IO_SYNCV ) {
+ io_type="readv(2)";
+
+ sprintf(Lio_SysCall,
+ "readv(%d, &iov, 1) nbyte:%d", fd, size);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+ if ((ret = readv(fd, &iov, 1)) == -1) {
+ sprintf(Errormsg, "%s/%d readv(%d, iov, 1) nbyte:%d ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, errno, strerror(errno));
+ return -errno;
+ }
+
+ if ( ret != size ) {
+ sprintf(Errormsg,
+ "%s/%d readv(%d, iov, 1) nbyte:%d returned=%d",
+ __FILE__, __LINE__,
+ fd, size, ret);
+ }
+ else if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: readv completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ } /* LIO_IO_SYNCV */
+#endif
+
+#ifdef sgi
+ else if ( method & LIO_IO_SYNCP ) {
+ io_type="pread(2)";
+
+ sprintf(Lio_SysCall,
+ "pread(%d, buf, %d, %lld)", fd, size, poffset);
+
+ if ( Debug_level ) {
+ printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall);
+ }
+ if ((ret = pread(fd, buffer, size, poffset)) == -1) {
+ sprintf(Errormsg, "%s/%d pread(%d, buf, %d, %lld) ret:-1, errno=%d %s",
+ __FILE__, __LINE__,
+ fd, size, poffset, errno, strerror(errno));
+ return -errno;
+ }
+
+ if ( ret != size ) {
+ sprintf(Errormsg,
+ "%s/%d pread(%d, buf, %d, %lld) returned=%d",
+ __FILE__, __LINE__,
+ fd, size, poffset, ret);
+ }
+ else if ( Debug_level > 1 )
+ printf("DEBUG %s/%d: pread completed without error (ret %d)\n",
+ __FILE__, __LINE__, ret);
+
+ return ret;
+ } /* LIO_IO_SYNCP */
+#endif
+
+ else {
+ printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__, __LINE__ );
+ return -1;
+ }
+
+ /*
+ * wait for async io to complete.
+ * Note: Sync io should have returned prior to getting here.
+ */
+#ifdef CRAY
+ ret=lio_wait4asyncio(method, fd, statptr);
+#endif
+#ifdef sgi
+ ret=lio_wait4asyncio(method, fd, &aiocbp);
+#endif
+
+ /*
+ * If there was an error waiting for async i/o to complete,
+ * return the error value (errno) to the caller.
+ * Note: Errormsg should already have been updated.
+ */
+ if ( ret < 0 ) {
+ return ret;
+ }
+
+ /*
+ * If i/o was not waited for (may not have been completed at this time),
+ * return the size that was requested.
+ */
+ if ( ret == 1 )
+ return size;
+
+ /*
+ * check that async io was successful.
+ * Note: if the there was an system call failure, -errno
+ * was returned and Errormsg should already have been updated.
+ * If amount i/o was different than size, Errormsg should already
+ * have been updated but the actual i/o size if returned.
+ */
+
+#ifdef CRAY
+ ret=lio_check_asyncio(io_type, size, &status);
+#endif
+#ifdef sgi
+ ret=lio_check_asyncio(io_type, size, &aiocbp, method);
+#endif
+
+ return ret;
+} /* end of lio_read_buffer */
+
+
+#ifndef linux
+/***********************************************************************
+ * This function will check that async io was successful.
+ * It can also be used to check sync listio since it uses the
+ * same method.
+ *
+ * Return Values
+ * If status.sw_error is set, -status.sw_error is returned.
+ * Otherwise sw_count's field value is returned.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+#ifdef CRAY
+lio_check_asyncio(char *io_type, int size, struct iosw *status)
+#else
+lio_check_asyncio(char *io_type, int size, aiocb_t *aiocbp, int method)
+#endif
+{
+ int ret;
+
+#ifdef CRAY
+ if ( status->sw_error ) {
+ sprintf(Errormsg,
+ "%s/%d %s, sw_error set = %d %s, sw_count = %d",
+ __FILE__, __LINE__, io_type,
+ status->sw_error, strerror(status->sw_error), status->sw_count);
+ return -status->sw_error;
+ }
+ else if ( status->sw_count != size ) {
+ sprintf(Errormsg,
+ "%s/%d %s, sw_count not as expected(%d), but actual:%d",
+ __FILE__, __LINE__, io_type,
+ size, status->sw_count);
+ }
+ else if ( Debug_level > 1 ) {
+ printf("DEBUG %s/%d: %s completed without error (sw_error == 0, sw_count == %d)\n",
+ __FILE__, __LINE__, io_type, status->sw_count);
+ }
+
+ return status->sw_count;
+
+#else
+
+ int cnt = 1;
+
+ /* The I/O may have been synchronous with signal completion. It doesn't
+ * make sense, but the combination could be generated. Release the
+ * completion signal here otherwise it'll hang around and bite us
+ * later.
+ */
+ if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL )
+ sigrelse( aiocbp->aio_sigevent.sigev_signo );
+
+ ret = aio_error( aiocbp );
+
+ while( ret == EINPROGRESS ){
+ ret = aio_error( aiocbp );
+ ++cnt;
+ }
+ if( cnt > 1 ){
+ sprintf(Errormsg,
+ "%s/%d %s, aio_error had to loop on EINPROGRESS, cnt=%d; random method %#o; sigev_notify=%s",
+ __FILE__, __LINE__, io_type, cnt, method,
+ (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ? "signal" :
+ aiocbp->aio_sigevent.sigev_notify == SIGEV_NONE ? "none" :
+ aiocbp->aio_sigevent.sigev_notify == SIGEV_CALLBACK ? "callback" :
+ "unknown") );
+ return -ret;
+ }
+
+ if( ret != 0 ){
+ sprintf(Errormsg,
+ "%s/%d %s, aio_error = %d %s; random method %#o",
+ __FILE__, __LINE__, io_type,
+ ret, strerror(ret),
+ method );
+ return -ret;
+ }
+ ret = aio_return( aiocbp );
+ if( ret != size ){
+ sprintf(Errormsg,
+ "%s/%d %s, aio_return not as expected(%d), but actual:%d",
+ __FILE__, __LINE__, io_type,
+ size, ret);
+
+#ifdef BUG1_workaround
+ if( ret == 0 ){
+ ret = size;
+ if( Debug_level > 1 ){
+ printf("WARN %s/%d: %s completed with bug1_workaround (aio_error == 0, aio_return now == %d)\n",
+ __FILE__, __LINE__, io_type, ret);
+ }
+ }
+#endif /* BUG1_workaround */
+
+ }
+ else if( Debug_level > 1 ){
+ printf("DEBUG %s/%d: %s completed without error (aio_error == 0, aio_return == %d)\n",
+ __FILE__, __LINE__, io_type, ret);
+ }
+
+ return ret;
+
+#endif
+
+} /* end of lio_check_asyncio */
+
+
+/***********************************************************************
+ *
+ * This function will wait for async io to complete.
+ * If multiple wait methods are specified, the order is predetermined
+ * to LIO_WAIT_RECALL,
+ * LIO_WAIT_ACTIVE, LIO_WAIT_SIGPAUSE, LIO_WAIT_SIGACTIVE,
+ * then LIO_WAIT_NONE.
+ *
+ * If no wait method was specified the default wait method is: recall(2)
+ * or aio_suspend(3), as appropriate.
+ *
+ * Return Values
+ * <0: errno of failed recall
+ * 0 : async io was completed
+ * 1 : async was not waited for, io may not have completed.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+int
+#ifdef CRAY
+lio_wait4asyncio(int method, int fd, struct iosw **statptr)
+#else
+lio_wait4asyncio(int method, int fd, aiocb_t *aiocbp)
+#endif
+{
+ int cnt;
+#ifdef sgi
+ int ret;
+ const aiocb_t *aioary[1];
+#endif
+
+ if ( (method & LIO_WAIT_RECALL)
+#ifdef sgi
+ || (method & LIO_WAIT_CBSUSPEND)
+ || (method & LIO_WAIT_SIGSUSPEND)
+#endif
+ || ((method & LIO_WAIT_TYPES) == 0) ){
+ /*
+ * If method has LIO_WAIT_RECALL bit set or method does
+ * not have any wait method bits set (default), use recall/aio_suspend.
+ */
+#ifdef CRAY
+ if ( Debug_level > 2 )
+ printf("DEBUG %s/%d: wait method : recall\n", __FILE__, __LINE__);
+ sigon();
+ if ( recall(fd, 1, statptr) ) {
+ sprintf(Errormsg, "%s/%d recall(%d, 1, stat) failed, errno:%d %s",
+ __FILE__, __LINE__,
+ fd, errno, strerror(errno));
+ return -errno;
+ }
+#else
+ if ( Debug_level > 2 )
+ printf("DEBUG %s/%d: wait method : aio_suspend, sigev_notify=%s\n", __FILE__, __LINE__,
+ (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ? "signal" :
+ aiocbp->aio_sigevent.sigev_notify == SIGEV_NONE ? "none" :
+ aiocbp->aio_sigevent.sigev_notify == SIGEV_CALLBACK ? "callback" :
+ "unknown") );
+
+ aioary[0] = aiocbp;
+ ret = aio_suspend( aioary, 1, NULL );
+ if( (ret == -1) && (errno == EINTR) ){
+ if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ){
+ if( Debug_level > 2 ){
+ printf("DEBUG %s/%d: aio_suspend received EINTR, sigev_notify=SIGEV_SIGNAL -- ok\n",
+ __FILE__, __LINE__ );
+ }
+ }
+ else {
+ sprintf(Errormsg, "%s/%d aio_suspend received EINTR, sigev_notify=%s, not ok\n",
+ __FILE__, __LINE__,
+ (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ? "signal" :
+ aiocbp->aio_sigevent.sigev_notify == SIGEV_NONE ? "none" :
+ aiocbp->aio_sigevent.sigev_notify == SIGEV_CALLBACK ? "callback" :
+ "unknown") );
+ return -errno;
+ }
+ }
+ else if ( ret ) {
+ sprintf(Errormsg, "%s/%d aio_suspend(fildes=%d, aioary, 1, NULL) failed, errno:%d %s",
+ __FILE__, __LINE__,
+ fd, errno, strerror(errno));
+ return -errno;
+ }
+#endif
+
+ } else if ( method & LIO_WAIT_ACTIVE ) {
+ if ( Debug_level > 2 )
+ printf("DEBUG %s/%d: wait method : active\n", __FILE__, __LINE__);
+#ifdef CRAY
+ sigon();
+ /*
+ * loop until sw_flag, sw_count or sw_error field elements
+ * change to non-zero.
+ */
+ cnt=0;
+ while ( (*statptr)->sw_flag == 0 &&
+ (*statptr)->sw_count == 0 &&
+ (*statptr)->sw_error == 0 ) {
+ cnt++;
+ }
+#else
+ /* loop while aio_error() returns EINPROGRESS */
+ cnt=0;
+ while(1){
+ ret = aio_error( aiocbp );
+ if( (ret == 0) || (ret != EINPROGRESS) ){
+ break;
+ }
+ ++cnt;
+ }
+
+#endif
+ if ( Debug_level > 5 && cnt && (cnt % 50) == 0 )
+ printf("DEBUG %s/%d: wait active cnt = %d\n",
+ __FILE__, __LINE__, cnt);
+
+ } else if ( method & LIO_WAIT_SIGPAUSE ) {
+ if ( Debug_level > 2 )
+ printf("DEBUG %s/%d: wait method : sigpause\n", __FILE__, __LINE__);
+#ifdef sgi
+ /* note: don't do the sigon() for CRAY in this case. why? -- roehrich 6/11/97 */
+ if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL )
+ sigrelse( aiocbp->aio_sigevent.sigev_signo );
+ else {
+ printf("DEBUG %s/%d: sigev_notify != SIGEV_SIGNAL\n", __FILE__, __LINE__ );
+ return -1;
+ }
+#endif
+ pause();
+
+ } else if ( method & LIO_WAIT_SIGACTIVE ) {
+ if ( Debug_level > 2 )
+ printf("DEBUG %s/%d: wait method : sigactive\n", __FILE__, __LINE__);
+#ifdef CRAY
+ sigon();
+#else
+ if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL )
+ sigrelse( aiocbp->aio_sigevent.sigev_signo );
+ else {
+ printf("DEBUG %s/%d: sigev_notify != SIGEV_SIGNAL\n", __FILE__, __LINE__ );
+ return -1;
+ }
+#endif
+ /* loop waiting for signal */
+ while ( Received_signal == Rec_signal ){
+#ifdef CRAY
+ sigon();
+#else
+ sigrelse( aiocbp->aio_sigevent.sigev_signo );
+#endif
+ }
+
+ } else if ( method & LIO_WAIT_NONE ) {
+ if ( Debug_level > 2 )
+ printf("DEBUG %s/%d: wait method : none\n", __FILE__, __LINE__);
+ /* It's broken because the aiocb/iosw is an automatic variable in
+ * lio_{read,write}_buffer, so when the function returns and the
+ * I/O completes there will be nowhere to write the I/O status.
+ * It doesn't cause a problem on unicos--probably because of some
+ * compiler quirk, or an accident. It causes POSIX async I/O
+ * to core dump some threads. spr/pv 705909. 6/27/97 roehrich
+ */
+ sprintf(Errormsg, "%s/%d LIO_WAIT_NONE was selected (this is broken)\n",
+ __FILE__, __LINE__ );
+#ifdef CRAY
+ sigon();
+#endif
+/* return 1;*/
+ return -1;
+ }
+ else {
+ if( Debug_level > 2 )
+ printf("DEBUG %s/%d: no wait method was chosen\n", __FILE__, __LINE__ );
+ return -1;
+ }
+
+ return 0;
+
+} /* end of lio_wait4asyncio */
+
+#endif /* ifndef linux */
+
+#if UNIT_TEST
+/***********************************************************************
+ * The following code is provided as unit test.
+ * Just define add "-DUNIT_TEST=1" to the cc line.
+ *
+ * (rrl 04/96)
+ ***********************************************************************/
+struct unit_info_t {
+ int method;
+ int sig;
+ char *str;
+} Unit_info[] = {
+ { LIO_IO_SYNC, 0, "sync io" },
+ { LIO_IO_SYNCV, 0, "sync readv/writev" },
+ { LIO_IO_SYNCP, 0, "sync pread/pwrite" },
+ { LIO_IO_ASYNC, 0, "async io, def wait" },
+ { LIO_IO_SLISTIO, 0, "sync listio" },
+ { LIO_IO_ALISTIO, 0, "async listio, def wait" },
+ { LIO_IO_ASYNC|LIO_WAIT_ACTIVE, 0, "async active" },
+ { LIO_IO_ASYNC|LIO_WAIT_RECALL, 0, "async recall/suspend" },
+ { LIO_IO_ASYNC|LIO_WAIT_SIGPAUSE, SIGUSR1, "async sigpause" },
+ { LIO_IO_ASYNC|LIO_WAIT_SIGACTIVE, SIGUSR1, "async sigactive" },
+ { LIO_IO_ALISTIO|LIO_WAIT_ACTIVE, 0, "async listio active" },
+ { LIO_IO_ALISTIO|LIO_WAIT_RECALL, 0, "async listio recall" },
+ { LIO_IO_ALISTIO|LIO_WAIT_SIGACTIVE, SIGUSR1, "async listio sigactive" },
+ { LIO_IO_ALISTIO|LIO_WAIT_SIGPAUSE, SIGUSR1, "async listio sigpause" },
+ { LIO_IO_ASYNC, SIGUSR2, "async io, def wait, sigusr2" },
+ { LIO_IO_ALISTIO, SIGUSR2, "async listio, def wait, sigusr2" },
+};
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+
+ int fd;
+ char *err;
+ char buffer[4096];
+ int size=4096;
+ int ret;
+ int ind;
+ int iter=3;
+ int method;
+ int exit_status = 0;
+ int c;
+ int i;
+ char *symbols = NULL;
+ int die_on_err = 0;
+
+ while( (c = getopt(argc,argv,"s:di:")) != -1 ){
+ switch(c){
+ case 's': symbols = optarg; break;
+ case 'd': ++die_on_err; break;
+ case 'i': iter = atoi(optarg); break;
+ }
+ }
+
+ if ((fd=open("unit_test_file", O_CREAT|O_RDWR|O_TRUNC, 0777)) == -1 ) {
+ perror("open(unit_test_file, O_CREAT|O_RDWR|O_TRUNC, 0777) failed");
+ exit(1);
+ }
+
+ Debug_level=9;
+
+ if ( symbols != NULL ) {
+ if ( (method=lio_parse_io_arg2(symbols, &err)) == -1 ){
+ printf("lio_parse_io_arg2(%s, &err) failed, bad token starting at %s\n",
+ symbols, err);
+ if( die_on_err )
+ exit(1);
+ }
+ else
+ printf("lio_parse_io_arg2(%s, &err) returned %#o\n", symbols, method);
+
+ exit_status = 0;
+ for(ind=0; ind < iter; ind++ ) {
+ memset( buffer, 'A', 4096 );
+ if( lseek(fd, 0, 0) == -1 ){
+ printf("lseek(fd,0,0), %d, failed, errno %d\n",
+ __LINE__, errno );
+ ++exit_status;
+ }
+ if ((ret=lio_write_buffer(fd, method, buffer,
+ size, SIGUSR1, &err, 0)) != size ) {
+ printf("lio_write_buffer returned -1, err = %s\n", err);
+ } else
+ printf("lio_write_buffer returned %d\n", ret);
+
+ memset( buffer, 'B', 4096 );
+ if( lseek(fd, 0, 0) == -1 ){
+ printf("lseek(fd,0,0), %d, failed, errno %d\n",
+ __LINE__, errno );
+ ++exit_status;
+ }
+ if ((ret=lio_read_buffer(fd, method, buffer,
+ size, SIGUSR2, &err, 0)) != size ) {
+ printf("lio_read_buffer returned -1, err = %s\n", err);
+ } else
+ printf("lio_read_buffer returned %d\n", ret);
+
+ for( i = 0; i < 4096; ++i ){
+ if( buffer[i] != 'A' ){
+ printf(" buffer[%d] = %d\n", i, buffer[i] );
+ ++exit_status;
+ break;
+ }
+ }
+
+ if( exit_status )
+ exit(exit_status);
+
+ }
+
+ unlink("unit_test_file");
+ exit(0);
+ }
+
+ for(ind=0; ind < sizeof(Unit_info)/sizeof(struct unit_info_t); ind++ ) {
+
+ printf("\n********* write %s ***************\n", Unit_info[ind].str);
+ if( lseek(fd, 0, 0) == -1 ){
+ printf("lseek(fd,0,0), %d, failed, errno %d\n",
+ __LINE__, errno );
+ ++exit_status;
+ }
+
+ memset( buffer, 'A', 4096 );
+ if ((ret=lio_write_buffer(fd, Unit_info[ind].method, buffer,
+ size, Unit_info[ind].sig, &err, 0)) != size ) {
+ printf(">>>>> lio_write_buffer(fd,0%x,buffer,%d,%d,err,0) returned -1,\n err = %s\n",
+ Unit_info[ind].method, size, Unit_info[ind].sig, err);
+ ++exit_status;
+ if( die_on_err )
+ exit(exit_status);
+ } else{
+ printf("lio_write_buffer returned %d\n", ret);
+ }
+
+ printf("\n********* read %s ***************\n", Unit_info[ind].str);
+ if( lseek(fd, 0, 0) == -1 ){
+ printf("lseek(fd,0,0), %d, failed, errno %d\n",
+ __LINE__, errno );
+ ++exit_status;
+ }
+ memset( buffer, 'B', 4096 );
+ if ((ret=lio_read_buffer(fd, Unit_info[ind].method, buffer,
+ size, Unit_info[ind].sig, &err, 0)) != size ) {
+ printf(">>>>> lio_read_buffer(fd,0%x,buffer,%d,%d,err,0) returned -1,\n err = %s\n",
+ Unit_info[ind].method, size, Unit_info[ind].sig, err);
+ ++exit_status;
+ if( die_on_err )
+ exit(exit_status);
+ } else {
+ printf("lio_read_buffer returned %d\n", ret);
+ }
+
+ for( i = 0; i < 4096; ++i ){
+ if( buffer[i] != 'A' ){
+ printf(" buffer[%d] = %d\n", i, buffer[i] );
+ ++exit_status;
+ if( die_on_err )
+ exit(exit_status);
+ break;
+ }
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+ sleep(1);
+
+ }
+
+ unlink("unit_test_file");
+
+ exit(exit_status);
+}
+#endif
diff --git a/lib/write_log.c b/lib/write_log.c
new file mode 100644
index 0000000..71025a3
--- /dev/null
+++ b/lib/write_log.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ */
+/*
+ * This module contains code for logging writes to files, and for
+ * perusing the resultant logfile. The main intent of all this is
+ * to provide a 'write history' of a file which can be examined to
+ * judge the state of a file (ie. whether it is corrupted or not) based
+ * on the write activity.
+ *
+ * The main abstractions available to the user are the wlog_file, and
+ * the wlog_rec. A wlog_file is a handle encapsulating a write logfile.
+ * It is initialized with the wlog_open() function. This handle is
+ * then passed to the various wlog_xxx() functions to provide transparent
+ * access to the write logfile.
+ *
+ * The wlog_rec datatype is a structure which contains all the information
+ * about a file write. Examples include the file name, offset, length,
+ * pattern, etc. In addition there is a bit which is cleared/set based
+ * on whether or not the write has been confirmed as complete. This
+ * allows the write logfile to contain information on writes which have
+ * been initiated, but not yet completed (as in async io).
+ *
+ * There is also a function to scan a write logfile in reverse order.
+ *
+ * NOTE: For target file analysis based on a write logfile, the
+ * assumption is made that the file being written to is
+ * locked from simultaneous access, so that the order of
+ * write completion is predictable. This is an issue when
+ * more than 1 process is trying to write data to the same
+ * target file simultaneously.
+ *
+ * The history file created is a collection of variable length records
+ * described by scruct wlog_rec_disk in write_log.h. See that module for
+ * the layout of the data on disk.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "write_log.h"
+
+#ifndef BSIZE
+#ifdef linux
+#define BSIZE DEV_BSIZE
+#else
+#define BSIZE BBSIZE
+#endif
+#endif
+
+#ifndef PATH_MAX
+#define PATH_MAX 255
+/*#define PATH_MAX pathconf("/", _PC_PATH_MAX)*/
+#endif
+
+#ifndef linux
+extern char *sys_errlist[];
+#endif
+#define SYSERR sys_errlist[errno]
+
+char Wlog_Error_String[256];
+
+#if __STDC__
+static int wlog_rec_pack(struct wlog_rec *wrec, char *buf, int flag);
+static int wlog_rec_unpack(struct wlog_rec *wrec, char *buf);
+#else
+static int wlog_rec_pack();
+static int wlog_rec_unpack();
+#endif
+
+/*
+ * Initialize a write logfile. wfile is a wlog_file structure that has
+ * the w_file field filled in. The rest of the information in the
+ * structure is initialized by the routine.
+ *
+ * The trunc flag is used to indicate whether or not the logfile should
+ * be truncated if it currently exists. If it is non-zero, the file will
+ * be truncated, otherwise it will be appended to.
+ *
+ * The mode argument is the [absolute] mode which the file will be
+ * given if it does not exist. This mode is not affected by your process
+ * umask.
+ */
+
+int
+wlog_open(wfile, trunc, mode)
+struct wlog_file *wfile;
+int trunc;
+int mode;
+{
+ int omask, oflags;
+
+ if (trunc)
+ trunc = O_TRUNC;
+
+ omask = umask(0);
+
+ /*
+ * Open 1 file descriptor as O_APPEND
+ */
+
+ oflags = O_WRONLY | O_APPEND | O_CREAT | trunc;
+ wfile->w_afd =
+ open(wfile->w_file, oflags, mode);
+ umask(omask);
+
+ if (wfile->w_afd == -1) {
+ sprintf(Wlog_Error_String,
+ "Could not open write_log - open(%s, %#o, %#o) failed: %s\n",
+ wfile->w_file, oflags, mode, SYSERR);
+ return -1;
+ }
+
+ /*
+ * Open the next fd as a random access descriptor
+ */
+
+ oflags = O_RDWR;
+ if ((wfile->w_rfd = open(wfile->w_file, oflags)) == -1) {
+ sprintf(Wlog_Error_String,
+ "Could not open write log - open(%s, %#o) failed: %s\n",
+ wfile->w_file, oflags, SYSERR);
+ close(wfile->w_afd);
+ wfile->w_afd = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Release all resources associated with a wlog_file structure allocated
+ * with the wlog_open() call.
+ */
+
+int
+wlog_close(wfile)
+struct wlog_file *wfile;
+{
+ close(wfile->w_afd);
+ close(wfile->w_rfd);
+ return 0;
+}
+
+/*
+ * Write a wlog_rec structure to a write logfile. Offset is used to
+ * control where the record will be written. If offset is < 0, the
+ * record will be appended to the end of the logfile. Otherwise, the
+ * record which exists at the indicated offset will be overlayed. This
+ * is so that we can record writes which are outstanding (with the w_done
+ * bit in wrec cleared), but not completed, and then later update the
+ * logfile when the write request completes (as with async io). When
+ * offset is >= 0, only the fixed length portion of the record is
+ * rewritten. See text in write_log.h for details on the format of an
+ * on-disk record.
+ *
+ * The return value of the function is the byte offset in the logfile
+ * where the record begins.
+ *
+ * Note: It is the callers responsibility to make sure that the offset
+ * parameter 'points' to a valid record location when a record is to be
+ * overlayed. This is guarenteed by saving the return value of a previous
+ * call to wlog_record_write() which wrote the record to be overlayed.
+ *
+ * Note2: The on-disk version of the wlog_rec is MUCH different than
+ * the user version. Don't expect to od the logfile and see data formatted
+ * as it is in the wlog_rec structure. Considerable data packing takes
+ * place before the record is written.
+ */
+
+int
+wlog_record_write(wfile, wrec, offset)
+struct wlog_file *wfile;
+struct wlog_rec *wrec;
+long offset;
+{
+ int reclen;
+ char wbuf[WLOG_REC_MAX_SIZE + 2];
+
+ /*
+ * If offset is -1, we append the record at the end of file
+ *
+ * Otherwise, we overlay wrec at the file offset indicated and assume
+ * that the caller passed us the correct offset. We do not record the
+ * fname in this case.
+ */
+
+ reclen = wlog_rec_pack(wrec, wbuf, (offset < 0));
+
+ if (offset < 0) {
+ /*
+ * Since we're writing a complete new record, we must also tack
+ * its length onto the end so that wlog_scan_backward() will work.
+ * Length is asumed to fit into 2 bytes.
+ */
+
+ wbuf[reclen] = reclen / 256;
+ wbuf[reclen+1] = reclen % 256;
+ reclen += 2;
+
+ write(wfile->w_afd, wbuf, reclen);
+ offset = lseek(wfile->w_afd, 0, SEEK_CUR) - reclen;
+ } else {
+ lseek(wfile->w_rfd, offset, SEEK_SET);
+ write(wfile->w_rfd, wbuf, reclen);
+ }
+
+ return offset;
+}
+
+/*
+ * Function to scan a logfile in reverse order. Wfile is a valid
+ * wlog_file structure initialized by wlog_open(). nrecs is the number
+ * of records to scan (all records are scanned if nrecs is 0). func is
+ * a user-supplied function to call for each record found. The function
+ * will be passed a single parameter - a wlog_rec structure .
+ */
+
+int
+wlog_scan_backward(wfile, nrecs, func, data)
+struct wlog_file *wfile;
+int nrecs;
+int (*func)();
+long data;
+{
+ int fd, leftover, nbytes, offset, recnum, reclen, rval;
+ char buf[BSIZE*32], *bufend, *cp, *bufstart;
+ char fname[PATH_MAX+1], albuf[WLOG_REC_MAX_SIZE];
+ struct wlog_rec wrec;
+
+ fd = wfile->w_rfd;
+
+ /*
+ * Move to EOF. offset will always hold the current file offset
+ */
+
+ lseek(fd, 0, SEEK_END);
+ offset = lseek(fd, 0, SEEK_CUR);
+
+ bufend = buf + sizeof(buf);
+ bufstart = buf;
+
+ recnum = 0;
+ leftover = 0;
+ while ((!nrecs || recnum < nrecs) && offset > 0) {
+ /*
+ * Check for beginning of file - if there aren't enough bytes
+ * remaining to fill buf, adjust bufstart.
+ */
+
+ if (offset + leftover < sizeof(buf)) {
+ bufstart = bufend - (offset + leftover);
+ offset = 0;
+ } else {
+ offset -= sizeof(buf) - leftover;
+ }
+
+ /*
+ * Move to the proper file offset, and read into buf
+ */
+
+ lseek(fd, offset, SEEK_SET);
+ nbytes = read(fd, bufstart, bufend - bufstart - leftover);
+
+ if (nbytes == -1) {
+ sprintf(Wlog_Error_String,
+ "Could not read history file at offset %d - read(%d, %#lo, %ld) failed: %s\n",
+ offset, fd, bufstart,
+ bufend - bufstart - leftover, SYSERR);
+ return -1;
+ }
+
+ cp = bufend;
+ leftover = 0;
+
+ while (cp >= bufstart) {
+
+ /*
+ * If cp-bufstart is not large enough to hold a piece
+ * of record length information, copy remainder to end
+ * of buf and continue reading the file.
+ */
+
+ if (cp - bufstart < 2) {
+ leftover = cp - bufstart;
+ memcpy(bufend - leftover, bufstart, leftover);
+ break;
+ }
+
+ /*
+ * Extract the record length. We must do it this way
+ * instead of casting cp to an int because cp might
+ * not be word aligned.
+ */
+
+ reclen = (*(cp-2) * 256) + *(cp -1);
+
+ /*
+ * If cp-bufstart isn't large enough to hold a
+ * complete record, plus the length information, copy
+ * the leftover bytes to the end of buf and continue
+ * reading.
+ */
+
+ if (cp - bufstart < reclen + 2) {
+ leftover = cp - bufstart;
+ memcpy(bufend - leftover, bufstart, leftover);
+ break;
+ }
+
+ /*
+ * Adjust cp to point at the start of the record.
+ * Copy the record into wbuf so that it is word
+ * aligned and pass the record to the user supplied
+ * function.
+ */
+
+ cp -= reclen + 2;
+ memcpy(albuf, cp, reclen);
+
+ wlog_rec_unpack(&wrec, albuf);
+
+ /*
+ * Call the user supplied function -
+ * stop if instructed to.
+ */
+
+ if ((rval = (*func)(&wrec, data)) == WLOG_STOP_SCAN) {
+ break;
+ }
+
+ recnum++;
+
+ if (nrecs && recnum >= nrecs)
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The following 2 routines are used to pack and unpack the user
+ * visible wlog_rec structure to/from a character buffer which is
+ * stored or read from the write logfile. Any changes to either of
+ * these routines must be reflected in the other.
+ */
+
+static int
+wlog_rec_pack(wrec, buf, flag)
+struct wlog_rec *wrec;
+char *buf;
+int flag;
+{
+ char *file, *host, *pattern;
+ struct wlog_rec_disk *wrecd;
+
+ wrecd = (struct wlog_rec_disk *)buf;
+
+ wrecd->w_pid = (uint)wrec->w_pid;
+ wrecd->w_offset = (uint)wrec->w_offset;
+ wrecd->w_nbytes = (uint)wrec->w_nbytes;
+ wrecd->w_oflags = (uint)wrec->w_oflags;
+ wrecd->w_done = (uint)wrec->w_done;
+ wrecd->w_async = (uint)wrec->w_async;
+
+ wrecd->w_pathlen = (wrec->w_pathlen > 0) ? (uint)wrec->w_pathlen : 0;
+ wrecd->w_hostlen = (wrec->w_hostlen > 0) ? (uint)wrec->w_hostlen : 0;
+ wrecd->w_patternlen = (wrec->w_patternlen > 0) ? (uint)wrec->w_patternlen : 0;
+
+ /*
+ * If flag is true, we should also pack the variable length parts
+ * of the wlog_rec. By default, we only pack the fixed length
+ * parts.
+ */
+
+ if (flag) {
+ file = buf + sizeof(struct wlog_rec_disk);
+ host = file + wrecd->w_pathlen;
+ pattern = host + wrecd->w_hostlen;
+
+ if (wrecd->w_pathlen > 0)
+ memcpy(file, wrec->w_path, wrecd->w_pathlen);
+
+ if (wrecd->w_hostlen > 0)
+ memcpy(host, wrec->w_host, wrecd->w_hostlen);
+
+ if (wrecd->w_patternlen > 0)
+ memcpy(pattern, wrec->w_pattern, wrecd->w_patternlen);
+
+ return (sizeof(struct wlog_rec_disk) +
+ wrecd->w_pathlen + wrecd->w_hostlen + wrecd->w_patternlen);
+ } else {
+ return sizeof(struct wlog_rec_disk);
+ }
+}
+
+static int
+wlog_rec_unpack(wrec, buf)
+struct wlog_rec *wrec;
+char *buf;
+{
+ char *file, *host, *pattern;
+ struct wlog_rec_disk *wrecd;
+
+ bzero((char *)wrec, sizeof(struct wlog_rec));
+ wrecd = (struct wlog_rec_disk *)buf;
+
+ wrec->w_pid = wrecd->w_pid;
+ wrec->w_offset = wrecd->w_offset;
+ wrec->w_nbytes = wrecd->w_nbytes;
+ wrec->w_oflags = wrecd->w_oflags;
+ wrec->w_hostlen = wrecd->w_hostlen;
+ wrec->w_pathlen = wrecd->w_pathlen;
+ wrec->w_patternlen = wrecd->w_patternlen;
+ wrec->w_done = wrecd->w_done;
+ wrec->w_async = wrecd->w_async;
+
+ if (wrec->w_pathlen > 0) {
+ file = buf + sizeof(struct wlog_rec_disk);
+ memcpy(wrec->w_path, file, wrec->w_pathlen);
+ }
+
+ if (wrec->w_hostlen > 0) {
+ host = buf + sizeof(struct wlog_rec_disk) + wrec->w_pathlen;
+ memcpy(wrec->w_host, host, wrec->w_hostlen);
+ }
+
+ if (wrec->w_patternlen > 0) {
+ pattern = buf + sizeof(struct wlog_rec_disk) +
+ wrec->w_pathlen + wrec->w_hostlen;
+ memcpy(wrec->w_pattern, pattern, wrec->w_patternlen);
+ }
+
+ return 0;
+}