BSD 4_3_Tahoe release
[unix-history] / usr / src / usr.bin / uucp / fio.c
CommitLineData
31b8bf0a 1#ifndef lint
ca67e7b4 2static char sccsid[] = "@(#)fio.c 5.5 (Berkeley) 4/5/88";
31b8bf0a
JB
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.
9d469936 26 *
31b8bf0a
JB
27 * Normally a calling site will take care of setting the
28 * local PAD pars via an X.28 command and those of the remote
29 * PAD via an X.29 command, unless the remote site has a
30 * special channel assigned for this protocol with the proper
31 * par settings.
32 *
9d469936
JB
33 * Additional comments for hosts with direct X.25 access:
34 * - the global variable IsTcpIp, when set, excludes the ioctl's,
35 * so the same binary can run on X.25 and non-X.25 hosts;
36 * - reads are done in small chunks, which can be smaller than
37 * the packet size; your X.25 driver must support that.
38 *
39 *
40 * Author:
41 * Piet Beertema, CWI, Amsterdam, Sep 1984
42 * Modified for X.25 hosts:
43 * Robert Elz, Melbourne Univ, Mar 1985
31b8bf0a
JB
44 */
45
31b8bf0a 46#include "uucp.h"
9d469936 47#include <signal.h>
31b8bf0a
JB
48#ifdef USG
49#include <termio.h>
50#else !USG
51#include <sgtty.h>
52#endif !USG
53#include <setjmp.h>
54
0aec7641 55#define FIBUFSIZ 4096 /* for X.25 interfaces: set equal to packet size,
9d469936
JB
56 * but see comment above
57 */
58
0aec7641 59#define FOBUFSIZ 4096 /* for X.25 interfaces: set equal to packet size;
9d469936
JB
60 * otherwise make as large as feasible to reduce
61 * number of write system calls
62 */
31b8bf0a
JB
63
64#ifndef MAXMSGLEN
65#define MAXMSGLEN BUFSIZ
66#endif MAXMSGLEN
67
68static int fchksum;
69static jmp_buf Ffailbuf;
70
0aec7641
RA
71extern long Bytes_Sent, Bytes_Received;
72
31b8bf0a
JB
73static
74falarm()
75{
76 signal(SIGALRM, falarm);
77 longjmp(Ffailbuf, 1);
78}
79
80static int (*fsig)();
81
82#ifndef USG
83#define TCGETA TIOCGETP
87d365c2 84#define TCSETAF TIOCSETP
31b8bf0a
JB
85#define termio sgttyb
86#endif USG
0aec7641 87static struct termio ttbuf;
31b8bf0a
JB
88
89fturnon()
90{
0aec7641 91 int ttbuf_flags;
31b8bf0a 92
9d469936
JB
93 if (!IsTcpIp) {
94 ioctl(Ifn, TCGETA, &ttbuf);
31b8bf0a 95#ifdef USG
0aec7641 96 ttbuf_flags = ttbuf.c_iflag;
9d469936
JB
97 ttbuf.c_iflag = IXOFF|IXON|ISTRIP;
98 ttbuf.c_cc[VMIN] = FIBUFSIZ > 64 ? 64 : FIBUFSIZ;
99 ttbuf.c_cc[VTIME] = 5;
87d365c2
RA
100 if (ioctl(Ifn, TCSETAF, &ttbuf) < 0) {
101 syslog(LOG_ERR, "ioctl(TCSETAF) failed: %m");
102 cleanup(FAIL);
103 }
0aec7641 104 ttbuf.c_iflag = ttbuf_flags;
9d469936 105#else !USG
0aec7641
RA
106 ttbuf_flags = ttbuf.sg_flags;
107 ttbuf.sg_flags = ANYP|CBREAK;
87d365c2
RA
108 if (ioctl(Ifn, TCSETAF, &ttbuf) < 0) {
109 syslog(LOG_ERR, "ioctl(TCSETAF) failed: %m");
110 cleanup(FAIL);
111 }
0aec7641
RA
112 /* this is two seperate ioctls to set the x.29 params */
113 ttbuf.sg_flags |= TANDEM;
87d365c2
RA
114 if (ioctl(Ifn, TCSETAF, &ttbuf) < 0) {
115 syslog(LOG_ERR, "ioctl(TCSETAF) failed: %m");
116 cleanup(FAIL);
117 }
0aec7641
RA
118 ttbuf.sg_flags = ttbuf_flags;
119#endif USG
9d469936 120 }
31b8bf0a
JB
121 fsig = signal(SIGALRM, falarm);
122 /* give the other side time to perform its ioctl;
123 * otherwise it may flush out the first data this
124 * side is about to send.
125 */
126 sleep(2);
127 return SUCCESS;
128}
129
130fturnoff()
131{
0aec7641 132 if (!IsTcpIp)
87d365c2 133 ioctl(Ifn, TCSETAF, &ttbuf);
31b8bf0a 134 (void) signal(SIGALRM, fsig);
0aec7641 135 sleep(2);
31b8bf0a
JB
136 return SUCCESS;
137}
138
139fwrmsg(type, str, fn)
140register char *str;
141int fn;
142char type;
143{
144 register char *s;
145 char bufr[MAXMSGLEN];
146
147 s = bufr;
148 *s++ = type;
149 while (*str)
150 *s++ = *str++;
151 if (*(s-1) == '\n')
152 s--;
153 *s++ = '\r';
9d469936 154 *s = 0;
31b8bf0a
JB
155 (void) write(fn, bufr, s - bufr);
156 return SUCCESS;
157}
158
159frdmsg(str, fn)
160register char *str;
161register int fn;
162{
163 register char *smax;
164
165 if (setjmp(Ffailbuf))
166 return FAIL;
167 smax = str + MAXMSGLEN - 1;
168 (void) alarm(2*MAXMSGTIME);
169 for (;;) {
170 if (read(fn, str, 1) <= 0)
171 goto msgerr;
9d469936 172 *str &= 0177;
31b8bf0a
JB
173 if (*str == '\r')
174 break;
9d469936 175 if (*str < ' ') {
31b8bf0a 176 continue;
9d469936 177 }
31b8bf0a
JB
178 if (str++ >= smax)
179 goto msgerr;
180 }
181 *str = '\0';
182 (void) alarm(0);
183 return SUCCESS;
184msgerr:
185 (void) alarm(0);
186 return FAIL;
187}
188
189fwrdata(fp1, fn)
190FILE *fp1;
191int fn;
192{
9d469936 193 register int alen, ret;
9d469936
JB
194 char ack, ibuf[MAXMSGLEN];
195 int flen, mil, retries = 0;
31b8bf0a
JB
196 long abytes, fbytes;
197 struct timeb t1, t2;
0aec7641 198 float ft;
31b8bf0a
JB
199
200 ret = FAIL;
201retry:
202 fchksum = 0xffff;
203 abytes = fbytes = 0L;
204 ack = '\0';
205#ifdef USG
206 time(&t1.time);
207 t1.millitm = 0;
208#else !USG
209 ftime(&t1);
210#endif !USG
9d469936
JB
211 do {
212 alen = fwrblk(fn, fp1, &flen);
31b8bf0a 213 fbytes += flen;
9d469936
JB
214 if (alen <= 0) {
215 abytes -= alen;
216 goto acct;
31b8bf0a 217 }
9d469936
JB
218 abytes += alen;
219 } while (!feof(fp1) && !ferror(fp1));
220 DEBUG(8, "\nchecksum: %04x\n", fchksum);
221 if (frdmsg(ibuf, fn) != FAIL) {
222 if ((ack = ibuf[0]) == 'G')
223 ret = SUCCESS;
224 DEBUG(4, "ack - '%c'\n", ack);
31b8bf0a
JB
225 }
226acct:
31b8bf0a
JB
227#ifdef USG
228 time(&t2.time);
229 t2.millitm = 0;
230#else !USG
231 ftime(&t2);
232#endif !USG
233 Now = t2;
234 t2.time -= t1.time;
235 mil = t2.millitm - t1.millitm;
236 if (mil < 0) {
237 --t2.time;
238 mil += 1000;
239 }
0aec7641
RA
240 ft = (float)t2.time + (float)mil/1000.;
241 sprintf(ibuf, "sent data %ld bytes %.2f secs %ld bps",
242 fbytes, ft, (long)((float)fbytes*8./ft));
28ed17d4 243 sysacct(abytes, t2.time);
0aec7641 244 Bytes_Sent += fbytes;
9d469936
JB
245 if (retries > 0)
246 sprintf(&ibuf[strlen(ibuf)], ", %d retries", retries);
31b8bf0a 247 DEBUG(1, "%s\n", ibuf);
87d365c2 248 log_xferstats(ibuf);
9d469936
JB
249 if (ack == 'R') {
250 DEBUG(4, "RETRY:\n", 0);
251 fseek(fp1, 0L, 0);
252 retries++;
253 goto retry;
254 }
31b8bf0a 255#ifdef SYSACCT
9d469936 256 if (ret == FAIL)
31b8bf0a
JB
257 sysaccf(NULL); /* force accounting */
258#endif SYSACCT
259 return ret;
260}
261
262/* max. attempts to retransmit a file: */
263#define MAXRETRIES (fbytes < 10000L ? 2 : 1)
264
265frddata(fn, fp2)
266register int fn;
267register FILE *fp2;
268{
269 register int flen;
270 register char eof;
9d469936
JB
271 char ibuf[FIBUFSIZ];
272 int ret, mil, retries = 0;
31b8bf0a
JB
273 long alen, abytes, fbytes;
274 struct timeb t1, t2;
0aec7641 275 float ft;
31b8bf0a
JB
276
277 ret = FAIL;
278retry:
279 fchksum = 0xffff;
280 abytes = fbytes = 0L;
281#ifdef USG
282 time(&t1.time);
283 t1.millitm = 0;
284#else !USG
285 ftime(&t1);
286#endif !USG
287 do {
288 flen = frdblk(ibuf, fn, &alen);
289 abytes += alen;
290 if (flen < 0)
291 goto acct;
9d469936
JB
292 if (eof = flen > FIBUFSIZ)
293 flen -= FIBUFSIZ + 1;
31b8bf0a
JB
294 fbytes += flen;
295 if (fwrite(ibuf, sizeof (char), flen, fp2) != flen)
296 goto acct;
297 } while (!eof);
9d469936 298 ret = SUCCESS;
31b8bf0a 299acct:
31b8bf0a
JB
300#ifdef USG
301 time(&t2.time);
302 t2.millitm = 0;
303#else !USG
304 ftime(&t2);
305#endif !USG
306 Now = t2;
307 t2.time -= t1.time;
308 mil = t2.millitm - t1.millitm;
309 if (mil < 0) {
310 --t2.time;
311 mil += 1000;
312 }
0aec7641
RA
313 ft = (float)t2.time + (float)mil/1000.;
314 sprintf(ibuf, "received data %ld bytes %.2f secs %ld bps",
315 fbytes, ft, (long)((float)fbytes*8./ft));
31b8bf0a 316 if (retries > 0)
9d469936 317 sprintf(&ibuf[strlen(ibuf)]," %d retries", retries);
28ed17d4 318 sysacct(abytes, t2.time);
0aec7641 319 Bytes_Received += fbytes;
31b8bf0a 320 DEBUG(1, "%s\n", ibuf);
87d365c2 321 log_xferstats(ibuf);
9d469936
JB
322 if (ret == FAIL) {
323 if (retries++ < MAXRETRIES) {
324 DEBUG(8, "send ack: 'R'\n", 0);
325 fwrmsg('R', "", fn);
326 fseek(fp2, 0L, 0);
327 DEBUG(4, "RETRY:\n", 0);
328 goto retry;
329 }
330 DEBUG(8, "send ack: 'Q'\n", 0);
331 fwrmsg('Q', "", fn);
332#ifdef SYSACCT
333 sysaccf(NULL); /* force accounting */
334#endif SYSACCT
335 }
336 else {
337 DEBUG(8, "send ack: 'G'\n", 0);
338 fwrmsg('G', "", fn);
339 }
31b8bf0a
JB
340 return ret;
341}
342
343static
344frdbuf(blk, len, fn)
345register char *blk;
346register int len;
347register int fn;
348{
9d469936 349 static int ret = FIBUFSIZ / 2;
31b8bf0a
JB
350
351 if (setjmp(Ffailbuf))
352 return FAIL;
31b8bf0a
JB
353 (void) alarm(MAXMSGTIME);
354 ret = read(fn, blk, len);
355 alarm(0);
356 return ret <= 0 ? FAIL : ret;
357}
358
9d469936 359#if !defined(BSD4_2) && !defined(USG)
31b8bf0a 360/* call ultouch every TC calls to either frdblk or fwrblk */
9d469936
JB
361#define TC 20
362static int tc = TC;
363#endif !defined(BSD4_2) && !defined(USG)
31b8bf0a
JB
364
365/* Byte conversion:
366 *
9d469936
JB
367 * from pre to
368 * 000-037 172 100-137
369 * 040-171 040-171
370 * 172-177 173 072-077
371 * 200-237 174 100-137
372 * 240-371 175 040-171
373 * 372-377 176 072-077
31b8bf0a
JB
374 */
375
376static
9d469936 377fwrblk(fn, fp, lenp)
31b8bf0a 378int fn;
9d469936
JB
379register FILE *fp;
380int *lenp;
31b8bf0a
JB
381{
382 register char *op;
9d469936
JB
383 register int c, sum, nl, len;
384 char obuf[FOBUFSIZ + 8];
31b8bf0a 385 int ret;
31b8bf0a 386
9d469936 387#if !defined(BSD4_2) && !defined(USG)
31b8bf0a
JB
388 /* call ultouch occasionally */
389 if (--tc < 0) {
390 tc = TC;
391 ultouch();
392 }
9d469936 393#endif !defined(BSD4_2) && !defined(USG)
31b8bf0a
JB
394 op = obuf;
395 nl = 0;
9d469936 396 len = 0;
31b8bf0a 397 sum = fchksum;
9d469936
JB
398 while ((c = getc(fp)) != EOF) {
399 len++;
31b8bf0a
JB
400 if (sum & 0x8000) {
401 sum <<= 1;
402 sum++;
403 } else
404 sum <<= 1;
9d469936 405 sum += c;
31b8bf0a 406 sum &= 0xffff;
9d469936
JB
407 if (c & 0200) {
408 c &= 0177;
409 if (c < 040) {
31b8bf0a 410 *op++ = '\174';
9d469936 411 *op++ = c + 0100;
31b8bf0a 412 } else
9d469936 413 if (c <= 0171) {
31b8bf0a 414 *op++ = '\175';
9d469936 415 *op++ = c;
31b8bf0a
JB
416 }
417 else {
418 *op++ = '\176';
9d469936 419 *op++ = c - 0100;
31b8bf0a
JB
420 }
421 nl += 2;
422 } else {
9d469936 423 if (c < 040) {
31b8bf0a 424 *op++ = '\172';
9d469936 425 *op++ = c + 0100;
31b8bf0a
JB
426 nl += 2;
427 } else
9d469936
JB
428 if (c <= 0171) {
429 *op++ = c;
31b8bf0a
JB
430 nl++;
431 } else {
432 *op++ = '\173';
9d469936 433 *op++ = c - 0100;
31b8bf0a
JB
434 nl += 2;
435 }
436 }
9d469936
JB
437 if (nl >= FOBUFSIZ - 1) {
438 /*
439 * peek at next char, see if it will fit
440 */
441 c = getc(fp);
442 if (c == EOF)
443 break;
444 (void) ungetc(c, fp);
445 if (nl >= FOBUFSIZ || c < 040 || c > 0171)
446 goto writeit;
447 }
448 }
449 /*
450 * At EOF - append checksum, there is space for it...
451 */
452 sprintf(op, "\176\176%04x\r", sum);
453 nl += strlen(op);
454writeit:
455 *lenp = len;
31b8bf0a 456 fchksum = sum;
9d469936 457 DEBUG(8, "%d/", len);
31b8bf0a
JB
458 DEBUG(8, "%d,", nl);
459 ret = write(fn, obuf, nl);
460 return ret == nl ? nl : ret < 0 ? 0 : -ret;
461}
462
463static
464frdblk(ip, fn, rlen)
465register char *ip;
466int fn;
467long *rlen;
468{
469 register char *op, c;
470 register int sum, len, nl;
471 char buf[5], *erbp = ip;
472 int i;
473 static char special = 0;
474
9d469936 475#if !defined(BSD4_2) && !defined(USG)
31b8bf0a
JB
476 /* call ultouch occasionally */
477 if (--tc < 0) {
478 tc = TC;
479 ultouch();
480 }
9d469936
JB
481#endif !defined(BSD4_2) && !defined(USG)
482 if ((len = frdbuf(ip, FIBUFSIZ, fn)) == FAIL) {
31b8bf0a
JB
483 *rlen = 0;
484 goto dcorr;
485 }
486 *rlen = len;
487 DEBUG(8, "%d/", len);
488 op = ip;
489 nl = 0;
490 sum = fchksum;
491 do {
492 if ((*ip &= 0177) >= '\172') {
493 if (special) {
494 DEBUG(8, "%d", nl);
495 special = 0;
496 op = buf;
497 if (*ip++ != '\176' || (i = --len) > 5)
498 goto dcorr;
499 while (i--)
9d469936 500 *op++ = *ip++ & 0177;
31b8bf0a
JB
501 while (len < 5) {
502 i = frdbuf(&buf[len], 5 - len, fn);
503 if (i == FAIL) {
504 len = FAIL;
505 goto dcorr;
506 }
507 DEBUG(8, ",%d", i);
508 len += i;
509 *rlen += i;
9d469936
JB
510 while (i--)
511 *op++ &= 0177;
31b8bf0a
JB
512 }
513 if (buf[4] != '\r')
514 goto dcorr;
515 sscanf(buf, "%4x", &fchksum);
516 DEBUG(8, "\nchecksum: %04x\n", sum);
517 if (fchksum == sum)
9d469936 518 return FIBUFSIZ + 1 + nl;
31b8bf0a
JB
519 else {
520 DEBUG(8, "\n", 0);
521 DEBUG(4, "Bad checksum\n", 0);
522 return FAIL;
523 }
524 }
525 special = *ip++;
526 } else {
527 if (*ip < '\040') {
528 /* error: shouldn't get control chars */
529 goto dcorr;
530 }
531 switch (special) {
532 case 0:
533 c = *ip++;
534 break;
535 case '\172':
536 c = *ip++ - 0100;
537 break;
538 case '\173':
539 c = *ip++ + 0100;
540 break;
541 case '\174':
542 c = *ip++ + 0100;
543 break;
544 case '\175':
545 c = *ip++ + 0200;
546 break;
547 case '\176':
548 c = *ip++ + 0300;
549 break;
550 }
551 *op++ = c;
552 if (sum & 0x8000) {
553 sum <<= 1;
554 sum++;
555 } else
556 sum <<= 1;
557 sum += c & 0377;
558 sum &= 0xffff;
559 special = 0;
560 nl++;
561 }
562 } while (--len);
563 fchksum = sum;
564 DEBUG(8, "%d,", nl);
565 return nl;
566dcorr:
567 DEBUG(8, "\n", 0);
568 DEBUG(4, "Data corrupted\n", 0);
569 while (len != FAIL) {
9d469936 570 if ((len = frdbuf(erbp, FIBUFSIZ, fn)) != FAIL)
31b8bf0a
JB
571 *rlen += len;
572 }
573 return FAIL;
574}
9d469936 575