1 /*
2  *	T T C P . C
3  *
4  * Test TCP connection.  Makes a connection on port 5001
5  * and transfers fabricated buffers or data copied from stdin.
6  *
7  * Usable on 4.2, 4.3, and 4.1a systems by defining one of
8  * BSD42 BSD43 (BSD41a)
9  * Machines using System V with BSD sockets should define SYSV.
10  *
11  * Modified for operation under 4.2BSD, 18 Dec 84
12  *      T.C. Slattery, USNA
13  * Minor improvements, Mike Muuss and Terry Slattery, 16-Oct-85.
14  * Modified in 1989 at Silicon Graphics, Inc.
15  *	catch SIGPIPE to be able to print stats when receiver has died
16  *	for tcp, don't look for sentinel during reads to allow small transfers
17  *	increased default buffer size to 8K, nbuf to 2K to transfer 16MB
18  *	moved default port to 5001, beyond IPPORT_USERRESERVED
19  *	make sinkmode default because it is more popular,
20  *		-s now means don't sink/source
21  *	count number of read/write system calls to see effects of
22  *		blocking from full socket buffers
23  *	for tcp, -D option turns off buffered writes (sets TCP_NODELAY sockopt)
24  *	buffer alignment options, -A and -O
25  *	print stats in a format that's a bit easier to use with grep & awk
26  *	for SYSV, mimic BSD routines to use most of the existing timing code
27  * Modified by Steve Miller of the University of Maryland, College Park
28  *	-b sets the socket buffer size (SO_SNDBUF/SO_RCVBUF)
29  * Modified Sept. 1989 at Silicon Graphics, Inc.
30  *	restored -s sense at request of tcs@brl
31  * Modified Oct. 1991 at Silicon Graphics, Inc.
32  *	use getopt(3) for option processing, add -f and -T options.
33  *	SGI IRIX 3.3 and 4.0 releases don't need #define SYSV.
34  *
35  * Distribution Status -
36  *      Public Domain.  Distribution Unlimited.
37  */
38 #ifndef lint
39 static char RCSid[] = "ttcp.c $Revision: 1.12 $";
40 #endif
41 
42 #define BSD43
43 /* #define BSD42 */
44 /* #define BSD41a */
45 /* #define SYSV */	/* required on SGI IRIX releases before 3.3 */
46 
47 #include <stdio.h>
48 #include <signal.h>
49 #include <ctype.h>
50 #include <errno.h>
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <netinet/tcp.h>
55 #include <arpa/inet.h>
56 #include <netdb.h>
57 #include <sys/time.h>		/* struct timeval */
58 #include <unistd.h>
59 #include <string.h>
60 #include <stdlib.h>
61 
62 #if defined(SYSV)
63 #include <sys/times.h>
64 #include <sys/param.h>
65 struct rusage {
66     struct timeval ru_utime, ru_stime;
67 };
68 #define RUSAGE_SELF 0
69 #else
70 #include <sys/resource.h>
71 #endif
72 
73 struct sockaddr_in sinme;
74 struct sockaddr_in sinhim;
75 struct sockaddr_in frominet;
76 
77 int domain, fromlen;
78 int fd;				/* fd of network socket */
79 
80 int buflen = 8 * 1024;		/* length of buffer */
81 char *buf;			/* ptr to dynamic buffer */
82 int nbuf = 2 * 1024;		/* number of buffers to send in sinkmode */
83 
84 int bufoffset = 0;		/* align buffer to this */
85 int bufalign = 16*1024;		/* modulo this */
86 
87 int udp = 0;			/* 0 = tcp, !0 = udp */
88 int options = 0;		/* socket options */
89 int one = 1;                    /* for 4.3 BSD style setsockopt() */
90 short port = 5001;		/* TCP port number */
91 char *host;			/* ptr to name of host */
92 int trans;			/* 0=receive, !0=transmit mode */
93 int sinkmode = 0;		/* 0=normal I/O, !0=sink/source mode */
94 int verbose = 0;		/* 0=print basic info, 1=print cpu rate, proc
95 				 * resource usage. */
96 int nodelay = 0;		/* set TCP_NODELAY socket option */
97 int b_flag = 0;			/* use mread() */
98 int sockbufsize = 0;		/* socket buffer size to use */
99 char fmt = 'K';			/* output format: k = kilobits, K = kilobytes,
100 				 *  m = megabits, M = megabytes,
101 				 *  g = gigabits, G = gigabytes */
102 int touchdata = 0;		/* access data after reading */
103 
104 struct hostent *addr;
105 extern int errno;
106 extern int optind;
107 extern char *optarg;
108 
109 char Usage[] = "\
110 Usage: ttcp -t [-options] host [ < in ]\n\
111        ttcp -r [-options > out]\n\
112 Common options:\n\
113 	-l ##	length of bufs read from or written to network (default 8192)\n\
114 	-u	use UDP instead of TCP\n\
115 	-p ##	port number to send to or listen at (default 5001)\n\
116 	-s	-t: source a pattern to network\n\
117 		-r: sink (discard) all data from network\n\
118 	-A	align the start of buffers to this modulus (default 16384)\n\
119 	-O	start buffers at this offset from the modulus (default 0)\n\
120 	-v	verbose: print more statistics\n\
121 	-d	set SO_DEBUG socket option\n\
122 	-b ##	set socket buffer size (if supported)\n\
123 	-f X	format for rate: k,K = kilo{bit,byte}; m,M = mega; g,G = giga\n\
124 Options specific to -t:\n\
125 	-n##	number of source bufs written to network (default 2048)\n\
126 	-D	don't buffer TCP writes (sets TCP_NODELAY socket option)\n\
127 Options specific to -r:\n\
128 	-B	for -s, only output full blocks as specified by -l (for TAR)\n\
129 	-T	\"touch\": access each byte as it's read\n\
130 ";
131 
132 char stats[128];
133 double nbytes;			/* bytes on net */
134 unsigned long numCalls;		/* # of I/O system calls */
135 double cput, realt;		/* user, real time (seconds) */
136 
137 void err();
138 void mes();
139 void pattern();
140 void prep_timer();
141 double read_timer();
142 int Nread();
143 int Nwrite();
144 void delay();
145 int mread();
146 char *outfmt();
147 
148 void
sigpipe()149 sigpipe()
150 {
151 }
152 
153 int
main(argc,argv)154 main(argc,argv)
155 int argc;
156 char **argv;
157 {
158 	unsigned long addr_tmp;
159 	int c;
160 
161 	if (argc < 2) goto usage;
162 
163 	while ((c = getopt(argc, argv, "drstuvBDTb:f:l:n:p:A:O:")) != -1) {
164 		switch (c) {
165 
166 		case 'B':
167 			b_flag = 1;
168 			break;
169 		case 't':
170 			trans = 1;
171 			break;
172 		case 'r':
173 			trans = 0;
174 			break;
175 		case 'd':
176 			options |= SO_DEBUG;
177 			break;
178 		case 'D':
179 #ifdef TCP_NODELAY
180 			nodelay = 1;
181 #else
182 			fprintf(stderr,
183 	"ttcp: -D option ignored: TCP_NODELAY socket option not supported\n");
184 #endif
185 			break;
186 		case 'n':
187 			nbuf = atoi(optarg);
188 			break;
189 		case 'l':
190 			buflen = atoi(optarg);
191 			break;
192 		case 's':
193 			sinkmode = !sinkmode;
194 			break;
195 		case 'p':
196 			port = atoi(optarg);
197 			break;
198 		case 'u':
199 			udp = 1;
200 			break;
201 		case 'v':
202 			verbose = 1;
203 			break;
204 		case 'A':
205 			bufalign = atoi(optarg);
206 			break;
207 		case 'O':
208 			bufoffset = atoi(optarg);
209 			break;
210 		case 'b':
211 #if defined(SO_SNDBUF) || defined(SO_RCVBUF)
212 			sockbufsize = atoi(optarg);
213 #else
214 			fprintf(stderr,
215 "ttcp: -b option ignored: SO_SNDBUF/SO_RCVBUF socket options not supported\n");
216 #endif
217 			break;
218 		case 'f':
219 			fmt = *optarg;
220 			break;
221 		case 'T':
222 			touchdata = 1;
223 			break;
224 
225 		default:
226 			goto usage;
227 		}
228 	}
229 	if(trans)  {
230 		/* xmitr */
231 		if (optind == argc)
232 			goto usage;
233 		bzero((char *)&sinhim, sizeof(sinhim));
234 		host = argv[optind];
235 		if (atoi(host) > 0 )  {
236 			/* Numeric */
237 			sinhim.sin_family = AF_INET;
238 #if defined(cray)
239 			addr_tmp = inet_addr(host);
240 			sinhim.sin_addr = addr_tmp;
241 #else
242 			sinhim.sin_addr.s_addr = inet_addr(host);
243 #endif
244 		} else {
245 			if ((addr=gethostbyname(host)) == NULL)
246 				err("bad hostname");
247 			sinhim.sin_family = addr->h_addrtype;
248 			bcopy(addr->h_addr,(char*)&addr_tmp, addr->h_length);
249 #if defined(cray)
250 			sinhim.sin_addr = addr_tmp;
251 #else
252 			sinhim.sin_addr.s_addr = addr_tmp;
253 #endif /* cray */
254 		}
255 		sinhim.sin_port = htons(port);
256 		sinme.sin_family = AF_INET;     /* Solaris needs this */
257 		sinme.sin_port = 0;		/* free choice */
258 	} else {
259 		/* rcvr */
260 		sinme.sin_port =  htons(port);
261 	}
262 
263 
264 	if (udp && buflen < 5) {
265 	    buflen = 5;		/* send more than the sentinel size */
266 	}
267 
268 	if ( (buf = (char *)malloc(buflen+bufalign)) == (char *)NULL)
269 		err("malloc");
270 	if (bufalign != 0)
271 		buf += (bufalign
272 			-((unsigned long)buf % bufalign)
273 			+ bufoffset) % bufalign;
274 
275 	if (trans) {
276 	    fprintf(stderr,
277 	    "ttcp-t: buflen=%d, nbuf=%d, align=%d/%d, port=%d",
278 		buflen, nbuf, bufalign, bufoffset, port);
279  	    if (sockbufsize)
280  		fprintf(stderr, ", sockbufsize=%d", sockbufsize);
281  	    fprintf(stderr, "  %s  -> %s\n", udp?"udp":"tcp", host);
282 	} else {
283 	    fprintf(stderr,
284  	    "ttcp-r: buflen=%d, nbuf=%d, align=%d/%d, port=%d",
285  		buflen, nbuf, bufalign, bufoffset, port);
286  	    if (sockbufsize)
287  		fprintf(stderr, ", sockbufsize=%d", sockbufsize);
288  	    fprintf(stderr, "  %s\n", udp?"udp":"tcp");
289 	}
290 
291 	if ((fd = socket(AF_INET, udp?SOCK_DGRAM:SOCK_STREAM, 0)) < 0)
292 		err("socket");
293 	mes("socket");
294 
295 	if (bind(fd, (struct sockaddr *) &sinme, sizeof(sinme)) < 0)
296 		err("bind");
297 
298 #if defined(SO_SNDBUF) || defined(SO_RCVBUF)
299 	if (sockbufsize) {
300 	    if (trans) {
301 		if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sockbufsize,
302 		    sizeof sockbufsize) < 0)
303 			err("setsockopt: sndbuf");
304 		mes("sndbuf");
305 	    } else {
306 		if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &sockbufsize,
307 		    sizeof sockbufsize) < 0)
308 			err("setsockopt: rcvbuf");
309 		mes("rcvbuf");
310 	    }
311 	}
312 #endif
313 
314 	if (!udp)  {
315 	    signal(SIGPIPE, sigpipe);
316 	    if (trans) {
317 		/* We are the client if transmitting */
318 		if (options)  {
319 #if defined(BSD42)
320 			if( setsockopt(fd, SOL_SOCKET, options, 0, 0) < 0)
321 #else /* BSD43 */
322 			if( setsockopt(fd, SOL_SOCKET, options, &one, sizeof(one)) < 0)
323 #endif
324 				err("setsockopt");
325 		}
326 #ifdef TCP_NODELAY
327 		if (nodelay) {
328 			struct protoent *p;
329 			p = getprotobyname("tcp");
330 			if( p && setsockopt(fd, p->p_proto, TCP_NODELAY,
331 			    &one, sizeof(one)) < 0)
332 				err("setsockopt: nodelay");
333 			mes("nodelay");
334 		}
335 #endif
336 		if(connect(fd, (struct sockaddr*)&sinhim, sizeof(sinhim) ) < 0)
337 			err("connect");
338 		mes("connect");
339 	    } else {
340 		/* otherwise, we are the server and
341 	         * should listen for the connections
342 	         */
343 #if defined(ultrix) || defined(sgi)
344 		listen(fd,1);   /* workaround for alleged u4.2 bug */
345 #else
346 		listen(fd,0);   /* allow a queue of 0 */
347 #endif
348 		if(options)  {
349 #if defined(BSD42)
350 			if( setsockopt(fd, SOL_SOCKET, options, 0, 0) < 0)
351 #else /* BSD43 */
352 			if( setsockopt(fd, SOL_SOCKET, options, &one, sizeof(one)) < 0)
353 #endif
354 				err("setsockopt");
355 		}
356 		fromlen = sizeof(frominet);
357 		domain = AF_INET;
358 		if((fd=accept(fd, (struct sockaddr*)&frominet, &fromlen) ) < 0)
359 			err("accept");
360 		{ struct sockaddr_in peer;
361 		  int peerlen = sizeof(peer);
362 		  if (getpeername(fd, (struct sockaddr*) &peer,
363 				&peerlen) < 0) {
364 			err("getpeername");
365 		  }
366 		  fprintf(stderr,"ttcp-r: accept from %s\n",
367 			inet_ntoa(peer.sin_addr));
368 		}
369 	    }
370 	}
371 	prep_timer();
372 	errno = 0;
373 	if (sinkmode) {
374 		register int cnt;
375 		if (trans)  {
376 			pattern( buf, buflen );
377 			if(udp)  (void)Nwrite( fd, buf, 4 ); /* rcvr start */
378 			while (nbuf-- && Nwrite(fd,buf,buflen) == buflen)
379 				nbytes += buflen;
380 			if(udp)  (void)Nwrite( fd, buf, 4 ); /* rcvr end */
381 		} else {
382 			if (udp) {
383 			    while ((cnt=Nread(fd,buf,buflen)) > 0)  {
384 				    static int going = 0;
385 				    if( cnt <= 4 )  {
386 					    if( going )
387 						    break;	/* "EOF" */
388 					    going = 1;
389 					    prep_timer();
390 				    } else {
391 					    nbytes += cnt;
392 				    }
393 			    }
394 			} else {
395 			    while ((cnt=Nread(fd,buf,buflen)) > 0)  {
396 				    nbytes += cnt;
397 			    }
398 			}
399 		}
400 	} else {
401 		register int cnt;
402 		if (trans)  {
403 			while((cnt=read(0,buf,buflen)) > 0 &&
404 			    Nwrite(fd,buf,cnt) == cnt)
405 				nbytes += cnt;
406 		}  else  {
407 			while((cnt=Nread(fd,buf,buflen)) > 0 &&
408 			    write(1,buf,cnt) == cnt)
409 				nbytes += cnt;
410 		}
411 	}
412 	if(errno) err("IO");
413 	(void)read_timer(stats,sizeof(stats));
414 	if(udp&&trans)  {
415 		(void)Nwrite( fd, buf, 4 ); /* rcvr end */
416 		(void)Nwrite( fd, buf, 4 ); /* rcvr end */
417 		(void)Nwrite( fd, buf, 4 ); /* rcvr end */
418 		(void)Nwrite( fd, buf, 4 ); /* rcvr end */
419 	}
420 	if( cput <= 0.0 )  cput = 0.001;
421 	if( realt <= 0.0 )  realt = 0.001;
422 	fprintf(stderr,
423 		"ttcp%s: %.0f bytes in %.2f real seconds = %s/sec +++\n",
424 		trans?"-t":"-r",
425 		nbytes, realt, outfmt(nbytes/realt));
426 	if (verbose) {
427 	    fprintf(stderr,
428 		"ttcp%s: %.0f bytes in %.2f CPU seconds = %s/cpu sec\n",
429 		trans?"-t":"-r",
430 		nbytes, cput, outfmt(nbytes/cput));
431 	}
432 	fprintf(stderr,
433 		"ttcp%s: %d I/O calls, msec/call = %.2f, calls/sec = %.2f\n",
434 		trans?"-t":"-r",
435 		numCalls,
436 		1024.0 * realt/((double)numCalls),
437 		((double)numCalls)/realt);
438 	fprintf(stderr,"ttcp%s: %s\n", trans?"-t":"-r", stats);
439 	if (verbose) {
440 	    fprintf(stderr,
441 		"ttcp%s: buffer address %#x\n",
442 		trans?"-t":"-r",
443 		buf);
444 	}
445 	exit(0);
446 
447 usage:
448 	fprintf(stderr,Usage);
449 	exit(1);
450 }
451 
452 void
err(s)453 err(s)
454 char *s;
455 {
456 	fprintf(stderr,"ttcp%s: ", trans?"-t":"-r");
457 	perror(s);
458 	fprintf(stderr,"errno=%d\n",errno);
459 	exit(1);
460 }
461 
462 void
mes(s)463 mes(s)
464 char *s;
465 {
466 	fprintf(stderr,"ttcp%s: %s\n", trans?"-t":"-r", s);
467 }
468 
469 void
pattern(cp,cnt)470 pattern( cp, cnt )
471 register char *cp;
472 register int cnt;
473 {
474 	register char c;
475 	c = 0;
476 	while( cnt-- > 0 )  {
477 		while( !isprint((c&0x7F)) )  c++;
478 		*cp++ = (c++&0x7F);
479 	}
480 }
481 
482 char *
outfmt(b)483 outfmt(b)
484 double b;
485 {
486     static char obuf[50];
487     switch (fmt) {
488 	case 'G':
489 	    sprintf(obuf, "%.2f GB", b / 1024.0 / 1024.0 / 1024.0);
490 	    break;
491 	default:
492 	case 'K':
493 	    sprintf(obuf, "%.2f KB", b / 1024.0);
494 	    break;
495 	case 'M':
496 	    sprintf(obuf, "%.2f MB", b / 1024.0 / 1024.0);
497 	    break;
498 	case 'g':
499 	    sprintf(obuf, "%.2f Gbit", b * 8.0 / 1024.0 / 1024.0 / 1024.0);
500 	    break;
501 	case 'k':
502 	    sprintf(obuf, "%.2f Kbit", b * 8.0 / 1024.0);
503 	    break;
504 	case 'm':
505 	    sprintf(obuf, "%.2f Mbit", b * 8.0 / 1024.0 / 1024.0);
506 	    break;
507     }
508     return obuf;
509 }
510 
511 static struct	timeval time0;	/* Time at which timing started */
512 static struct	rusage ru0;	/* Resource utilization at the start */
513 
514 static void prusage();
515 static void tvadd();
516 static void tvsub();
517 static void psecs();
518 
519 #if defined(SYSV)
520 /*ARGSUSED*/
521 static
getrusage(ignored,ru)522 getrusage(ignored, ru)
523     int ignored;
524     register struct rusage *ru;
525 {
526     struct tms buf;
527 
528     times(&buf);
529 
530     /* Assumption: HZ <= 2147 (LONG_MAX/1000000) */
531     ru->ru_stime.tv_sec  = buf.tms_stime / HZ;
532     ru->ru_stime.tv_usec = ((buf.tms_stime % HZ) * 1000000) / HZ;
533     ru->ru_utime.tv_sec  = buf.tms_utime / HZ;
534     ru->ru_utime.tv_usec = ((buf.tms_utime % HZ) * 1000000) / HZ;
535 }
536 
537 /*ARGSUSED*/
538 static
539 gettimeofday(tp, zp)
540     struct timeval *tp;
541     struct timezone *zp;
542 {
543     tp->tv_sec = time(0);
544     tp->tv_usec = 0;
545 }
546 #endif /* SYSV */
547 
548 /*
549  *			P R E P _ T I M E R
550  */
551 void
prep_timer()552 prep_timer()
553 {
554 	gettimeofday(&time0, (struct timezone *)0);
555 	getrusage(RUSAGE_SELF, &ru0);
556 }
557 
558 /*
559  *			R E A D _ T I M E R
560  *
561  */
562 double
read_timer(str,len)563 read_timer(str,len)
564 char *str;
565 {
566 	struct timeval timedol;
567 	struct rusage ru1;
568 	struct timeval td;
569 	struct timeval tend, tstart;
570 	char line[132];
571 
572 	getrusage(RUSAGE_SELF, &ru1);
573 	gettimeofday(&timedol, (struct timezone *)0);
574 	prusage(&ru0, &ru1, &timedol, &time0, line);
575 	(void)strncpy( str, line, len );
576 
577 	/* Get real time */
578 	tvsub( &td, &timedol, &time0 );
579 	realt = td.tv_sec + ((double)td.tv_usec) / 1000000;
580 
581 	/* Get CPU time (user+sys) */
582 	tvadd( &tend, &ru1.ru_utime, &ru1.ru_stime );
583 	tvadd( &tstart, &ru0.ru_utime, &ru0.ru_stime );
584 	tvsub( &td, &tend, &tstart );
585 	cput = td.tv_sec + ((double)td.tv_usec) / 1000000;
586 	if( cput < 0.00001 )  cput = 0.00001;
587 	return( cput );
588 }
589 
590 static void
prusage(r0,r1,e,b,outp)591 prusage(r0, r1, e, b, outp)
592 	register struct rusage *r0, *r1;
593 	struct timeval *e, *b;
594 	char *outp;
595 {
596 	struct timeval tdiff;
597 	register time_t t;
598 	register char *cp;
599 	register int i;
600 	int ms;
601 
602 	t = (r1->ru_utime.tv_sec-r0->ru_utime.tv_sec)*100+
603 	    (r1->ru_utime.tv_usec-r0->ru_utime.tv_usec)/10000+
604 	    (r1->ru_stime.tv_sec-r0->ru_stime.tv_sec)*100+
605 	    (r1->ru_stime.tv_usec-r0->ru_stime.tv_usec)/10000;
606 	ms =  (e->tv_sec-b->tv_sec)*100 + (e->tv_usec-b->tv_usec)/10000;
607 
608 #define END(x)	{while(*x) x++;}
609 #if defined(SYSV)
610 	cp = "%Uuser %Ssys %Ereal %P";
611 #else
612 #if defined(sgi)		/* IRIX 3.3 will show 0 for %M,%F,%R,%C */
613 	cp = "%Uuser %Ssys %Ereal %P %Mmaxrss %F+%Rpf %Ccsw";
614 #else
615 	cp = "%Uuser %Ssys %Ereal %P %Xi+%Dd %Mmaxrss %F+%Rpf %Ccsw";
616 #endif
617 #endif
618 	for (; *cp; cp++)  {
619 		if (*cp != '%')
620 			*outp++ = *cp;
621 		else if (cp[1]) switch(*++cp) {
622 
623 		case 'U':
624 			tvsub(&tdiff, &r1->ru_utime, &r0->ru_utime);
625 			sprintf(outp,"%ld.%01ld", tdiff.tv_sec, tdiff.tv_usec/100000);
626 			END(outp);
627 			break;
628 
629 		case 'S':
630 			tvsub(&tdiff, &r1->ru_stime, &r0->ru_stime);
631 			sprintf(outp,"%ld.%01ld", tdiff.tv_sec, tdiff.tv_usec/100000);
632 			END(outp);
633 			break;
634 
635 		case 'E':
636 			psecs(ms / 100, outp);
637 			END(outp);
638 			break;
639 
640 		case 'P':
641 			sprintf(outp,"%d%%", (int) (t*100 / ((ms ? ms : 1))));
642 			END(outp);
643 			break;
644 
645 #if !defined(SYSV)
646 		case 'W':
647 			i = r1->ru_nswap - r0->ru_nswap;
648 			sprintf(outp,"%d", i);
649 			END(outp);
650 			break;
651 
652 		case 'X':
653 			sprintf(outp,"%ld", t == 0 ? 0 : (r1->ru_ixrss-r0->ru_ixrss)/t);
654 			END(outp);
655 			break;
656 
657 		case 'D':
658 			sprintf(outp,"%ld", t == 0 ? 0 :
659 			    (r1->ru_idrss+r1->ru_isrss-(r0->ru_idrss+r0->ru_isrss))/t);
660 			END(outp);
661 			break;
662 
663 		case 'K':
664 			sprintf(outp,"%ld", t == 0 ? 0 :
665 			    ((r1->ru_ixrss+r1->ru_isrss+r1->ru_idrss) -
666 			    (r0->ru_ixrss+r0->ru_idrss+r0->ru_isrss))/t);
667 			END(outp);
668 			break;
669 
670 		case 'M':
671 			sprintf(outp,"%ld", r1->ru_maxrss/2);
672 			END(outp);
673 			break;
674 
675 		case 'F':
676 			sprintf(outp,"%ld", r1->ru_majflt-r0->ru_majflt);
677 			END(outp);
678 			break;
679 
680 		case 'R':
681 			sprintf(outp,"%ld", r1->ru_minflt-r0->ru_minflt);
682 			END(outp);
683 			break;
684 
685 		case 'I':
686 			sprintf(outp,"%ld", r1->ru_inblock-r0->ru_inblock);
687 			END(outp);
688 			break;
689 
690 		case 'O':
691 			sprintf(outp,"%ld", r1->ru_oublock-r0->ru_oublock);
692 			END(outp);
693 			break;
694 		case 'C':
695 			sprintf(outp,"%ld+%ld", r1->ru_nvcsw-r0->ru_nvcsw,
696 				r1->ru_nivcsw-r0->ru_nivcsw );
697 			END(outp);
698 			break;
699 #endif /* !SYSV */
700 		}
701 	}
702 	*outp = '\0';
703 }
704 
705 static void
tvadd(tsum,t0,t1)706 tvadd(tsum, t0, t1)
707 	struct timeval *tsum, *t0, *t1;
708 {
709 
710 	tsum->tv_sec = t0->tv_sec + t1->tv_sec;
711 	tsum->tv_usec = t0->tv_usec + t1->tv_usec;
712 	if (tsum->tv_usec > 1000000)
713 		tsum->tv_sec++, tsum->tv_usec -= 1000000;
714 }
715 
716 static void
tvsub(tdiff,t1,t0)717 tvsub(tdiff, t1, t0)
718 	struct timeval *tdiff, *t1, *t0;
719 {
720 
721 	tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
722 	tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
723 	if (tdiff->tv_usec < 0)
724 		tdiff->tv_sec--, tdiff->tv_usec += 1000000;
725 }
726 
727 static void
psecs(l,cp)728 psecs(l,cp)
729 long l;
730 register char *cp;
731 {
732 	register int i;
733 
734 	i = l / 3600;
735 	if (i) {
736 		sprintf(cp,"%d:", i);
737 		END(cp);
738 		i = l % 3600;
739 		sprintf(cp,"%d%d", (i/60) / 10, (i/60) % 10);
740 		END(cp);
741 	} else {
742 		i = l;
743 		sprintf(cp,"%d", i / 60);
744 		END(cp);
745 	}
746 	i %= 60;
747 	*cp++ = ':';
748 	sprintf(cp,"%d%d", i / 10, i % 10);
749 }
750 
751 /*
752  *			N R E A D
753  */
754 int
Nread(fd,buf,count)755 Nread( fd, buf, count )
756 int fd;
757 void *buf;
758 int count;
759 {
760 	struct sockaddr_in from;
761 	int len = sizeof(from);
762 	register int cnt;
763 	if( udp )  {
764 		cnt = recvfrom( fd, buf, count, 0, (struct sockaddr *)&from, &len );
765 		numCalls++;
766 	} else {
767 		if( b_flag )
768 			cnt = mread( fd, buf, count );	/* fill buf */
769 		else {
770 			cnt = read( fd, buf, count );
771 			numCalls++;
772 		}
773 		if (touchdata && cnt > 0) {
774 			register int c = cnt, sum;
775 			register char *b = buf;
776 			while (c--)
777 				sum += *b++;
778 		}
779 	}
780 	return(cnt);
781 }
782 
783 /*
784  *			N W R I T E
785  */
786 int
Nwrite(fd,buf,count)787 Nwrite( fd, buf, count )
788 int fd;
789 void *buf;
790 int count;
791 {
792 	register int cnt;
793 	if( udp )  {
794 again:
795 		cnt = sendto( fd, buf, count, 0, (struct sockaddr *)&sinhim, sizeof(sinhim) );
796 		numCalls++;
797 		if( cnt<0 && errno == ENOBUFS )  {
798 			delay(18000);
799 			errno = 0;
800 			goto again;
801 		}
802 	} else {
803 		cnt = write( fd, buf, count );
804 		numCalls++;
805 	}
806 	return(cnt);
807 }
808 
809 void
delay(us)810 delay(us)
811 {
812 	struct timeval tv;
813 
814 	tv.tv_sec = 0;
815 	tv.tv_usec = us;
816 	(void)select( 1, NULL, NULL, NULL, &tv );
817 }
818 
819 /*
820  *			M R E A D
821  *
822  * This function performs the function of a read(II) but will
823  * call read(II) multiple times in order to get the requested
824  * number of characters.  This can be necessary because
825  * network connections don't deliver data with the same
826  * grouping as it is written with.  Written by Robert S. Miles, BRL.
827  */
828 int
mread(fd,bufp,n)829 mread(fd, bufp, n)
830 int fd;
831 register char	*bufp;
832 unsigned	n;
833 {
834 	register unsigned	count = 0;
835 	register int		nread;
836 
837 	do {
838 		nread = read(fd, bufp, n-count);
839 		numCalls++;
840 		if(nread < 0)  {
841 			perror("ttcp_mread");
842 			return(-1);
843 		}
844 		if(nread == 0)
845 			return((int)count);
846 		count += (unsigned)nread;
847 		bufp += nread;
848 	 } while(count < n);
849 
850 	return((int)count);
851 }
852