Home | History | Annotate | Line # | Download | only in tftp
tftp.c revision 1.9.2.1
      1  1.9.2.1        he /*	$NetBSD: tftp.c,v 1.9.2.1 2000/01/23 12:10:09 he Exp $	*/
      2      1.4       jtc 
      3      1.1       cgd /*
      4      1.4       jtc  * Copyright (c) 1983, 1993
      5      1.4       jtc  *	The Regents of the University of California.  All rights reserved.
      6      1.1       cgd  *
      7      1.1       cgd  * Redistribution and use in source and binary forms, with or without
      8      1.1       cgd  * modification, are permitted provided that the following conditions
      9      1.1       cgd  * are met:
     10      1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     11      1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     12      1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     13      1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     14      1.1       cgd  *    documentation and/or other materials provided with the distribution.
     15      1.1       cgd  * 3. All advertising materials mentioning features or use of this software
     16      1.1       cgd  *    must display the following acknowledgement:
     17      1.1       cgd  *	This product includes software developed by the University of
     18      1.1       cgd  *	California, Berkeley and its contributors.
     19      1.1       cgd  * 4. Neither the name of the University nor the names of its contributors
     20      1.1       cgd  *    may be used to endorse or promote products derived from this software
     21      1.1       cgd  *    without specific prior written permission.
     22      1.1       cgd  *
     23      1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24      1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25      1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26      1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27      1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28      1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29      1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30      1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31      1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32      1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33      1.1       cgd  * SUCH DAMAGE.
     34      1.1       cgd  */
     35      1.1       cgd 
     36      1.6       mrg #include <sys/cdefs.h>
     37      1.1       cgd #ifndef lint
     38      1.4       jtc #if 0
     39      1.4       jtc static char sccsid[] = "@(#)tftp.c	8.1 (Berkeley) 6/6/93";
     40      1.6       mrg #else
     41  1.9.2.1        he __RCSID("$NetBSD: tftp.c,v 1.9.2.1 2000/01/23 12:10:09 he Exp $");
     42      1.4       jtc #endif
     43      1.1       cgd #endif /* not lint */
     44      1.1       cgd 
     45      1.1       cgd /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
     46      1.1       cgd 
     47      1.1       cgd /*
     48      1.1       cgd  * TFTP User Program -- Protocol Machines
     49      1.1       cgd  */
     50      1.1       cgd #include <sys/types.h>
     51      1.1       cgd #include <sys/socket.h>
     52      1.1       cgd #include <sys/time.h>
     53      1.1       cgd 
     54      1.1       cgd #include <netinet/in.h>
     55      1.1       cgd 
     56      1.1       cgd #include <arpa/tftp.h>
     57      1.1       cgd 
     58      1.7     lukem #include <err.h>
     59      1.4       jtc #include <errno.h>
     60      1.4       jtc #include <setjmp.h>
     61      1.1       cgd #include <signal.h>
     62      1.1       cgd #include <stdio.h>
     63      1.5       cgd #include <string.h>
     64      1.4       jtc #include <unistd.h>
     65      1.4       jtc 
     66      1.4       jtc #include "extern.h"
     67      1.4       jtc #include "tftpsubs.h"
     68      1.1       cgd 
     69      1.4       jtc extern  struct sockaddr_in peeraddr;	/* filled in by main */
     70      1.4       jtc extern  int     f;			/* the opened socket */
     71      1.1       cgd extern  int     trace;
     72      1.1       cgd extern  int     verbose;
     73      1.1       cgd extern  int     rexmtval;
     74      1.1       cgd extern  int     maxtimeout;
     75      1.1       cgd 
     76      1.1       cgd #define PKTSIZE    SEGSIZE+4
     77      1.1       cgd char    ackbuf[PKTSIZE];
     78      1.1       cgd int	timeout;
     79      1.1       cgd jmp_buf	toplevel;
     80      1.1       cgd jmp_buf	timeoutbuf;
     81      1.1       cgd 
     82      1.4       jtc static void nak __P((int));
     83      1.4       jtc static int makerequest __P((int, const char *, struct tftphdr *, const char *));
     84      1.4       jtc static void printstats __P((const char *, unsigned long));
     85      1.4       jtc static void startclock __P((void));
     86      1.4       jtc static void stopclock __P((void));
     87      1.4       jtc static void timer __P((int));
     88      1.4       jtc static void tpacket __P((const char *, struct tftphdr *, int));
     89      1.1       cgd 
     90      1.1       cgd /*
     91      1.1       cgd  * Send the requested file.
     92      1.1       cgd  */
     93      1.4       jtc void
     94      1.1       cgd sendfile(fd, name, mode)
     95      1.1       cgd 	int fd;
     96      1.1       cgd 	char *name;
     97      1.1       cgd 	char *mode;
     98      1.1       cgd {
     99      1.7     lukem 	struct tftphdr *ap;	   /* data and ack packets */
    100      1.6       mrg 	struct tftphdr *dp;
    101      1.7     lukem 	int n;
    102      1.4       jtc 	volatile int block, size, convert;
    103      1.4       jtc 	volatile unsigned long amount;
    104      1.1       cgd 	struct sockaddr_in from;
    105      1.1       cgd 	int fromlen;
    106      1.1       cgd 	FILE *file;
    107      1.1       cgd 
    108      1.4       jtc 	startclock();		/* start stat's clock */
    109      1.4       jtc 	dp = r_init();		/* reset fillbuf/read-ahead code */
    110      1.1       cgd 	ap = (struct tftphdr *)ackbuf;
    111      1.1       cgd 	file = fdopen(fd, "r");
    112      1.1       cgd 	convert = !strcmp(mode, "netascii");
    113      1.4       jtc 	block = 0;
    114      1.4       jtc 	amount = 0;
    115      1.1       cgd 
    116      1.1       cgd 	signal(SIGALRM, timer);
    117      1.1       cgd 	do {
    118      1.1       cgd 		if (block == 0)
    119      1.1       cgd 			size = makerequest(WRQ, name, dp, mode) - 4;
    120      1.1       cgd 		else {
    121      1.4       jtc 		/*	size = read(fd, dp->th_data, SEGSIZE);	 */
    122      1.1       cgd 			size = readit(file, &dp, convert);
    123      1.1       cgd 			if (size < 0) {
    124      1.1       cgd 				nak(errno + 100);
    125      1.1       cgd 				break;
    126      1.1       cgd 			}
    127      1.1       cgd 			dp->th_opcode = htons((u_short)DATA);
    128      1.1       cgd 			dp->th_block = htons((u_short)block);
    129      1.1       cgd 		}
    130      1.1       cgd 		timeout = 0;
    131      1.1       cgd 		(void) setjmp(timeoutbuf);
    132      1.1       cgd send_data:
    133      1.1       cgd 		if (trace)
    134      1.1       cgd 			tpacket("sent", dp, size + 4);
    135      1.1       cgd 		n = sendto(f, dp, size + 4, 0,
    136      1.4       jtc 		    (struct sockaddr *)&peeraddr, sizeof(peeraddr));
    137      1.1       cgd 		if (n != size + 4) {
    138      1.7     lukem 			warn("sendto");
    139      1.1       cgd 			goto abort;
    140      1.1       cgd 		}
    141      1.1       cgd 		read_ahead(file, convert);
    142      1.1       cgd 		for ( ; ; ) {
    143      1.1       cgd 			alarm(rexmtval);
    144      1.1       cgd 			do {
    145      1.4       jtc 				fromlen = sizeof(from);
    146      1.4       jtc 				n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
    147      1.1       cgd 				    (struct sockaddr *)&from, &fromlen);
    148      1.1       cgd 			} while (n <= 0);
    149      1.1       cgd 			alarm(0);
    150      1.1       cgd 			if (n < 0) {
    151      1.7     lukem 				warn("recvfrom");
    152      1.1       cgd 				goto abort;
    153      1.1       cgd 			}
    154      1.4       jtc 			peeraddr.sin_port = from.sin_port;	/* added */
    155      1.1       cgd 			if (trace)
    156      1.1       cgd 				tpacket("received", ap, n);
    157      1.1       cgd 			/* should verify packet came from server */
    158      1.1       cgd 			ap->th_opcode = ntohs(ap->th_opcode);
    159      1.1       cgd 			ap->th_block = ntohs(ap->th_block);
    160      1.1       cgd 			if (ap->th_opcode == ERROR) {
    161      1.1       cgd 				printf("Error code %d: %s\n", ap->th_code,
    162      1.1       cgd 					ap->th_msg);
    163      1.1       cgd 				goto abort;
    164      1.1       cgd 			}
    165      1.1       cgd 			if (ap->th_opcode == ACK) {
    166      1.1       cgd 				int j;
    167      1.1       cgd 
    168      1.1       cgd 				if (ap->th_block == block) {
    169      1.1       cgd 					break;
    170      1.1       cgd 				}
    171      1.1       cgd 				/* On an error, try to synchronize
    172      1.1       cgd 				 * both sides.
    173      1.1       cgd 				 */
    174      1.1       cgd 				j = synchnet(f);
    175      1.1       cgd 				if (j && trace) {
    176      1.1       cgd 					printf("discarded %d packets\n",
    177      1.1       cgd 							j);
    178      1.1       cgd 				}
    179      1.1       cgd 				if (ap->th_block == (block-1)) {
    180      1.1       cgd 					goto send_data;
    181      1.1       cgd 				}
    182      1.1       cgd 			}
    183      1.1       cgd 		}
    184      1.1       cgd 		if (block > 0)
    185      1.1       cgd 			amount += size;
    186      1.1       cgd 		block++;
    187      1.1       cgd 	} while (size == SEGSIZE || block == 1);
    188      1.1       cgd abort:
    189      1.1       cgd 	fclose(file);
    190      1.1       cgd 	stopclock();
    191      1.1       cgd 	if (amount > 0)
    192      1.1       cgd 		printstats("Sent", amount);
    193      1.1       cgd }
    194      1.1       cgd 
    195      1.1       cgd /*
    196      1.1       cgd  * Receive a file.
    197      1.1       cgd  */
    198      1.4       jtc void
    199      1.1       cgd recvfile(fd, name, mode)
    200      1.1       cgd 	int fd;
    201      1.1       cgd 	char *name;
    202      1.1       cgd 	char *mode;
    203      1.1       cgd {
    204      1.7     lukem 	struct tftphdr *ap;
    205      1.6       mrg 	struct tftphdr *dp;
    206      1.7     lukem 	int n;
    207      1.4       jtc 	volatile int block, size, firsttrip;
    208      1.4       jtc 	volatile unsigned long amount;
    209      1.1       cgd 	struct sockaddr_in from;
    210      1.4       jtc 	int fromlen;
    211      1.1       cgd 	FILE *file;
    212      1.4       jtc 	volatile int convert;		/* true if converting crlf -> lf */
    213      1.1       cgd 
    214      1.1       cgd 	startclock();
    215      1.1       cgd 	dp = w_init();
    216      1.1       cgd 	ap = (struct tftphdr *)ackbuf;
    217      1.1       cgd 	file = fdopen(fd, "w");
    218      1.1       cgd 	convert = !strcmp(mode, "netascii");
    219      1.4       jtc 	block = 1;
    220      1.4       jtc 	firsttrip = 1;
    221      1.4       jtc 	amount = 0;
    222      1.1       cgd 
    223      1.1       cgd 	signal(SIGALRM, timer);
    224      1.1       cgd 	do {
    225      1.1       cgd 		if (firsttrip) {
    226      1.1       cgd 			size = makerequest(RRQ, name, ap, mode);
    227      1.1       cgd 			firsttrip = 0;
    228      1.1       cgd 		} else {
    229      1.1       cgd 			ap->th_opcode = htons((u_short)ACK);
    230      1.1       cgd 			ap->th_block = htons((u_short)(block));
    231      1.1       cgd 			size = 4;
    232      1.1       cgd 			block++;
    233      1.1       cgd 		}
    234      1.1       cgd 		timeout = 0;
    235      1.1       cgd 		(void) setjmp(timeoutbuf);
    236      1.1       cgd send_ack:
    237      1.1       cgd 		if (trace)
    238      1.1       cgd 			tpacket("sent", ap, size);
    239      1.4       jtc 		if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
    240      1.4       jtc 		    sizeof(peeraddr)) != size) {
    241      1.1       cgd 			alarm(0);
    242      1.7     lukem 			warn("sendto");
    243      1.1       cgd 			goto abort;
    244      1.1       cgd 		}
    245      1.1       cgd 		write_behind(file, convert);
    246      1.1       cgd 		for ( ; ; ) {
    247      1.1       cgd 			alarm(rexmtval);
    248      1.1       cgd 			do  {
    249      1.4       jtc 				fromlen = sizeof(from);
    250      1.1       cgd 				n = recvfrom(f, dp, PKTSIZE, 0,
    251      1.1       cgd 				    (struct sockaddr *)&from, &fromlen);
    252      1.1       cgd 			} while (n <= 0);
    253      1.1       cgd 			alarm(0);
    254      1.1       cgd 			if (n < 0) {
    255      1.7     lukem 				warn("recvfrom");
    256      1.1       cgd 				goto abort;
    257      1.1       cgd 			}
    258      1.4       jtc 			peeraddr.sin_port = from.sin_port;	/* added */
    259      1.1       cgd 			if (trace)
    260      1.1       cgd 				tpacket("received", dp, n);
    261      1.1       cgd 			/* should verify client address */
    262      1.1       cgd 			dp->th_opcode = ntohs(dp->th_opcode);
    263      1.1       cgd 			dp->th_block = ntohs(dp->th_block);
    264      1.1       cgd 			if (dp->th_opcode == ERROR) {
    265      1.1       cgd 				printf("Error code %d: %s\n", dp->th_code,
    266      1.1       cgd 					dp->th_msg);
    267      1.1       cgd 				goto abort;
    268      1.1       cgd 			}
    269      1.1       cgd 			if (dp->th_opcode == DATA) {
    270      1.1       cgd 				int j;
    271      1.1       cgd 
    272      1.1       cgd 				if (dp->th_block == block) {
    273      1.4       jtc 					break;		/* have next packet */
    274      1.1       cgd 				}
    275      1.1       cgd 				/* On an error, try to synchronize
    276      1.1       cgd 				 * both sides.
    277      1.1       cgd 				 */
    278      1.1       cgd 				j = synchnet(f);
    279      1.1       cgd 				if (j && trace) {
    280      1.1       cgd 					printf("discarded %d packets\n", j);
    281      1.1       cgd 				}
    282      1.1       cgd 				if (dp->th_block == (block-1)) {
    283      1.4       jtc 					goto send_ack;	/* resend ack */
    284      1.1       cgd 				}
    285      1.1       cgd 			}
    286      1.1       cgd 		}
    287      1.4       jtc 	/*	size = write(fd, dp->th_data, n - 4); */
    288      1.1       cgd 		size = writeit(file, &dp, n - 4, convert);
    289      1.1       cgd 		if (size < 0) {
    290      1.1       cgd 			nak(errno + 100);
    291      1.1       cgd 			break;
    292      1.1       cgd 		}
    293      1.1       cgd 		amount += size;
    294      1.1       cgd 	} while (size == SEGSIZE);
    295      1.4       jtc abort:						/* ok to ack, since user */
    296      1.4       jtc 	ap->th_opcode = htons((u_short)ACK);	/* has seen err msg */
    297      1.1       cgd 	ap->th_block = htons((u_short)block);
    298      1.4       jtc 	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
    299      1.4       jtc 	    sizeof(peeraddr));
    300      1.4       jtc 	write_behind(file, convert);		/* flush last buffer */
    301      1.1       cgd 	fclose(file);
    302      1.1       cgd 	stopclock();
    303      1.1       cgd 	if (amount > 0)
    304      1.1       cgd 		printstats("Received", amount);
    305      1.1       cgd }
    306      1.1       cgd 
    307      1.4       jtc static int
    308      1.1       cgd makerequest(request, name, tp, mode)
    309      1.1       cgd 	int request;
    310      1.4       jtc 	const char *name;
    311      1.1       cgd 	struct tftphdr *tp;
    312      1.4       jtc 	const char *mode;
    313      1.1       cgd {
    314      1.7     lukem 	char *cp;
    315      1.1       cgd 
    316      1.1       cgd 	tp->th_opcode = htons((u_short)request);
    317      1.9  christos #ifndef __SVR4
    318      1.1       cgd 	cp = tp->th_stuff;
    319      1.9  christos #else
    320      1.9  christos 	cp = (void *)&tp->th_stuff;
    321      1.9  christos #endif
    322      1.1       cgd 	strcpy(cp, name);
    323      1.1       cgd 	cp += strlen(name);
    324      1.1       cgd 	*cp++ = '\0';
    325      1.1       cgd 	strcpy(cp, mode);
    326      1.1       cgd 	cp += strlen(mode);
    327      1.1       cgd 	*cp++ = '\0';
    328      1.1       cgd 	return (cp - (char *)tp);
    329      1.1       cgd }
    330      1.1       cgd 
    331      1.8   mycroft const struct errmsg {
    332      1.1       cgd 	int	e_code;
    333      1.8   mycroft 	const char *e_msg;
    334      1.1       cgd } errmsgs[] = {
    335      1.1       cgd 	{ EUNDEF,	"Undefined error code" },
    336      1.1       cgd 	{ ENOTFOUND,	"File not found" },
    337      1.1       cgd 	{ EACCESS,	"Access violation" },
    338      1.1       cgd 	{ ENOSPACE,	"Disk full or allocation exceeded" },
    339      1.1       cgd 	{ EBADOP,	"Illegal TFTP operation" },
    340      1.1       cgd 	{ EBADID,	"Unknown transfer ID" },
    341      1.1       cgd 	{ EEXISTS,	"File already exists" },
    342      1.1       cgd 	{ ENOUSER,	"No such user" },
    343      1.1       cgd 	{ -1,		0 }
    344      1.1       cgd };
    345      1.1       cgd 
    346      1.1       cgd /*
    347      1.1       cgd  * Send a nak packet (error message).
    348      1.1       cgd  * Error code passed in is one of the
    349      1.1       cgd  * standard TFTP codes, or a UNIX errno
    350      1.1       cgd  * offset by 100.
    351      1.1       cgd  */
    352      1.4       jtc static void
    353      1.1       cgd nak(error)
    354      1.1       cgd 	int error;
    355      1.1       cgd {
    356      1.8   mycroft 	const struct errmsg *pe;
    357      1.7     lukem 	struct tftphdr *tp;
    358      1.1       cgd 	int length;
    359      1.1       cgd 
    360      1.1       cgd 	tp = (struct tftphdr *)ackbuf;
    361      1.1       cgd 	tp->th_opcode = htons((u_short)ERROR);
    362      1.1       cgd 	for (pe = errmsgs; pe->e_code >= 0; pe++)
    363      1.1       cgd 		if (pe->e_code == error)
    364      1.1       cgd 			break;
    365      1.1       cgd 	if (pe->e_code < 0) {
    366      1.1       cgd 		tp->th_code = EUNDEF;
    367      1.8   mycroft 		strcpy(tp->th_msg, strerror(error - 100));
    368      1.8   mycroft 	} else {
    369      1.8   mycroft 		tp->th_code = htons((u_short)error);
    370      1.8   mycroft 		strcpy(tp->th_msg, pe->e_msg);
    371      1.1       cgd 	}
    372      1.1       cgd 	length = strlen(pe->e_msg) + 4;
    373      1.1       cgd 	if (trace)
    374      1.1       cgd 		tpacket("sent", tp, length);
    375      1.4       jtc 	if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
    376      1.4       jtc 	    sizeof(peeraddr)) != length)
    377      1.7     lukem 		warn("nak");
    378      1.1       cgd }
    379      1.1       cgd 
    380      1.4       jtc static void
    381      1.1       cgd tpacket(s, tp, n)
    382      1.4       jtc 	const char *s;
    383      1.1       cgd 	struct tftphdr *tp;
    384      1.1       cgd 	int n;
    385      1.1       cgd {
    386      1.1       cgd 	static char *opcodes[] =
    387      1.1       cgd 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
    388      1.7     lukem 	char *cp, *file;
    389      1.1       cgd 	u_short op = ntohs(tp->th_opcode);
    390      1.1       cgd 
    391      1.1       cgd 	if (op < RRQ || op > ERROR)
    392      1.1       cgd 		printf("%s opcode=%x ", s, op);
    393      1.1       cgd 	else
    394      1.1       cgd 		printf("%s %s ", s, opcodes[op]);
    395      1.1       cgd 	switch (op) {
    396      1.1       cgd 
    397      1.1       cgd 	case RRQ:
    398      1.1       cgd 	case WRQ:
    399      1.1       cgd 		n -= 2;
    400      1.9  christos #ifndef __SVR4
    401      1.9  christos 		cp = tp->th_stuff;
    402      1.9  christos #else
    403      1.9  christos 		cp = (void *) &tp->th_stuff;
    404      1.9  christos #endif
    405      1.9  christos 		file = cp;
    406      1.7     lukem 		cp = strchr(cp, '\0');
    407      1.1       cgd 		printf("<file=%s, mode=%s>\n", file, cp + 1);
    408      1.1       cgd 		break;
    409      1.1       cgd 
    410      1.1       cgd 	case DATA:
    411      1.1       cgd 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
    412      1.1       cgd 		break;
    413      1.1       cgd 
    414      1.1       cgd 	case ACK:
    415      1.1       cgd 		printf("<block=%d>\n", ntohs(tp->th_block));
    416      1.1       cgd 		break;
    417      1.1       cgd 
    418      1.1       cgd 	case ERROR:
    419      1.1       cgd 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
    420      1.1       cgd 		break;
    421      1.1       cgd 	}
    422      1.1       cgd }
    423      1.1       cgd 
    424      1.1       cgd struct timeval tstart;
    425      1.1       cgd struct timeval tstop;
    426      1.1       cgd 
    427      1.4       jtc static void
    428      1.4       jtc startclock()
    429      1.4       jtc {
    430      1.4       jtc 
    431      1.4       jtc 	(void)gettimeofday(&tstart, NULL);
    432      1.1       cgd }
    433      1.1       cgd 
    434      1.4       jtc static void
    435      1.4       jtc stopclock()
    436      1.4       jtc {
    437      1.4       jtc 
    438      1.4       jtc 	(void)gettimeofday(&tstop, NULL);
    439      1.1       cgd }
    440      1.1       cgd 
    441      1.4       jtc static void
    442      1.1       cgd printstats(direction, amount)
    443      1.4       jtc 	const char *direction;
    444      1.4       jtc 	unsigned long amount;
    445      1.1       cgd {
    446      1.1       cgd 	double delta;
    447      1.6       mrg 
    448      1.6       mrg 	/* compute delta in 1/10's second units */
    449      1.1       cgd 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
    450      1.1       cgd 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
    451      1.1       cgd 	delta = delta/10.;      /* back to seconds */
    452      1.6       mrg 	printf("%s %ld bytes in %.1f seconds", direction, amount, delta);
    453      1.1       cgd 	if (verbose)
    454      1.1       cgd 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
    455      1.1       cgd 	putchar('\n');
    456      1.1       cgd }
    457      1.1       cgd 
    458      1.4       jtc static void
    459      1.4       jtc timer(sig)
    460      1.4       jtc 	int sig;
    461      1.4       jtc {
    462      1.4       jtc 
    463      1.4       jtc 	timeout += rexmtval;
    464      1.4       jtc 	if (timeout >= maxtimeout) {
    465      1.4       jtc 		printf("Transfer timed out.\n");
    466      1.4       jtc 		longjmp(toplevel, -1);
    467      1.4       jtc 	}
    468      1.4       jtc 	longjmp(timeoutbuf, 1);
    469      1.4       jtc }
    470