xref: /original-bsd/usr.bin/uucp/uucico/fio.c (revision f238860a)
1 #ifndef lint
2 static char sccsid[] = "@(#)fio.c	5.1 (Berkeley) 06/19/85";
3 #endif
4 
5 /*
6  * flow control protocol.
7  *
8  * This protocol relies on flow control of the data stream.
9  * It is meant for working over links that can (almost) be
10  * guaranteed to be errorfree, specifically X.25/PAD links.
11  * A sumcheck is carried out over a whole file only. If a
12  * transport fails the receiver can request retransmission(s).
13  * This protocol uses a 7-bit datapath only, so it can be
14  * used on links that are not 8-bit transparent.
15  *
16  * When using this protocol with an X.25 PAD:
17  * Although this protocol uses no control chars except CR,
18  * control chars NULL and ^P are used before this protocol
19  * is started; since ^P is the default char for accessing
20  * PAD X.28 command mode, be sure to disable that access
21  * (PAD par 1). Also make sure both flow control pars
22  * (5 and 12) are set. The CR used in this proto is meant
23  * to trigger packet transmission, hence par 3 should be
24  * set to 2; a good value for the Idle Timer (par 4) is 10.
25  * All other pars should be set to 0.
26  * Normally a calling site will take care of setting the
27  * local PAD pars via an X.28 command and those of the remote
28  * PAD via an X.29 command, unless the remote site has a
29  * special channel assigned for this protocol with the proper
30  * par settings.
31  *
32  * Author: Piet Beertema, CWI, Amsterdam, Sep 1984
33  */
34 
35 #include <signal.h>
36 #include "uucp.h"
37 #ifdef USG
38 #include <termio.h>
39 #else !USG
40 #include <sgtty.h>
41 #endif !USG
42 #include <setjmp.h>
43 
44 #define FBUFSIZ		256
45 
46 #ifndef MAXMSGLEN
47 #define MAXMSGLEN	BUFSIZ
48 #endif MAXMSGLEN
49 
50 static int fchksum;
51 static jmp_buf Ffailbuf;
52 
53 static
54 falarm()
55 {
56 	signal(SIGALRM, falarm);
57 	longjmp(Ffailbuf, 1);
58 }
59 
60 static int (*fsig)();
61 
62 #ifndef USG
63 #define TCGETA	TIOCGETP
64 #define TCSETA	TIOCSETP
65 #define termio	sgttyb
66 #endif USG
67 
68 fturnon()
69 {
70 	int ret;
71 	struct termio ttbuf;
72 
73 	ioctl(Ifn, TCGETA, &ttbuf);
74 #ifdef USG
75 	ttbuf.c_iflag = IXOFF|IXON|ISTRIP;
76 	ttbuf.c_cc[VMIN] = FBUFSIZ > 64 ? 64 : FBUFSIZ;
77 	ttbuf.c_cc[VTIME] = 5;
78 #else
79 	ttbuf.sg_flags = ANYP|CBREAK|TANDEM;
80 #endif USG
81 	ret = ioctl(Ifn, TCSETA, &ttbuf);
82 	ASSERT(ret >= 0, "STTY FAILED", "", ret);
83 	fsig = signal(SIGALRM, falarm);
84 	/* give the other side time to perform its ioctl;
85 	 * otherwise it may flush out the first data this
86 	 * side is about to send.
87 	 */
88 	sleep(2);
89 	return SUCCESS;
90 }
91 
92 fturnoff()
93 {
94 	(void) signal(SIGALRM, fsig);
95 	return SUCCESS;
96 }
97 
98 fwrmsg(type, str, fn)
99 register char *str;
100 int fn;
101 char type;
102 {
103 	register char *s;
104 	char bufr[MAXMSGLEN];
105 
106 	s = bufr;
107 	*s++ = type;
108 	while (*str)
109 		*s++ = *str++;
110 	if (*(s-1) == '\n')
111 		s--;
112 	*s++ = '\r';
113 	(void) write(fn, bufr, s - bufr);
114 	return SUCCESS;
115 }
116 
117 frdmsg(str, fn)
118 register char *str;
119 register int fn;
120 {
121 	register char *smax;
122 
123 	if (setjmp(Ffailbuf))
124 		return FAIL;
125 	smax = str + MAXMSGLEN - 1;
126 	(void) alarm(2*MAXMSGTIME);
127 	for (;;) {
128 		if (read(fn, str, 1) <= 0)
129 			goto msgerr;
130 		if (*str == '\r')
131 			break;
132 		if (*str < ' ')
133 			continue;
134 		if (str++ >= smax)
135 			goto msgerr;
136 	}
137 	*str = '\0';
138 	(void) alarm(0);
139 	return SUCCESS;
140 msgerr:
141 	(void) alarm(0);
142 	return FAIL;
143 }
144 
145 fwrdata(fp1, fn)
146 FILE *fp1;
147 int fn;
148 {
149 	register int flen, alen, ret;
150 	char ibuf[FBUFSIZ];
151 	char ack;
152 	long abytes, fbytes;
153 	struct timeb t1, t2;
154 	int mil, retries = 0;
155 
156 	ret = FAIL;
157 retry:
158 	fchksum = 0xffff;
159 	abytes = fbytes = 0L;
160 	ack = '\0';
161 #ifdef USG
162 	time(&t1.time);
163 	t1.millitm = 0;
164 #else !USG
165 	ftime(&t1);
166 #endif !USG
167 	while ((flen = fread(ibuf, sizeof (char), FBUFSIZ, fp1)) > 0) {
168 		alen = fwrblk(fn, ibuf, flen);
169 		abytes += alen >= 0 ? alen : -alen;
170 		if (alen <= 0)
171 			goto acct;
172 		fbytes += flen;
173 	}
174 	sprintf(ibuf, "\176\176%04x\r", fchksum);
175 	abytes += alen = strlen(ibuf);
176 	if (write(fn, ibuf, alen) == alen) {
177 		DEBUG(8, "%d\n", alen);
178 		DEBUG(8, "checksum: %04x\n", fchksum);
179 		if (frdmsg(ibuf, fn) != FAIL) {
180 			if ((ack = ibuf[0]) == 'G')
181 				ret = 0;
182 			DEBUG(4, "ack - '%c'\n", ack);
183 		}
184 	}
185 acct:
186 	if (ack == 'R') {
187 		DEBUG(4, "RETRY:\n", 0);
188 		fseek(fp1, 0L, 0);
189 		retries++;
190 		goto retry;
191 	}
192 #ifdef USG
193 	time(&t2.time);
194 	t2.millitm = 0;
195 #else !USG
196 	ftime(&t2);
197 #endif !USG
198 	Now = t2;
199 	t2.time -= t1.time;
200 	mil = t2.millitm - t1.millitm;
201 	if (mil < 0) {
202 		--t2.time;
203 		mil += 1000;
204 	}
205 	sprintf(ibuf, "sent data %ld bytes %ld.%02d secs",
206 				fbytes, (long)t2.time, mil/10);
207 	sysacct(abytes, t2.time - t1.time);
208 	if (retries > 0)
209 		sprintf((char *)ibuf+strlen(ibuf)," %d retries", retries);
210 	DEBUG(1, "%s\n", ibuf);
211 	syslog(ibuf);
212 #ifdef SYSACCT
213 	if (ret)
214 		sysaccf(NULL);		/* force accounting */
215 #endif SYSACCT
216 	return ret;
217 }
218 
219 /* max. attempts to retransmit a file: */
220 #define MAXRETRIES	(fbytes < 10000L ? 2 : 1)
221 
222 frddata(fn, fp2)
223 register int fn;
224 register FILE *fp2;
225 {
226 	register int flen;
227 	register char eof;
228 	char ibuf[FBUFSIZ];
229 	int ret, retries = 0;
230 	long alen, abytes, fbytes;
231 	struct timeb t1, t2;
232 	int mil;
233 
234 	ret = FAIL;
235 retry:
236 	fchksum = 0xffff;
237 	abytes = fbytes = 0L;
238 #ifdef USG
239 	time(&t1.time);
240 	t1.millitm = 0;
241 #else !USG
242 	ftime(&t1);
243 #endif !USG
244 	do {
245 		flen = frdblk(ibuf, fn, &alen);
246 		abytes += alen;
247 		if (flen < 0)
248 			goto acct;
249 		if (eof = flen > FBUFSIZ)
250 			flen -= FBUFSIZ + 1;
251 		fbytes += flen;
252 		if (fwrite(ibuf, sizeof (char), flen, fp2) != flen)
253 			goto acct;
254 	} while (!eof);
255 	ret = 0;
256 acct:
257 	if (ret) {
258 		if (retries++ < MAXRETRIES) {
259 			DEBUG(8, "send ack: 'R'\n", 0);
260 			fwrmsg('R', "", fn);
261 			fseek(fp2, 0L, 0);
262 			DEBUG(4, "RETRY:\n", 0);
263 			goto retry;
264 		}
265 		DEBUG(8, "send ack: 'Q'\n", 0);
266 		fwrmsg('Q', "", fn);
267 #ifdef SYSACCT
268 		sysaccf(NULL);		/* force accounting */
269 #endif SYSACCT
270 	} else {
271 		DEBUG(8, "send ack: 'G'\n", 0);
272 		fwrmsg('G', "", fn);
273 	}
274 #ifdef USG
275 	time(&t2.time);
276 	t2.millitm = 0;
277 #else !USG
278 	ftime(&t2);
279 #endif !USG
280 	Now = t2;
281 	t2.time -= t1.time;
282 	mil = t2.millitm - t1.millitm;
283 	if (mil < 0) {
284 		--t2.time;
285 		mil += 1000;
286 	}
287 	sprintf(ibuf, "received data %ld bytes %ld.%02d secs",
288 				fbytes, (long)t2.time, mil/10);
289 	sysacct(abytes, t2.time - t1.time);
290 	if (retries > 0)
291 		sprintf((char *)ibuf+strlen(ibuf)," %d retries", retries);
292 	DEBUG(1, "%s\n", ibuf);
293 	syslog(ibuf);
294 	return ret;
295 }
296 
297 static
298 frdbuf(blk, len, fn)
299 register char *blk;
300 register int len;
301 register int fn;
302 {
303 	static int ret = FBUFSIZ / 2;
304 #ifndef Not080
305 	extern int linebaudrate;
306 #endif Not080
307 
308 	if (setjmp(Ffailbuf))
309 		return FAIL;
310 #ifndef Not080
311 	if (len == FBUFSIZ && ret < FBUFSIZ / 2 &&
312 	    linebaudrate > 0 && linebaudrate < 4800)
313 		sleep(1);
314 #endif Not080
315 	(void) alarm(MAXMSGTIME);
316 	ret = read(fn, blk, len);
317 	alarm(0);
318 	return ret <= 0 ? FAIL : ret;
319 }
320 
321 /* call ultouch every TC calls to either frdblk or fwrblk  */
322 
323 #define	TC	20
324 static	int tc = TC;
325 
326 /* Byte conversion:
327  *
328  *   from        pre       to
329  * 000-037       172     100-137
330  * 040-171               040-171
331  * 172-177       173     072-077
332  * 200-237       174     100-137
333  * 240-371       175     040-171
334  * 372-377       176     072-077
335  */
336 
337 static
338 fwrblk(fn, ip, len)
339 int fn;
340 register char *ip;
341 register int len;
342 {
343 	register char *op;
344 	register int sum, nl;
345 	int ret;
346 	char obuf[FBUFSIZ * 2];
347 
348 	/* call ultouch occasionally */
349 	if (--tc < 0) {
350 		tc = TC;
351 		ultouch();
352 	}
353 	DEBUG(8, "%d/", len);
354 	op = obuf;
355 	nl = 0;
356 	sum = fchksum;
357 	do {
358 		if (sum & 0x8000) {
359 			sum <<= 1;
360 			sum++;
361 		} else
362 			sum <<= 1;
363 		sum += *ip & 0377;
364 		sum &= 0xffff;
365 		if (*ip & 0200) {
366 			*ip &= 0177;
367 			if (*ip < 040) {
368 				*op++ = '\174';
369 				*op++ = *ip++ + 0100;
370 			} else
371 			if (*ip <= 0171) {
372 				*op++ = '\175';
373 				*op++ = *ip++;
374 			}
375 			else {
376 				*op++ = '\176';
377 				*op++ = *ip++ - 0100;
378 			}
379 			nl += 2;
380 		} else {
381 			if (*ip < 040) {
382 				*op++ = '\172';
383 				*op++ = *ip++ + 0100;
384 				nl += 2;
385 			} else
386 			if (*ip <= 0171) {
387 				*op++ = *ip++;
388 				nl++;
389 			} else {
390 				*op++ = '\173';
391 				*op++ = *ip++ - 0100;
392 				nl += 2;
393 			}
394 		}
395 	} while (--len);
396 	fchksum = sum;
397 	DEBUG(8, "%d,", nl);
398 	ret = write(fn, obuf, nl);
399 	return ret == nl ? nl : ret < 0 ? 0 : -ret;
400 }
401 
402 static
403 frdblk(ip, fn, rlen)
404 register char *ip;
405 int fn;
406 long *rlen;
407 {
408 	register char *op, c;
409 	register int sum, len, nl;
410 	char buf[5], *erbp = ip;
411 	int i;
412 	static char special = 0;
413 
414 	/* call ultouch occasionally */
415 	if (--tc < 0) {
416 		tc = TC;
417 		ultouch();
418 	}
419 
420 	if ((len = frdbuf(ip, FBUFSIZ, fn)) == FAIL) {
421 		*rlen = 0;
422 		goto dcorr;
423 	}
424 	*rlen = len;
425 	DEBUG(8, "%d/", len);
426 	op = ip;
427 	nl = 0;
428 	sum = fchksum;
429 	do {
430 		if ((*ip &= 0177) >= '\172') {
431 			if (special) {
432 				DEBUG(8, "%d", nl);
433 				special = 0;
434 				op = buf;
435 				if (*ip++ != '\176' || (i = --len) > 5)
436 					goto dcorr;
437 				while (i--)
438 					*op++ = *ip++;
439 				while (len < 5) {
440 					i = frdbuf(&buf[len], 5 - len, fn);
441 					if (i == FAIL) {
442 						len = FAIL;
443 						goto dcorr;
444 					}
445 					DEBUG(8, ",%d", i);
446 					len += i;
447 					*rlen += i;
448 				}
449 				if (buf[4] != '\r')
450 					goto dcorr;
451 				sscanf(buf, "%4x", &fchksum);
452 				DEBUG(8, "\nchecksum: %04x\n", sum);
453 				if (fchksum == sum)
454 					return FBUFSIZ + 1 + nl;
455 				else {
456 					DEBUG(8, "\n", 0);
457 					DEBUG(4, "Bad checksum\n", 0);
458 					return FAIL;
459 				}
460 			}
461 			special = *ip++;
462 		} else {
463 			if (*ip < '\040') {
464 				/* error: shouldn't get control chars */
465 				goto dcorr;
466 			}
467 			switch (special) {
468 			case 0:
469 				c = *ip++;
470 				break;
471 			case '\172':
472 				c = *ip++ - 0100;
473 				break;
474 			case '\173':
475 				c = *ip++ + 0100;
476 				break;
477 			case '\174':
478 				c = *ip++ + 0100;
479 				break;
480 			case '\175':
481 				c = *ip++ + 0200;
482 				break;
483 			case '\176':
484 				c = *ip++ + 0300;
485 				break;
486 			}
487 			*op++ = c;
488 			if (sum & 0x8000) {
489 				sum <<= 1;
490 				sum++;
491 			} else
492 				sum <<= 1;
493 			sum += c & 0377;
494 			sum &= 0xffff;
495 			special = 0;
496 			nl++;
497 		}
498 	} while (--len);
499 	fchksum = sum;
500 	DEBUG(8, "%d,", nl);
501 	return nl;
502 dcorr:
503 	DEBUG(8, "\n", 0);
504 	DEBUG(4, "Data corrupted\n", 0);
505 	while (len != FAIL) {
506 		if ((len = frdbuf(erbp, FBUFSIZ, fn)) != FAIL)
507 			*rlen += len;
508 	}
509 	return FAIL;
510 }
511