1 /*
2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31 *
32 */
33 /* $Header: /cvsroot/ltp/ltp/testcases/kernel/ipc/pipeio/pipeio.c,v 1.18 2009/03/19 07:10:02 subrata_modak Exp $ */
34 /*
35 * This tool can be used to beat on system or named pipes.
36 * See the help() function below for user information.
37 */
38 #include <stdio.h>
39 #include <fcntl.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <sys/wait.h>
45 #include <time.h>
46 #include <errno.h>
47 #include <string.h>
48 #include <signal.h>
49 #include <sys/stat.h>
50 #include <sys/sem.h>
51
52 #include "tlibio.h"
53
54 #include "test.h"
55 #include "safe_macros.h"
56 #include "lapi/semun.h"
57
58 char *TCID = "pipeio";
59 int TST_TOTAL = 1;
60
61 #define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } }
62
63 #if defined(__linux__)
64 #define NBPW sizeof(int)
65 #endif
66
67 #define OCTAL 'o'
68 #define HEX 'x'
69 #define DECIMAL 'd'
70 #define ASCII 'a'
71 #define NO_OUT 'n'
72
73 #define PIPE_NAMED "named pipe,"
74 #define PIPE_UNNAMED "sys pipe,"
75
76 #define BLOCKING_IO "blking,"
77 #define NON_BLOCKING_IO "non-blking,"
78 #define UNNAMED_IO ""
79
80 #define MAX_ERRS 16
81 #define MAX_EMPTY 256
82
83 static int parse_options(int argc, char *argv[]);
84 static void setup(int argc, char *argv[]);
85 static void cleanup(void);
86
87 static void do_child(void);
88 static void do_parent(void);
89
90 static void help(void), usage(void), prt_examples(void);
91 static void prt_buf(char **addr, char *buf, int length, int format);
92 static void sig_child(int sig);
93 static int check_rw_buf(void);
94
95 static volatile sig_atomic_t nchildcompleted;
96
97 /* variables may be modified in setup() */
98 static int num_writers = 1; /* number of writers */
99 static int num_writes = 1; /* number of writes per child */
100 static int loop; /* loop indefinitely */
101 static int exit_error = 1; /* exit on error #, zero means no exit */
102 static int size = 327; /* default size */
103 static int unpipe; /* un-named pipe if non-zero */
104 static int verbose; /* verbose mode if set */
105 static int quiet; /* quiet mode if set */
106 static int num_rpt; /* ping number, how often to print message */
107 static int chld_wait; /* max time to wait between writes, 1 == no wait */
108 static int parent_wait; /* max time to wait between reads, 1 == no wait */
109 static int ndelay = O_NDELAY; /* additional flag to open */
110 static char *writebuf;
111 static char *readbuf;
112 static char pname[PATH_MAX]; /* contains the name of the named pipe */
113 static char *blk_type = NON_BLOCKING_IO; /* blocking i/o or not */
114 static char *pipe_type; /* type of pipe under test */
115 static int format = HEX;
116 static int format_size = -1;
117 static int iotype; /* sync io */
118
119 /* variables will be modified in running */
120 static int error;
121 static int count;
122 static int read_fd;
123 static int write_fd;
124 static int empty_read;
125 static int sem_id;
126
127 static union semun u;
128
main(int ac,char * av[])129 int main(int ac, char *av[])
130 {
131 int i;
132 unsigned int j;
133 unsigned int uwait_iter = 1000, uwait_total = 5000000;
134 pid_t child;
135
136 setup(ac, av);
137
138 for (i = num_writers; i > 0; --i) {
139
140 child = tst_fork();
141 switch (child) {
142 case -1:
143 tst_brkm(TBROK | TERRNO, cleanup, "fork() failed");
144 case 0:
145 do_child();
146 exit(0);
147 default:
148 break;
149 }
150 }
151
152 do_parent();
153
154 if (empty_read)
155 tst_resm(TWARN, "%d empty reads", empty_read);
156
157 if (error) {
158 tst_resm(TFAIL, "%d data errors on pipe, read size = %d, %s %s",
159 error, size, pipe_type, blk_type);
160 } else if (!quiet) {
161 tst_resm(TPASS, "%d pipe reads complete, read size = %d, %s %s",
162 count + 1, size, pipe_type, blk_type);
163 }
164
165 /*
166 * wait for all children to finish, timeout after uwait_total
167 * semtimedop might not be available everywhere
168 */
169 for (j = 0; j < uwait_total; j += uwait_iter) {
170 if (semctl(sem_id, 1, GETVAL) == 0)
171 break;
172 usleep(uwait_iter);
173 }
174
175 if (j >= uwait_total) {
176 tst_resm(TWARN,
177 "Timed out waiting for child processes to exit");
178 }
179
180 cleanup();
181 tst_exit();
182 }
183
parse_options(int argc,char * argv[])184 static int parse_options(int argc, char *argv[])
185 {
186 char *cp;
187 int c;
188 int ret = 0;
189 static double d;
190
191 while ((c = getopt(argc, argv, "T:bc:D:he:Ef:i:I:ln:p:qs:uvW:w:"))
192 != -1) {
193 switch (c) {
194 case 'T':
195 TCID = optarg;
196 break;
197 case 'h':
198 help();
199 ret = 1;
200 break;
201 case 'D': /* pipe name */
202 strcpy(pname, optarg);
203 break;
204 case 'b': /* blocked */
205 ndelay = 0;
206 blk_type = BLOCKING_IO;
207 break;
208 case 'c': /* number childern */
209 if (sscanf(optarg, "%d", &num_writers) != 1) {
210 fprintf(stderr,
211 "%s: --c option invalid arg '%s'.\n",
212 TCID, optarg);
213 ret = 1;
214 } else if (num_writers <= 0) {
215 fprintf(stderr, "%s: --c option must be "
216 "greater than zero.\n", TCID);
217 ret = 1;
218 }
219 break;
220 case 'e': /* exit on error # */
221 if (sscanf(optarg, "%d", &exit_error) != 1) {
222 fprintf(stderr,
223 "%s: --e option invalid arg '%s'.\n",
224 TCID, optarg);
225 ret = 1;
226 } else if (exit_error < 0) {
227 fprintf(stderr, "%s: --e option must be "
228 "greater than zero.\n", TCID);
229 ret = 1;
230 }
231 break;
232 case 'E':
233 prt_examples();
234 ret = 1;
235 break;
236 case 'f': /* format of buffer on error */
237 switch (optarg[0]) {
238 case 'x':
239 case 'X':
240 format = HEX;
241 break;
242 case 'o':
243 case 'O':
244 format = OCTAL;
245 break;
246 case 'd':
247 case 'D':
248 format = DECIMAL;
249 break;
250 case 'a':
251 case 'A':
252 format = ASCII;
253 break;
254 case 'n': /* not output */
255 case 'N':
256 format = NO_OUT;
257 break;
258
259 default:
260 fprintf(stderr,
261 "%s: --f option invalid arg '%s'.\n",
262 TCID, optarg);
263 fprintf(stderr, "\tIt must be x(hex), o(octal),"
264 "d(decimal), a(ascii) or n(none) with "
265 "opt sz\n");
266 ret = 1;
267 break;
268 }
269 cp = optarg;
270 cp++;
271 if (*cp) {
272 if (sscanf(cp, "%i", &format_size) != 1) {
273 fprintf(stderr, "%s: --f option invalid"
274 "arg '%s'.\n", TCID, optarg);
275 fprintf(stderr, "\tIt must be x(hex),"
276 "o(octal), d(decimal), a(ascii)"
277 " or n(none) with opt sz\n");
278 ret = 1;
279 break;
280 }
281 }
282 break;
283
284 case 'I':
285 iotype = lio_parse_io_arg1(optarg);
286 if (iotype == -1) {
287 fprintf(stderr, "%s: --I arg is invalid, "
288 "must be s, p, f, a, l, L or r.\n",
289 TCID);
290 ret = 1;
291 }
292 break;
293
294 case 'l': /* loop forever */
295 ++loop;
296 break;
297
298 case 'i':
299 case 'n': /* number writes per child */
300 if (sscanf(optarg, "%d", &num_writes) != 1) {
301 fprintf(stderr, "%s: --i/n option invalid "
302 "arg '%s'.\n", TCID, optarg);
303 ret = 1;
304 } else if (num_writes < 0) {
305 fprintf(stderr, "%s: --i/n option must be "
306 "greater than equal to zero.\n",
307 TCID);
308 ret = 1;
309 }
310
311 if (num_writes == 0) /* loop forever */
312 ++loop;
313 break;
314 case 'p': /* ping */
315 if (sscanf(optarg, "%d", &num_rpt) != 1) {
316 fprintf(stderr,
317 "%s: --p option invalid arg '%s'.\n",
318 TCID, optarg);
319 ret = 1;
320 } else if (num_rpt < 0) {
321 fprintf(stderr, "%s: --p option must be greater"
322 " than equal to zero.\n", TCID);
323 ret = 1;
324 }
325 break;
326 case 'q': /* Quiet - NOPASS */
327 quiet = 1;
328 break;
329 case 's': /* size */
330 if (sscanf(optarg, "%d", &size) != 1) {
331 fprintf(stderr,
332 "%s: --s option invalid arg '%s'.\n",
333 TCID, optarg);
334 ret = 1;
335 } else if (size <= 0) {
336 fprintf(stderr, "%s: --s option must be greater"
337 " than zero.\n", TCID);
338 ret = 1;
339 }
340 break;
341 case 'u':
342 unpipe = 1; /* un-named pipe */
343 break;
344 case 'v': /* verbose */
345 verbose = 1;
346 break;
347 case 'W': /* max wait time between reads */
348 d = strtod(optarg, &cp);
349 if (*cp != '\0') {
350 fprintf(stderr,
351 "%s: --w option invalid arg '%s'.\n",
352 TCID, optarg);
353 ret = 1;
354 } else if (d < 0) {
355 fprintf(stderr, "%s: --w option must be greater"
356 " than zero.\n", TCID);
357 ret = 1;
358 }
359 parent_wait = (int)(d * 1000000.0);
360 break;
361 case 'w': /* max wait time between writes */
362 d = strtod(optarg, &cp);
363 if (*cp != '\0') {
364 fprintf(stderr,
365 "%s: --w option invalid arg '%s'.\n",
366 TCID, optarg);
367 ret = 1;
368 } else if (d < 0) {
369 fprintf(stderr, "%s: --w option must be greater"
370 " than zero.\n", TCID);
371 ret = 1;
372 }
373 chld_wait = (int)(d * 1000000.0);
374 break;
375 case '?':
376 ret = 1;
377 break;
378 }
379
380 if (ret == 1) {
381 usage();
382 return ret;
383 }
384 }
385
386 return ret;
387 }
388
setup(int argc,char * argv[])389 static void setup(int argc, char *argv[])
390 {
391 int ret;
392 char *toutput;
393 int fds[2];
394
395 tst_sig(FORK, DEF_HANDLER, cleanup);
396
397 TEST_PAUSE;
398
399 tst_tmpdir();
400
401 if (signal(SIGCHLD, sig_child) == SIG_ERR) {
402 tst_brkm(TBROK | TERRNO, cleanup,
403 "set signal handler for SIGCHLD failed");
404 }
405
406 toutput = getenv("TOUTPUT");
407 if (toutput != NULL && strcmp(toutput, "NOPASS") == 0)
408 quiet = 1;
409
410 sprintf(pname, "%s", "tpipe");
411
412 ret = parse_options(argc, argv);
413 if (ret == 1)
414 tst_brkm(TBROK, cleanup, "options parse error");
415
416 if (format_size == -1)
417 format_size = size;
418
419 /*
420 * If there is more than one writer, all writes and reads
421 * must be the same size. Only writes of a size <= PIPE_BUF
422 * are atomic. T
423 * Therefore, if size is greater than PIPE_BUF, we will break
424 * the writes into PIPE_BUF chunks. We will also increase the
425 * number of writes to ensure the same (or more) amount of
426 * data is written. This is the same as erroring and telling
427 * the user the new cmd line to do the same thing.
428 * Example:
429 * pipeio -s 5000 -n 10 -c 5
430 * (each child will write at least 50000 bytes, since all
431 * writes have to be in 4096 chuncks or 13*4096 (53248)
432 * bytes will be written.) This is the same as:
433 * pipeio -s 4096 -n 13 -c 5
434 */
435 if (size > PIPE_BUF && num_writers > 1) {
436 if (!loop) {
437 /*
438 * we must set num_writes*num_writers
439 * doesn't overflow later
440 */
441 num_writes = MIN(((long long)num_writes * size +
442 PIPE_BUF - 1) / PIPE_BUF,
443 INT_MAX / num_writers);
444 tst_resm(TINFO, "adjusting i/o size to %d, and # of "
445 "writes to %d", PIPE_BUF, num_writes);
446 } else {
447 tst_resm(TINFO, "adjusting i/o size to %d", PIPE_BUF);
448 }
449 size = PIPE_BUF;
450 }
451
452 writebuf = SAFE_MALLOC(cleanup, size);
453 readbuf = SAFE_MALLOC(cleanup, size);
454
455 memset(writebuf, 'Z', size);
456 writebuf[size - 1] = 'A';
457
458 sem_id = semget(IPC_PRIVATE, 2, IPC_CREAT | S_IRWXU);
459 if (sem_id == -1) {
460 tst_brkm(TBROK | TERRNO, cleanup,
461 "Couldn't allocate semaphore");
462 }
463
464 if (semctl(sem_id, 0, SETVAL, u) == -1) {
465 tst_brkm(TBROK | TERRNO, cleanup,
466 "Couldn't initialize semaphore 0 value");
467 }
468
469 if (semctl(sem_id, 1, SETVAL, u) == -1) {
470 tst_brkm(TBROK | TERRNO, cleanup,
471 "Couldn't initialize semaphore 1 value");
472 }
473
474 if (unpipe) {
475 SAFE_PIPE(cleanup, fds);
476 read_fd = fds[0];
477 write_fd = fds[1];
478 pipe_type = PIPE_UNNAMED;
479 blk_type = UNNAMED_IO;
480 } else {
481 if (mkfifo(pname, 0777) == -1) {
482 tst_brkm(TBROK | TERRNO, cleanup,
483 "mkfifo(%s, 0777) failed", pname);
484 }
485 pipe_type = PIPE_NAMED;
486 }
487 }
488
cleanup(void)489 static void cleanup(void)
490 {
491 SAFE_FREE(writebuf);
492 SAFE_FREE(readbuf);
493
494 semctl(sem_id, 0, IPC_RMID);
495
496 if (!unpipe)
497 SAFE_UNLINK(NULL, pname);
498
499 tst_rmdir();
500 }
501
do_child(void)502 static void do_child(void)
503 {
504 int *count_word; /* holds address where to write writers count */
505 int *pid_word; /* holds address where to write writers pid */
506 int nb, j;
507 long clock;
508 char *cp;
509 long int n;
510 struct sembuf sem_op;
511 pid_t self_pid = getpid();
512
513 if (!unpipe) {
514 write_fd = open(pname, O_WRONLY);
515 if (write_fd == -1) {
516 fprintf(stderr, "child pipe open(%s, %#o) failed",
517 pname, O_WRONLY | ndelay);
518 exit(1);
519 }
520 if (ndelay && fcntl(write_fd, F_SETFL, O_NONBLOCK) == -1) {
521 fprintf(stderr, "Failed setting the pipe to "
522 "nonblocking mode");
523 exit(1);
524 }
525 } else {
526 close(read_fd);
527 }
528
529 sem_op = (struct sembuf) {
530 .sem_num = 0, .sem_op = 1, .sem_flg = 0};
531
532 if (semop(sem_id, &sem_op, 1) == -1) {
533 fprintf(stderr, "child: %d couldn't raise the semaphore 0",
534 self_pid);
535 exit(1);
536 }
537
538 pid_word = (int *)&writebuf[0];
539 count_word = (int *)&writebuf[NBPW];
540
541 for (j = 0; j < num_writes || loop; ++j) {
542 /*
543 * writes are only in one unit when the size of the write
544 * is <= PIPE_BUF.
545 * Therefore, if size is greater than PIPE_BUF, we will break
546 * the writes into PIPE_BUF chunks.
547 * All writes and read need to be same size.
548 */
549
550 /*
551 * write pid and count in first two
552 * words of buffer
553 */
554 *count_word = j;
555 *pid_word = self_pid;
556
557 nb = lio_write_buffer(write_fd, iotype, writebuf, size,
558 SIGUSR1, &cp, 0);
559 if (nb < 0) {
560 /*
561 * If lio_write_buffer returns a negative number,
562 * the return will be -errno.
563 */
564 fprintf(stderr, "pass %d: lio_write_buffer(%s) failed;"
565 " it returned %d: %s",
566 j, cp, nb, strerror(-nb));
567 exit(1);
568 } else if (nb != size) {
569 fprintf(stderr, "pass %d: lio_write_buffer(%s) failed,"
570 " write count %d, but expected to write %d",
571 j, cp, nb, size);
572 }
573 if (verbose) {
574 fprintf(stderr, "pass %d: pid %d: wrote %d bytes,"
575 "expected %d bytes",
576 j, self_pid, nb, size);
577 }
578
579 if (chld_wait) {
580 clock = time(0);
581 srand48(clock);
582 n = lrand48() % chld_wait;
583 usleep(n);
584 }
585 fflush(stderr);
586 }
587
588 /* child waits until parent completes open() */
589 sem_op = (struct sembuf) {
590 .sem_num = 1, .sem_op = -1, .sem_flg = 0};
591 if (semop(sem_id, &sem_op, 1) == -1)
592 fprintf(stderr, "Couldn't lower the semaphore 1");
593
594 exit(0);
595 }
596
check_rw_buf(void)597 static int check_rw_buf(void)
598 {
599 int i;
600
601 for (i = 2 * NBPW; i < size; ++i) {
602 if (writebuf[i] != readbuf[i]) {
603 ++error;
604 tst_resm(TFAIL,
605 "FAIL data error on byte %d; rd# %d, sz= %d, "
606 "%s %s empty_reads= %d, err= %d",
607 i, count, size, pipe_type, blk_type,
608 empty_read, error);
609 prt_buf(&readbuf, readbuf, format_size, format);
610 fflush(stdout);
611 return 1;
612 }
613 }
614
615 return 0;
616 }
617
do_parent(void)618 static void do_parent(void)
619 {
620 int i, nb;
621 long clock;
622 time_t start_time, current_time, diff_time;
623 char *cp;
624 long int n;
625 struct sembuf sem_op;
626
627 start_time = time(0);
628 if (!unpipe) {
629 read_fd = SAFE_OPEN(cleanup, pname, O_RDONLY);
630 if (ndelay && fcntl(read_fd, F_SETFL, O_NONBLOCK) == -1) {
631 tst_brkm(TBROK | TERRNO, cleanup,
632 "Failed setting the pipe to nonblocking mode");
633 }
634 } else {
635 SAFE_CLOSE(cleanup, write_fd);
636 }
637
638 /* raise semaphore so children can exit */
639 sem_op = (struct sembuf) {
640 .sem_num = 1, .sem_op = num_writers, .sem_flg = 0};
641 if (semop(sem_id, &sem_op, 1) == -1) {
642 tst_brkm(TBROK | TERRNO, cleanup,
643 "Couldn't raise the semaphore 1");
644 }
645
646 sem_op = (struct sembuf) {
647 .sem_num = 0, .sem_op = -num_writers, .sem_flg = 0};
648
649 while (nchildcompleted < num_writers
650 && semop(sem_id, &sem_op, 1) == -1) {
651 if (errno == EINTR)
652 continue;
653 tst_brkm(TBROK | TERRNO, cleanup,
654 "Couldn't wait on semaphore 0");
655 }
656
657 /* parent start to read pipe */
658 for (i = num_writers * num_writes; i > 0 || loop; --i) {
659 if (error >= MAX_ERRS || empty_read >= MAX_EMPTY)
660 break;
661 if (parent_wait) {
662 clock = time(0);
663 srand48(clock);
664 n = lrand48() % parent_wait;
665 usleep(n);
666 }
667 ++count;
668 nb = lio_read_buffer(read_fd, iotype, readbuf, size,
669 SIGUSR1, &cp, 0);
670 if (nb < 0) {
671 /*
672 * If lio_read_buffer returns a negative number,
673 * the return will be -errno.
674 */
675 tst_resm(TFAIL, "pass %d: lio_read_buffer(%s) failed; "
676 "returned %d: %s", i, cp, nb, strerror(-nb));
677 ++i;
678 count--;
679 error++;
680 continue;
681 } else {
682 if (nb == 0) {
683 if (nchildcompleted >= num_writers && !loop) {
684 tst_resm(TWARN, "The children have "
685 "died prematurely");
686 break; /* All children have died */
687 }
688 empty_read++;
689 ++i;
690 count--;
691 continue;
692 } else if (nb < size && size <= PIPE_BUF) {
693 tst_resm(TFAIL, "pass %d: partial read from the"
694 " pipe: read %d bytes, expected %d, "
695 "read count %d", i, nb, size, count);
696 ++error;
697 } else if (nb == size) {
698 check_rw_buf();
699 if (exit_error && exit_error == error)
700 return;
701 }
702
703 if (verbose || (num_rpt && !(count % num_rpt))) {
704 current_time = time(0);
705 diff_time = current_time - start_time;
706 tst_resm(TFAIL,
707 "(%d) rd# %d, sz= %d, %s %s "
708 "empty_reads= %d, err= %d\n",
709 (int)diff_time, count, size,
710 pipe_type, blk_type,
711 empty_read, error);
712 fflush(stdout);
713 }
714 }
715 }
716
717 SAFE_CLOSE(cleanup, read_fd);
718 }
719
usage(void)720 static void usage(void)
721 {
722 fprintf(stderr, "Usage: %s [-bEv][-c #writers][-D pname][-h]"
723 "[-e exit_num][-f fmt][-l][-i #writes][-n #writes][-p num_rpt]"
724 "\n\t[-s size][-W max_wait][-w max_wait][-u]\n", TCID);
725 fflush(stderr);
726 }
727
help(void)728 static void help(void)
729 {
730 usage();
731
732 printf(" -b - blocking reads and writes. default non-block\n\
733 -c #writers - number of writers (childern)\n\
734 -D pname - name of fifo (def tpipe<pid>)\n\
735 -h - print this help message\n\
736 -e exit_num - exit on error exit_num, 0 is ignore errors, 1 is default.\n\
737 -E - print cmd line examples and exit\n\
738 -f format - define format of bad buffer: h(hex), o(octal)\n\
739 d(decimal), a(ascii), n (none). hex is default\n\
740 option size can be added to control output\n\
741 -i #writes - number write per child, zero means forever.\n\
742 -I io_type - Specifies io type: s - sync, p - polled async, a - async (def s)\n\
743 l - listio sync, L - listio async, r - random\n\
744 -l - loop forever (implied by -n 0).\n\
745 -n #writes - same as -i (for compatability).\n\
746 -p num_rpt - number of reads before a report\n\
747 -q - quiet mode, no PASS results are printed\n\
748 -s size - size of read and write (def 327)\n\
749 if size >= 4096, i/o will be in 4096 chuncks\n\
750 -w max_wait - max time (seconds) for sleep between writes.\n\
751 max_wait is interpreted as a double with ms accuracy.\n\
752 -W max_wait - max time (seconds) for sleep between reads\n\
753 max_wait is interpreted as a double with ms accuracy.\n\
754 -u - un-named pipe instead of named pipe\n\
755 -v - verbose mode, all writes/reads resutlts printed\n");
756
757 fflush(stdout);
758 }
759
prt_buf(char ** addr,char * buf,int length,int format)760 static void prt_buf(char **addr, char *buf, int length, int format)
761 {
762 int i;
763 int num_words = length / NBPW; /* given length in bytes, get length in words */
764 int width; /* number of columns */
765 int extra_words = 0; /* odd or even number of words */
766 char *a = buf;
767 char b[NBPW];
768 char c[NBPW * 2];
769 char *p;
770 long *word;
771
772 if (format == NO_OUT) /* if no output wanted, return */
773 return;
774
775 if (length % NBPW)
776 ++num_words; /* is length in full words? */
777 if (format == ASCII) {
778 width = 3;
779 } else {
780 width = 2;
781 /* do we have an odd number of words? */
782 extra_words = num_words % width;
783 }
784 for (i = 0; i < num_words; ++i, a += NBPW, addr++) {
785 word = (long *)a;
786 if (!(i % width)) {
787 if (i > 0 && format != ASCII) {
788 /*
789 * print the ascii equivalent of the data
790 * before beginning the next line of output.
791 */
792 memset(c, 0x00, width * NBPW);
793 /*
794 * get the last 2 words printed
795 */
796 memcpy(c, a - (width * NBPW), width * NBPW);
797 for (p = c; (p - c) < (int)(width*NBPW); ++p) {
798 if (*p < '!' || *p > '~')
799 *p = '.';
800 }
801 printf("\t%16.16s", c);
802 }
803 printf("\n%p: ", addr);
804 /***printf("\n%7o (%d): ",addr,i);***/
805 }
806
807 switch (format) {
808 case HEX:
809 printf("%16.16lx ", *word);
810 break;
811 case DECIMAL:
812 printf("%10.10ld ", *word);
813 break;
814 case ASCII:
815 memcpy(b, a, NBPW);
816 for (p = b; (p - b) < (int)NBPW; ++p) {
817 if (*p < '!' || *p > '~')
818 *p = '.';
819 }
820 printf("%8.8s ", b);
821 break;
822 default:
823 printf("%22.22lo ", *word);
824 break;
825 }
826 }
827 if (format != ASCII) {
828 /*
829 * print the ascii equivalent of the last words in the buffer
830 * before returning.
831 */
832 memset(c, 0x00, width * NBPW);
833 if (extra_words)
834 width = extra_words; /* odd number of words */
835 memcpy(c, a - (width * NBPW), width * NBPW);
836 for (p = c; (p - c) < (int)(width * NBPW); ++p) {
837 if (*p < '!' || *p > '~')
838 *p = '.';
839 }
840 if (width == 2)
841 printf("\t%16.16s", c);
842 else
843 printf("\t\t%16.8s", c);
844 }
845 printf("\n");
846 fflush(stdout);
847 }
848
prt_examples(void)849 static void prt_examples(void)
850 {
851 printf("%s -c 5 -i 0 -s 4090 -b\n", TCID);
852 printf("%s -c 5 -i 0 -s 4090 -b -u \n", TCID);
853 printf("%s -c 5 -i 0 -s 4090 -b -W 3 -w 3 \n", TCID);
854 }
855
sig_child(int sig)856 static void sig_child(int sig)
857 {
858 int status;
859
860 nchildcompleted++;
861 #if DEBUG
862 #define STR "parent: received SIGCHLD\n"
863 write(STDOUT_FILENO, str, strlen(STR));
864 #endif
865 waitpid(-1, &status, WNOHANG);
866 }
867