| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2000 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| */ |
| /* |
| * 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 is 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 "global.h" |
| |
| #ifdef HAVE_SYS_FILE_H |
| #include <sys/file.h> |
| #endif |
| |
| #include "dataascii.h" |
| #include "random_range.h" |
| #include "databin.h" |
| #include "open_flags.h" |
| #include "forker.h" |
| #include "file_lock.h" |
| |
| extern int datapidgen(int pid, unsigned char *buffer, int bsize, int offset); |
| extern void databingen(int mode, unsigned char *buffer, int bsize, int offset); |
| extern int datapidchk(int pid, char *buffer, int bsize, int offset, char **errmsg); |
| extern int databinchk(int mode, char *buffer, int bsize, int offset, char **errmsg); |
| |
| int file_size(int fd); |
| int check_write(int fd, int cf_inter, char *filename, int mode); |
| int shrinkfile(int fd, char *filename, int trunc_incr, int trunc_inter, int just_trunc); |
| int check_file(int fd, int cf_inter, char *filename, int no_file_check); |
| int growfile(int fd, char *file, int grow_incr, unsigned char *buf); |
| int cleanup(); |
| int handle_error(); |
| int lkfile(int fd, int operation, int lklevel); |
| void usage(); |
| void help(); |
| void prt_examples(FILE *stream); |
| int set_sig(); |
| void sig_handler(); |
| static void notify_others(); |
| int pre_alloc(char *file, int fd, int size); |
| |
| |
| #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 */ |
| int Pattern=PATTERN_ASCII; |
| 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[] = { |
| 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, |
| |
| }; |
| |
| #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 |
| #define BSIZE 512 |
| #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; |
| int total_grow_value = 0; /* used in pre-allocations */ |
| int backgrnd = 1; /* return control to user */ |
| struct stat statbuf; |
| int time_iterval = -1; |
| time_t start_time = 0; |
| 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; |
| 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 */ |
| printf("%s%s: --p is illegal option on this system\n", |
| Progname, TagName); |
| exit(1); |
| break; |
| |
| case 'P': |
| printf("%s%s: --P is illegal option on non-cray system\n", |
| Progname, TagName); |
| exit(1); |
| 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, "(%.37s)", 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, (int)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, (int)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 ( 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/%s.%d", |
| 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); |
| } |
| |
| 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; |
| } |
| } |
| |
| /* |
| * If delaying between iterations, get amount time to |
| * delaysecs in clocks or usecs. |
| */ |
| if ( delaysecs ) { |
| delaytime=(int)((float)USECS_PER_SEC * delaysecs); |
| } |
| |
| /* |
| * 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, ",", 0)); |
| } 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 */ |
| |
| /* |
| * preallocation is only done once, if specified. |
| */ |
| if ( pre_alloc_space ) { |
| if (pre_alloc(filename, 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; |
| } |
| |
| /* |
| * 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, (unsigned char *)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; |
| 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; |
| } |
| } |
| } |
| /* |
| * 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; |
| } |
| } /* 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 SIGCKPT |
| case SIGCKPT: |
| #endif /* SIGCKPT */ |
| #ifdef SIGRESTART |
| case SIGRESTART: |
| #endif /* SIGRESTART */ |
| case SIGCHLD: |
| break; |
| |
| default: |
| signal(sig, sig_handler); |
| 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); |
| signal(sig, sig_handler); /* allow us to get this signal more than once */ |
| |
| } 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 ) { |
| |
| 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); |
| } |
| } |
| } |
| |
| } |
| |
| /*********************************************************************** |
| * 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++; |
| |
| 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; |
| unsigned char *buf; |
| { |
| int noffset; |
| int ret; |
| /* REFERENCED */ |
| 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, (char *)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, (char *)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, (char *)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; |
| } |
| } |
| |
| 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; |
| |
| 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); |
| } |
| } |
| |
| else { /* remove trunc_incr from file */ |
| |
| new_offset = cur_offset-trunc_incr; |
| |
| if ( new_offset < 0 ) |
| new_offset=0; |
| } |
| |
| 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); |
| } |
| |
| lkfile(fd, LOCK_UN, LKLVL0); |
| |
| if ( ret == -1 ) { |
| fprintf(stderr, "%s%s: %d %s/%d: ftruncate failed: %s\n", |
| Progname, TagName, Pid, __FILE__, __LINE__, strerror(errno)); |
| 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, |
| (int)lseek(fd,SEEK_CUR,0), /* FIXME: 64bit/LFS ? */ |
| (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, ",", 0)); |
| |
| 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; |
| } |
| |
| /*********************************************************************** |
| * |
| ***********************************************************************/ |
| int |
| pre_alloc(file, fd, size) |
| char *file; |
| int fd; |
| int size; |
| { |
| |
| #ifdef XFS_IOC_RESVSP |
| struct xfs_flock64 f; |
| |
| f.l_whence = 0; |
| f.l_start = 0; |
| f.l_len = size; |
| |
| /* non-zeroing reservation */ |
| if( xfsctl( file, fd, XFS_IOC_RESVSP, &f ) == -1 ){ |
| fprintf(stderr, "%s%s %s/%d: Unable to pre-alloc space: xfsctl(XFS_IOC_RESVSP) failed: %d %s\n", |
| Progname, TagName, |
| __FILE__, __LINE__, errno, strerror(errno)); |
| return -1; |
| } |
| #else |
| struct flock64 f; |
| |
| f.l_whence = 0; |
| f.l_start = 0; |
| f.l_len = size; |
| |
| /* non-zeroing reservation */ |
| if( fcntl( fd, F_RESVSP64, &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; |
| } |