863 lines
21 KiB
C
863 lines
21 KiB
C
/*
|
|
* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
|
|
*
|
|
*/
|
|
/* $Header: /cvsroot/ltp/ltp/testcases/kernel/ipc/pipeio/pipeio.c,v 1.18 2009/03/19 07:10:02 subrata_modak Exp $ */
|
|
/*
|
|
* This tool can be used to beat on system or named pipes.
|
|
* See the help() function below for user information.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "tlibio.h"
|
|
|
|
#include "test.h"
|
|
#include "safe_macros.h"
|
|
#include "lapi/sem.h"
|
|
|
|
char *TCID = "pipeio";
|
|
int TST_TOTAL = 1;
|
|
|
|
#define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } }
|
|
|
|
#if defined(__linux__)
|
|
#define NBPW sizeof(int)
|
|
#endif
|
|
|
|
#define OCTAL 'o'
|
|
#define HEX 'x'
|
|
#define DECIMAL 'd'
|
|
#define ASCII 'a'
|
|
#define NO_OUT 'n'
|
|
|
|
#define PIPE_NAMED "named pipe,"
|
|
#define PIPE_UNNAMED "sys pipe,"
|
|
|
|
#define BLOCKING_IO "blking,"
|
|
#define NON_BLOCKING_IO "non-blking,"
|
|
#define UNNAMED_IO ""
|
|
|
|
#define MAX_ERRS 16
|
|
#define MAX_EMPTY 256
|
|
|
|
static int parse_options(int argc, char *argv[]);
|
|
static void setup(int argc, char *argv[]);
|
|
static void cleanup(void);
|
|
|
|
static void do_child(void);
|
|
static void do_parent(void);
|
|
|
|
static void help(void), usage(void), prt_examples(void);
|
|
static void prt_buf(char **addr, char *buf, int length, int format);
|
|
static void sig_child(int sig);
|
|
static int check_rw_buf(void);
|
|
|
|
static volatile sig_atomic_t nchildcompleted;
|
|
|
|
/* variables may be modified in setup() */
|
|
static int num_writers = 1; /* number of writers */
|
|
static int num_writes = 1; /* number of writes per child */
|
|
static int loop; /* loop indefinitely */
|
|
static int exit_error = 1; /* exit on error #, zero means no exit */
|
|
static int size = 327; /* default size */
|
|
static int unpipe; /* un-named pipe if non-zero */
|
|
static int verbose; /* verbose mode if set */
|
|
static int quiet; /* quiet mode if set */
|
|
static int num_rpt; /* ping number, how often to print message */
|
|
static int chld_wait; /* max time to wait between writes, 1 == no wait */
|
|
static int parent_wait; /* max time to wait between reads, 1 == no wait */
|
|
static int ndelay = O_NDELAY; /* additional flag to open */
|
|
static char *writebuf;
|
|
static char *readbuf;
|
|
static char pname[PATH_MAX]; /* contains the name of the named pipe */
|
|
static char *blk_type = NON_BLOCKING_IO; /* blocking i/o or not */
|
|
static char *pipe_type; /* type of pipe under test */
|
|
static int format = HEX;
|
|
static int format_size = -1;
|
|
static int iotype; /* sync io */
|
|
|
|
/* variables will be modified in running */
|
|
static int error;
|
|
static int count;
|
|
static int read_fd;
|
|
static int write_fd;
|
|
static int empty_read;
|
|
static int sem_id;
|
|
|
|
static union semun u;
|
|
|
|
int main(int ac, char *av[])
|
|
{
|
|
int i;
|
|
unsigned int j;
|
|
unsigned int uwait_iter = 1000, uwait_total = 5000000;
|
|
pid_t child;
|
|
|
|
setup(ac, av);
|
|
|
|
for (i = num_writers; i > 0; --i) {
|
|
|
|
child = tst_fork();
|
|
switch (child) {
|
|
case -1:
|
|
tst_brkm(TBROK | TERRNO, cleanup, "fork() failed");
|
|
case 0:
|
|
do_child();
|
|
exit(0);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
do_parent();
|
|
|
|
if (empty_read)
|
|
tst_resm(TWARN, "%d empty reads", empty_read);
|
|
|
|
if (error) {
|
|
tst_resm(TFAIL, "%d data errors on pipe, read size = %d, %s %s",
|
|
error, size, pipe_type, blk_type);
|
|
} else if (!quiet) {
|
|
tst_resm(TPASS, "%d pipe reads complete, read size = %d, %s %s",
|
|
count + 1, size, pipe_type, blk_type);
|
|
}
|
|
|
|
/*
|
|
* wait for all children to finish, timeout after uwait_total
|
|
* semtimedop might not be available everywhere
|
|
*/
|
|
for (j = 0; j < uwait_total; j += uwait_iter) {
|
|
if (semctl(sem_id, 1, GETVAL) == 0)
|
|
break;
|
|
usleep(uwait_iter);
|
|
}
|
|
|
|
if (j >= uwait_total) {
|
|
tst_resm(TWARN,
|
|
"Timed out waiting for child processes to exit");
|
|
}
|
|
|
|
cleanup();
|
|
tst_exit();
|
|
}
|
|
|
|
static int parse_options(int argc, char *argv[])
|
|
{
|
|
char *cp;
|
|
int c;
|
|
int ret = 0;
|
|
static double d;
|
|
|
|
while ((c = getopt(argc, argv, "T:bc:D:he:Ef:i:I:ln:p:qs:uvW:w:"))
|
|
!= -1) {
|
|
switch (c) {
|
|
case 'T':
|
|
TCID = optarg;
|
|
break;
|
|
case 'h':
|
|
help();
|
|
ret = 1;
|
|
break;
|
|
case 'D': /* pipe name */
|
|
strcpy(pname, optarg);
|
|
break;
|
|
case 'b': /* blocked */
|
|
ndelay = 0;
|
|
blk_type = BLOCKING_IO;
|
|
break;
|
|
case 'c': /* number childern */
|
|
if (sscanf(optarg, "%d", &num_writers) != 1) {
|
|
fprintf(stderr,
|
|
"%s: --c option invalid arg '%s'.\n",
|
|
TCID, optarg);
|
|
ret = 1;
|
|
} else if (num_writers <= 0) {
|
|
fprintf(stderr, "%s: --c option must be "
|
|
"greater than zero.\n", TCID);
|
|
ret = 1;
|
|
}
|
|
break;
|
|
case 'e': /* exit on error # */
|
|
if (sscanf(optarg, "%d", &exit_error) != 1) {
|
|
fprintf(stderr,
|
|
"%s: --e option invalid arg '%s'.\n",
|
|
TCID, optarg);
|
|
ret = 1;
|
|
} else if (exit_error < 0) {
|
|
fprintf(stderr, "%s: --e option must be "
|
|
"greater than zero.\n", TCID);
|
|
ret = 1;
|
|
}
|
|
break;
|
|
case 'E':
|
|
prt_examples();
|
|
ret = 1;
|
|
break;
|
|
case 'f': /* format of buffer on error */
|
|
switch (optarg[0]) {
|
|
case 'x':
|
|
case 'X':
|
|
format = HEX;
|
|
break;
|
|
case 'o':
|
|
case 'O':
|
|
format = OCTAL;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
format = DECIMAL;
|
|
break;
|
|
case 'a':
|
|
case 'A':
|
|
format = ASCII;
|
|
break;
|
|
case 'n': /* not output */
|
|
case 'N':
|
|
format = NO_OUT;
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,
|
|
"%s: --f option invalid arg '%s'.\n",
|
|
TCID, optarg);
|
|
fprintf(stderr, "\tIt must be x(hex), o(octal),"
|
|
"d(decimal), a(ascii) or n(none) with "
|
|
"opt sz\n");
|
|
ret = 1;
|
|
break;
|
|
}
|
|
cp = optarg;
|
|
cp++;
|
|
if (*cp) {
|
|
if (sscanf(cp, "%i", &format_size) != 1) {
|
|
fprintf(stderr, "%s: --f option invalid"
|
|
"arg '%s'.\n", TCID, optarg);
|
|
fprintf(stderr, "\tIt must be x(hex),"
|
|
"o(octal), d(decimal), a(ascii)"
|
|
" or n(none) with opt sz\n");
|
|
ret = 1;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'I':
|
|
iotype = lio_parse_io_arg1(optarg);
|
|
if (iotype == -1) {
|
|
fprintf(stderr, "%s: --I arg is invalid, "
|
|
"must be s, p, f, a, l, L or r.\n",
|
|
TCID);
|
|
ret = 1;
|
|
}
|
|
break;
|
|
|
|
case 'l': /* loop forever */
|
|
++loop;
|
|
break;
|
|
|
|
case 'i':
|
|
case 'n': /* number writes per child */
|
|
if (sscanf(optarg, "%d", &num_writes) != 1) {
|
|
fprintf(stderr, "%s: --i/n option invalid "
|
|
"arg '%s'.\n", TCID, optarg);
|
|
ret = 1;
|
|
} else if (num_writes < 0) {
|
|
fprintf(stderr, "%s: --i/n option must be "
|
|
"greater than equal to zero.\n",
|
|
TCID);
|
|
ret = 1;
|
|
}
|
|
|
|
if (num_writes == 0) /* loop forever */
|
|
++loop;
|
|
break;
|
|
case 'p': /* ping */
|
|
if (sscanf(optarg, "%d", &num_rpt) != 1) {
|
|
fprintf(stderr,
|
|
"%s: --p option invalid arg '%s'.\n",
|
|
TCID, optarg);
|
|
ret = 1;
|
|
} else if (num_rpt < 0) {
|
|
fprintf(stderr, "%s: --p option must be greater"
|
|
" than equal to zero.\n", TCID);
|
|
ret = 1;
|
|
}
|
|
break;
|
|
case 'q': /* Quiet - NOPASS */
|
|
quiet = 1;
|
|
break;
|
|
case 's': /* size */
|
|
if (sscanf(optarg, "%d", &size) != 1) {
|
|
fprintf(stderr,
|
|
"%s: --s option invalid arg '%s'.\n",
|
|
TCID, optarg);
|
|
ret = 1;
|
|
} else if (size <= 0) {
|
|
fprintf(stderr, "%s: --s option must be greater"
|
|
" than zero.\n", TCID);
|
|
ret = 1;
|
|
}
|
|
break;
|
|
case 'u':
|
|
unpipe = 1; /* un-named pipe */
|
|
break;
|
|
case 'v': /* verbose */
|
|
verbose = 1;
|
|
break;
|
|
case 'W': /* max wait time between reads */
|
|
d = strtod(optarg, &cp);
|
|
if (*cp != '\0') {
|
|
fprintf(stderr,
|
|
"%s: --w option invalid arg '%s'.\n",
|
|
TCID, optarg);
|
|
ret = 1;
|
|
} else if (d < 0) {
|
|
fprintf(stderr, "%s: --w option must be greater"
|
|
" than zero.\n", TCID);
|
|
ret = 1;
|
|
}
|
|
parent_wait = (int)(d * 1000000.0);
|
|
break;
|
|
case 'w': /* max wait time between writes */
|
|
d = strtod(optarg, &cp);
|
|
if (*cp != '\0') {
|
|
fprintf(stderr,
|
|
"%s: --w option invalid arg '%s'.\n",
|
|
TCID, optarg);
|
|
ret = 1;
|
|
} else if (d < 0) {
|
|
fprintf(stderr, "%s: --w option must be greater"
|
|
" than zero.\n", TCID);
|
|
ret = 1;
|
|
}
|
|
chld_wait = (int)(d * 1000000.0);
|
|
break;
|
|
case '?':
|
|
ret = 1;
|
|
break;
|
|
}
|
|
|
|
if (ret == 1) {
|
|
usage();
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void setup(int argc, char *argv[])
|
|
{
|
|
int ret;
|
|
char *toutput;
|
|
int fds[2];
|
|
|
|
tst_sig(FORK, DEF_HANDLER, cleanup);
|
|
|
|
TEST_PAUSE;
|
|
|
|
tst_tmpdir();
|
|
|
|
if (signal(SIGCHLD, sig_child) == SIG_ERR) {
|
|
tst_brkm(TBROK | TERRNO, cleanup,
|
|
"set signal handler for SIGCHLD failed");
|
|
}
|
|
|
|
toutput = getenv("TOUTPUT");
|
|
if (toutput != NULL && strcmp(toutput, "NOPASS") == 0)
|
|
quiet = 1;
|
|
|
|
sprintf(pname, "%s", "tpipe");
|
|
|
|
ret = parse_options(argc, argv);
|
|
if (ret == 1)
|
|
tst_brkm(TBROK, cleanup, "options parse error");
|
|
|
|
if (format_size == -1)
|
|
format_size = size;
|
|
|
|
/*
|
|
* If there is more than one writer, all writes and reads
|
|
* must be the same size. Only writes of a size <= PIPE_BUF
|
|
* are atomic. T
|
|
* Therefore, if size is greater than PIPE_BUF, we will break
|
|
* the writes into PIPE_BUF chunks. We will also increase the
|
|
* number of writes to ensure the same (or more) amount of
|
|
* data is written. This is the same as erroring and telling
|
|
* the user the new cmd line to do the same thing.
|
|
* Example:
|
|
* pipeio -s 5000 -n 10 -c 5
|
|
* (each child will write at least 50000 bytes, since all
|
|
* writes have to be in 4096 chuncks or 13*4096 (53248)
|
|
* bytes will be written.) This is the same as:
|
|
* pipeio -s 4096 -n 13 -c 5
|
|
*/
|
|
if (size > PIPE_BUF && num_writers > 1) {
|
|
if (!loop) {
|
|
/*
|
|
* we must set num_writes*num_writers
|
|
* doesn't overflow later
|
|
*/
|
|
num_writes = MIN(((long long)num_writes * size +
|
|
PIPE_BUF - 1) / PIPE_BUF,
|
|
INT_MAX / num_writers);
|
|
tst_resm(TINFO, "adjusting i/o size to %d, and # of "
|
|
"writes to %d", PIPE_BUF, num_writes);
|
|
} else {
|
|
tst_resm(TINFO, "adjusting i/o size to %d", PIPE_BUF);
|
|
}
|
|
size = PIPE_BUF;
|
|
}
|
|
|
|
writebuf = SAFE_MALLOC(cleanup, size);
|
|
readbuf = SAFE_MALLOC(cleanup, size);
|
|
|
|
memset(writebuf, 'Z', size);
|
|
writebuf[size - 1] = 'A';
|
|
|
|
sem_id = semget(IPC_PRIVATE, 2, IPC_CREAT | S_IRWXU);
|
|
if (sem_id == -1) {
|
|
tst_brkm(TBROK | TERRNO, cleanup,
|
|
"Couldn't allocate semaphore");
|
|
}
|
|
|
|
if (semctl(sem_id, 0, SETVAL, u) == -1) {
|
|
tst_brkm(TBROK | TERRNO, cleanup,
|
|
"Couldn't initialize semaphore 0 value");
|
|
}
|
|
|
|
if (semctl(sem_id, 1, SETVAL, u) == -1) {
|
|
tst_brkm(TBROK | TERRNO, cleanup,
|
|
"Couldn't initialize semaphore 1 value");
|
|
}
|
|
|
|
if (unpipe) {
|
|
SAFE_PIPE(cleanup, fds);
|
|
read_fd = fds[0];
|
|
write_fd = fds[1];
|
|
pipe_type = PIPE_UNNAMED;
|
|
blk_type = UNNAMED_IO;
|
|
} else {
|
|
SAFE_MKFIFO(cleanup, pname, 0777);
|
|
pipe_type = PIPE_NAMED;
|
|
}
|
|
}
|
|
|
|
static void cleanup(void)
|
|
{
|
|
SAFE_FREE(writebuf);
|
|
SAFE_FREE(readbuf);
|
|
|
|
semctl(sem_id, 0, IPC_RMID);
|
|
|
|
if (!unpipe)
|
|
unlink(pname);
|
|
|
|
tst_rmdir();
|
|
}
|
|
|
|
static void do_child(void)
|
|
{
|
|
int *count_word; /* holds address where to write writers count */
|
|
int *pid_word; /* holds address where to write writers pid */
|
|
int nb, j;
|
|
long clock;
|
|
char *cp;
|
|
long int n;
|
|
struct sembuf sem_op;
|
|
pid_t self_pid = getpid();
|
|
|
|
if (!unpipe) {
|
|
write_fd = open(pname, O_WRONLY);
|
|
if (write_fd == -1) {
|
|
fprintf(stderr, "child pipe open(%s, %#o) failed",
|
|
pname, O_WRONLY | ndelay);
|
|
exit(1);
|
|
}
|
|
if (ndelay && fcntl(write_fd, F_SETFL, O_NONBLOCK) == -1) {
|
|
fprintf(stderr, "Failed setting the pipe to "
|
|
"nonblocking mode");
|
|
exit(1);
|
|
}
|
|
} else {
|
|
close(read_fd);
|
|
}
|
|
|
|
sem_op = (struct sembuf) {
|
|
.sem_num = 0, .sem_op = 1, .sem_flg = 0};
|
|
|
|
if (semop(sem_id, &sem_op, 1) == -1) {
|
|
fprintf(stderr, "child: %d couldn't raise the semaphore 0",
|
|
self_pid);
|
|
exit(1);
|
|
}
|
|
|
|
pid_word = (int *)&writebuf[0];
|
|
count_word = (int *)&writebuf[NBPW];
|
|
|
|
for (j = 0; j < num_writes || loop; ++j) {
|
|
/*
|
|
* writes are only in one unit when the size of the write
|
|
* is <= PIPE_BUF.
|
|
* Therefore, if size is greater than PIPE_BUF, we will break
|
|
* the writes into PIPE_BUF chunks.
|
|
* All writes and read need to be same size.
|
|
*/
|
|
|
|
/*
|
|
* write pid and count in first two
|
|
* words of buffer
|
|
*/
|
|
*count_word = j;
|
|
*pid_word = self_pid;
|
|
|
|
nb = lio_write_buffer(write_fd, iotype, writebuf, size,
|
|
SIGUSR1, &cp, 0);
|
|
if (nb < 0) {
|
|
/*
|
|
* If lio_write_buffer returns a negative number,
|
|
* the return will be -errno.
|
|
*/
|
|
fprintf(stderr, "pass %d: lio_write_buffer(%s) failed;"
|
|
" it returned %d: %s",
|
|
j, cp, nb, strerror(-nb));
|
|
exit(1);
|
|
} else if (nb != size) {
|
|
fprintf(stderr, "pass %d: lio_write_buffer(%s) failed,"
|
|
" write count %d, but expected to write %d",
|
|
j, cp, nb, size);
|
|
}
|
|
if (verbose) {
|
|
fprintf(stderr, "pass %d: pid %d: wrote %d bytes,"
|
|
"expected %d bytes",
|
|
j, self_pid, nb, size);
|
|
}
|
|
|
|
if (chld_wait) {
|
|
clock = time(0);
|
|
srand48(clock);
|
|
n = lrand48() % chld_wait;
|
|
usleep(n);
|
|
}
|
|
fflush(stderr);
|
|
}
|
|
|
|
/* child waits until parent completes open() */
|
|
sem_op = (struct sembuf) {
|
|
.sem_num = 1, .sem_op = -1, .sem_flg = 0};
|
|
if (semop(sem_id, &sem_op, 1) == -1)
|
|
fprintf(stderr, "Couldn't lower the semaphore 1");
|
|
|
|
exit(0);
|
|
}
|
|
|
|
static int check_rw_buf(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 2 * NBPW; i < size; ++i) {
|
|
if (writebuf[i] != readbuf[i]) {
|
|
++error;
|
|
tst_resm(TFAIL,
|
|
"FAIL data error on byte %d; rd# %d, sz= %d, "
|
|
"%s %s empty_reads= %d, err= %d",
|
|
i, count, size, pipe_type, blk_type,
|
|
empty_read, error);
|
|
prt_buf(&readbuf, readbuf, format_size, format);
|
|
fflush(stdout);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void do_parent(void)
|
|
{
|
|
int i, nb;
|
|
long clock;
|
|
time_t start_time, current_time, diff_time;
|
|
char *cp;
|
|
long int n;
|
|
struct sembuf sem_op;
|
|
|
|
start_time = time(0);
|
|
if (!unpipe) {
|
|
read_fd = SAFE_OPEN(cleanup, pname, O_RDONLY);
|
|
if (ndelay && fcntl(read_fd, F_SETFL, O_NONBLOCK) == -1) {
|
|
tst_brkm(TBROK | TERRNO, cleanup,
|
|
"Failed setting the pipe to nonblocking mode");
|
|
}
|
|
} else {
|
|
SAFE_CLOSE(cleanup, write_fd);
|
|
}
|
|
|
|
/* raise semaphore so children can exit */
|
|
sem_op = (struct sembuf) {
|
|
.sem_num = 1, .sem_op = num_writers, .sem_flg = 0};
|
|
if (semop(sem_id, &sem_op, 1) == -1) {
|
|
tst_brkm(TBROK | TERRNO, cleanup,
|
|
"Couldn't raise the semaphore 1");
|
|
}
|
|
|
|
sem_op = (struct sembuf) {
|
|
.sem_num = 0, .sem_op = -num_writers, .sem_flg = 0};
|
|
|
|
while (nchildcompleted < num_writers
|
|
&& semop(sem_id, &sem_op, 1) == -1) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
tst_brkm(TBROK | TERRNO, cleanup,
|
|
"Couldn't wait on semaphore 0");
|
|
}
|
|
|
|
/* parent start to read pipe */
|
|
for (i = num_writers * num_writes; i > 0 || loop; --i) {
|
|
if (error >= MAX_ERRS || empty_read >= MAX_EMPTY)
|
|
break;
|
|
if (parent_wait) {
|
|
clock = time(0);
|
|
srand48(clock);
|
|
n = lrand48() % parent_wait;
|
|
usleep(n);
|
|
}
|
|
++count;
|
|
nb = lio_read_buffer(read_fd, iotype, readbuf, size,
|
|
SIGUSR1, &cp, 0);
|
|
if (nb < 0) {
|
|
/*
|
|
* If lio_read_buffer returns a negative number,
|
|
* the return will be -errno.
|
|
*/
|
|
tst_resm(TFAIL, "pass %d: lio_read_buffer(%s) failed; "
|
|
"returned %d: %s", i, cp, nb, strerror(-nb));
|
|
++i;
|
|
count--;
|
|
error++;
|
|
continue;
|
|
} else {
|
|
if (nb == 0) {
|
|
if (nchildcompleted >= num_writers && !loop) {
|
|
tst_resm(TWARN, "The children have "
|
|
"died prematurely");
|
|
break; /* All children have died */
|
|
}
|
|
empty_read++;
|
|
++i;
|
|
count--;
|
|
continue;
|
|
} else if (nb < size && size <= PIPE_BUF) {
|
|
tst_resm(TFAIL, "pass %d: partial read from the"
|
|
" pipe: read %d bytes, expected %d, "
|
|
"read count %d", i, nb, size, count);
|
|
++error;
|
|
} else if (nb == size) {
|
|
check_rw_buf();
|
|
if (exit_error && exit_error == error)
|
|
return;
|
|
}
|
|
|
|
if (verbose || (num_rpt && !(count % num_rpt))) {
|
|
current_time = time(0);
|
|
diff_time = current_time - start_time;
|
|
tst_resm(TFAIL,
|
|
"(%d) rd# %d, sz= %d, %s %s "
|
|
"empty_reads= %d, err= %d\n",
|
|
(int)diff_time, count, size,
|
|
pipe_type, blk_type,
|
|
empty_read, error);
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
}
|
|
|
|
SAFE_CLOSE(cleanup, read_fd);
|
|
}
|
|
|
|
static void usage(void)
|
|
{
|
|
fprintf(stderr, "Usage: %s [-bEv][-c #writers][-D pname][-h]"
|
|
"[-e exit_num][-f fmt][-l][-i #writes][-n #writes][-p num_rpt]"
|
|
"\n\t[-s size][-W max_wait][-w max_wait][-u]\n", TCID);
|
|
fflush(stderr);
|
|
}
|
|
|
|
static void help(void)
|
|
{
|
|
usage();
|
|
|
|
printf(" -b - blocking reads and writes. default non-block\n\
|
|
-c #writers - number of writers (childern)\n\
|
|
-D pname - name of fifo (def tpipe<pid>)\n\
|
|
-h - print this help message\n\
|
|
-e exit_num - exit on error exit_num, 0 is ignore errors, 1 is default.\n\
|
|
-E - print cmd line examples and exit\n\
|
|
-f format - define format of bad buffer: h(hex), o(octal)\n\
|
|
d(decimal), a(ascii), n (none). hex is default\n\
|
|
option size can be added to control output\n\
|
|
-i #writes - number write per child, zero means forever.\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\
|
|
-l - loop forever (implied by -n 0).\n\
|
|
-n #writes - same as -i (for compatability).\n\
|
|
-p num_rpt - number of reads before a report\n\
|
|
-q - quiet mode, no PASS results are printed\n\
|
|
-s size - size of read and write (def 327)\n\
|
|
if size >= 4096, i/o will be in 4096 chuncks\n\
|
|
-w max_wait - max time (seconds) for sleep between writes.\n\
|
|
max_wait is interpreted as a double with ms accuracy.\n\
|
|
-W max_wait - max time (seconds) for sleep between reads\n\
|
|
max_wait is interpreted as a double with ms accuracy.\n\
|
|
-u - un-named pipe instead of named pipe\n\
|
|
-v - verbose mode, all writes/reads resutlts printed\n");
|
|
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void prt_buf(char **addr, char *buf, int length, int format)
|
|
{
|
|
int i;
|
|
int num_words = length / NBPW; /* given length in bytes, get length in words */
|
|
int width; /* number of columns */
|
|
int extra_words = 0; /* odd or even number of words */
|
|
char *a = buf;
|
|
char b[NBPW];
|
|
char c[NBPW * 2];
|
|
char *p;
|
|
long *word;
|
|
|
|
if (format == NO_OUT) /* if no output wanted, return */
|
|
return;
|
|
|
|
if (length % NBPW)
|
|
++num_words; /* is length in full words? */
|
|
if (format == ASCII) {
|
|
width = 3;
|
|
} else {
|
|
width = 2;
|
|
/* do we have an odd number of words? */
|
|
extra_words = num_words % width;
|
|
}
|
|
for (i = 0; i < num_words; ++i, a += NBPW, addr++) {
|
|
word = (long *)a;
|
|
if (!(i % width)) {
|
|
if (i > 0 && format != ASCII) {
|
|
/*
|
|
* print the ascii equivalent of the data
|
|
* before beginning the next line of output.
|
|
*/
|
|
memset(c, 0x00, width * NBPW);
|
|
/*
|
|
* get the last 2 words printed
|
|
*/
|
|
memcpy(c, a - (width * NBPW), width * NBPW);
|
|
for (p = c; (p - c) < (int)(width*NBPW); ++p) {
|
|
if (*p < '!' || *p > '~')
|
|
*p = '.';
|
|
}
|
|
printf("\t%16.16s", c);
|
|
}
|
|
printf("\n%p: ", addr);
|
|
/***printf("\n%7o (%d): ",addr,i);***/
|
|
}
|
|
|
|
switch (format) {
|
|
case HEX:
|
|
printf("%16.16lx ", *word);
|
|
break;
|
|
case DECIMAL:
|
|
printf("%10.10ld ", *word);
|
|
break;
|
|
case ASCII:
|
|
memcpy(b, a, NBPW);
|
|
for (p = b; (p - b) < (int)NBPW; ++p) {
|
|
if (*p < '!' || *p > '~')
|
|
*p = '.';
|
|
}
|
|
printf("%8.8s ", b);
|
|
break;
|
|
default:
|
|
printf("%22.22lo ", *word);
|
|
break;
|
|
}
|
|
}
|
|
if (format != ASCII) {
|
|
/*
|
|
* print the ascii equivalent of the last words in the buffer
|
|
* before returning.
|
|
*/
|
|
memset(c, 0x00, width * NBPW);
|
|
if (extra_words)
|
|
width = extra_words; /* odd number of words */
|
|
memcpy(c, a - (width * NBPW), width * NBPW);
|
|
for (p = c; (p - c) < (int)(width * NBPW); ++p) {
|
|
if (*p < '!' || *p > '~')
|
|
*p = '.';
|
|
}
|
|
if (width == 2)
|
|
printf("\t%16.16s", c);
|
|
else
|
|
printf("\t\t%16.8s", c);
|
|
}
|
|
printf("\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void prt_examples(void)
|
|
{
|
|
printf("%s -c 5 -i 0 -s 4090 -b\n", TCID);
|
|
printf("%s -c 5 -i 0 -s 4090 -b -u \n", TCID);
|
|
printf("%s -c 5 -i 0 -s 4090 -b -W 3 -w 3 \n", TCID);
|
|
}
|
|
|
|
static void sig_child(int sig)
|
|
{
|
|
int status;
|
|
|
|
nchildcompleted++;
|
|
#if DEBUG
|
|
#define STR "parent: received SIGCHLD\n"
|
|
write(STDOUT_FILENO, str, strlen(STR));
|
|
#endif
|
|
waitpid(-1, &status, WNOHANG);
|
|
}
|