xref: /illumos-gate/usr/src/cmd/bnu/fio.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1995 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  *
31  * flow control protocol.
32  *
33  * This protocol relies on flow control of the data stream.
34  * It is meant for working over links that can (almost) be
35  * guaranteed to be errorfree, specifically X.25/PAD links.
36  * A sumcheck is carried out over a whole file only. If a
37  * transport fails the receiver can request retransmission(s).
38  * This protocol uses a 7-bit datapath only, so it can be
39  * used on links that are not 8-bit transparent.
40  *
41  * When using this protocol with an X.25 PAD:
42  * Although this protocol uses no control chars except CR,
43  * control chars NULL and ^P are used before this protocol
44  * is started; since ^P is the default char for accessing
45  * PAD X.28 command mode, be sure to disable that access
46  * (PAD par 1). Also make sure both flow control pars
47  * (5 and 12) are set. The CR used in this proto is meant
48  * to trigger packet transmission, hence par 3 should be
49  * set to 2; a good value for the Idle Timer (par 4) is 10.
50  * All other pars should be set to 0.
51  *
52  * Normally a calling site will take care of setting the
53  * local PAD pars via an X.28 command and those of the remote
54  * PAD via an X.29 command, unless the remote site has a
55  * special channel assigned for this protocol with the proper
56  * par settings.
57  *
58  * Additional comments for hosts with direct X.25 access:
59  * - the global variable IsTcpIp, when set, excludes the ioctl's,
60  *   so the same binary can run on X.25 and non-X.25 hosts;
61  * - reads are done in small chunks, which can be smaller than
62  *   the packet size; your X.25 driver must support that.
63  *
64  *
65  * Author:
66  *	Piet Beertema, CWI, Amsterdam, Sep 1984
67  * Modified for X.25 hosts:
68  *	Robert Elz, Melbourne Univ, Mar 1985
69  */
70 
71 #include "uucp.h"
72 #ifdef F_PROTOCOL
73 
74 extern unsigned msgtime;
75 
76 /* privates */
77 static int frdblk(), fwrblk();
78 
79 #define FIBUFSIZ	4096	/* for X.25 interfaces: set equal to packet size,
80 				 * but see comment above
81 				 */
82 
83 #define FOBUFSIZ	4096	/* for X.25 interfaces: set equal to packet size;
84 				 * otherwise make as large as feasible to reduce
85 				 * number of write system calls
86 				 */
87 
88 #ifndef MAXMSGLEN
89 #define MAXMSGLEN	BUFSIZ
90 #endif MAXMSGLEN
91 
92 static int fchksum;
93 static jmp_buf Ffailbuf;
94 
95 /* ARGSUSED */
96 static void
97 falarm(sig)
98 	int sig;
99 {
100 	signal(SIGALRM, falarm);
101 	longjmp(Ffailbuf, 1);
102 }
103 
104 static void (*fsig)();
105 
106 static int ioctlok;
107 #ifdef ATTSVTTY
108 static struct termio ttbuf;
109 #else
110 static struct sgttyb ttbuf;
111 #endif
112 
113 fturnon()
114 {
115 	int ret;
116 #ifdef ATTSVTTY
117 	struct termio save_ttbuf;
118 #else
119 	struct sgttyb save_ttbuf;
120 #endif
121 
122 #ifdef ATTSVTTY
123 	if (ioctl(Ifn, TCGETA, &ttbuf) >= 0) {
124 		ioctlok = 1;
125 		save_ttbuf = ttbuf;
126 		ioctl(Ifn, TCGETA, &ttbuf);
127 		ttbuf.c_iflag = IXOFF|IXON|ISTRIP;
128 		ttbuf.c_cc[VMIN] = FIBUFSIZ > 64 ? 64 : FIBUFSIZ;
129 		ttbuf.c_cc[VTIME] = 5;
130 		ret = ioctl(Ifn, TCSETA, &ttbuf);
131 		ASSERT(ret >= 0, "STTY FAILED", "", ret);
132 		ttbuf = save_ttbuf;
133 	}
134 #else /* !ATTSVTTY */
135 	if (ioctl(Ifn, TIOCGETP, &ttbuf) >= 0) {
136 		ioctlok = 1;
137 		save_ttbuf = ttbuf;
138 		ttbuf.sg_flags = ANYP|CBREAK|TANDEM;
139 		ret = ioctl(Ifn, TIOCSETP, &ttbuf);
140 		ASSERT(ret >= 0, "STTY FAILED", "", ret);
141 		ttbuf = save_ttbuf;
142 	}
143 #endif /* ATTSVTTY */
144 	fsig = signal(SIGALRM, falarm);
145 	/* give the other side time to perform its ioctl;
146 	 * otherwise it may flush out the first data this
147 	 * side is about to send.
148 	 */
149 	sleep(2);
150 	return SUCCESS;
151 }
152 
153 fturnoff()
154 {
155 	if (ioctlok) {
156 #ifdef ATTSVTTY
157 		(void) ioctl(Ifn, TCSETA, &ttbuf);
158 #else
159 		(void) ioctl(Ifn, TIOCSETP, &ttbuf);
160 #endif
161 	}
162 	(void) signal(SIGALRM, fsig);
163 	sleep(2);
164 	return SUCCESS;
165 }
166 
167 fwrmsg(type, str, fn)
168 register char *str;
169 int fn;
170 char type;
171 {
172 	register char *s;
173 	char bufr[MAXMSGLEN];
174 
175 	s = bufr;
176 	*s++ = type;
177 	while (*str)
178 		*s++ = *str++;
179 	if (*(s-1) == '\n')
180 		s--;
181 	*s++ = '\r';
182 	*s = 0;
183 	(void) write(fn, bufr, s - bufr);
184 	return SUCCESS;
185 }
186 
187 frdmsg(str, fn)
188 register char *str;
189 register int fn;
190 {
191 	register char *smax;
192 
193 	if (setjmp(Ffailbuf))
194 		return FAIL;
195 	smax = str + MAXMSGLEN - 1;
196 	(void) alarm(msgtime);
197 	for (;;) {
198 		if (read(fn, str, 1) <= 0)
199 			goto msgerr;
200 		*str &= 0177;
201 		if (*str == '\r')
202 			break;
203 		if (*str < ' ') {
204 			continue;
205 		}
206 		if (str++ >= smax)
207 			goto msgerr;
208 	}
209 	*str = '\0';
210 	(void) alarm(0);
211 	return SUCCESS;
212 msgerr:
213 	(void) alarm(0);
214 	return FAIL;
215 }
216 
217 fwrdata(fp1, fn)
218 FILE *fp1;
219 int fn;
220 {
221 	register int alen, ret;
222 	char ack, ibuf[MAXMSGLEN];
223 	int flen, retries = 0;
224 	long fbytes;
225 
226 	ret = FAIL;
227 retry:
228 	fchksum = 0xffff;
229 	fbytes = 0L;
230 	ack = '\0';
231 	do {
232 		alen = fwrblk(fn, fp1, &flen);
233 		fbytes += flen;
234 		if (alen <= 0) {
235 			goto acct;
236 		}
237 	} while (!feof(fp1) && !ferror(fp1));
238 	DEBUG(8, "\nchecksum: %04x\n", fchksum);
239 	if (frdmsg(ibuf, fn) != FAIL) {
240 		if ((ack = ibuf[0]) == 'G')
241 			ret = SUCCESS;
242 		DEBUG(4, "ack - '%c'\n", ack);
243 	}
244 acct:
245 	DEBUG(7, "%d retries\n", retries);
246 	if (ack == 'R') {
247 		DEBUG(4, "RETRY:\n", 0);
248 		fseek(fp1, 0L, 0);
249 		retries++;
250 		goto retry;
251 	}
252 	return ret;
253 }
254 
255 /* max. attempts to retransmit a file: */
256 #define MAXRETRIES	(fbytes < 10000L ? 2 : 1)
257 
258 frddata(fn, fp2)
259 register int fn;
260 register FILE *fp2;
261 {
262 	register int flen;
263 	register char eof;
264 	char ibuf[FIBUFSIZ];
265 	int ret, retries = 0;
266 	long alen, fbytes;
267 
268 	ret = FAIL;
269 retry:
270 	fchksum = 0xffff;
271 	fbytes = 0L;
272 	do {
273 		flen = frdblk(ibuf, fn, &alen);
274 		if (flen < 0)
275 			goto acct;
276 		if (eof = flen > FIBUFSIZ)
277 			flen -= FIBUFSIZ + 1;
278 		fbytes += flen;
279 		if (fwrite(ibuf, sizeof (char), flen, fp2) != flen)
280 			goto acct;
281 	} while (!eof);
282 	ret = SUCCESS;
283 acct:
284 	DEBUG(7, "%d retries\n", retries);
285 	if (ret == FAIL) {
286 		if (retries++ < MAXRETRIES) {
287 			DEBUG(8, "send ack: 'R'\n", 0);
288 			fwrmsg('R', "", fn);
289 			fseek(fp2, 0L, 0);
290 			DEBUG(4, "RETRY:\n", 0);
291 			goto retry;
292 		}
293 		DEBUG(8, "send ack: 'Q'\n", 0);
294 		fwrmsg('Q', "", fn);
295 	}
296 	else {
297 		DEBUG(8, "send ack: 'G'\n", 0);
298 		fwrmsg('G', "", fn);
299 	}
300 	return ret;
301 }
302 
303 static
304 frdbuf(blk, len, fn)
305 register char *blk;
306 register int len;
307 register int fn;
308 {
309 	static int ret = FIBUFSIZ / 2;
310 
311 	if (setjmp(Ffailbuf))
312 		return FAIL;
313 	(void) alarm(msgtime);
314 	ret = read(fn, blk, len);
315 	alarm(0);
316 	return ret <= 0 ? FAIL : ret;
317 }
318 
319 #if !defined(ATTSVKILL)
320 /* call ultouch every TC calls to either frdblk or fwrblk  */
321 #define TC	20
322 static int tc = TC;
323 #endif !defined(ATTSVKILL)
324 
325 /* Byte conversion:
326  *
327  *   from	 pre	   to
328  * 000-037	 172	 100-137
329  * 040-171		 040-171
330  * 172-177	 173	 072-077
331  * 200-237	 174	 100-137
332  * 240-371	 175	 040-171
333  * 372-377	 176	 072-077
334  */
335 
336 static
337 fwrblk(fn, fp, lenp)
338 int fn;
339 register FILE *fp;
340 int *lenp;
341 {
342 	register char *op;
343 	register int c, sum, nl, len;
344 	char obuf[FOBUFSIZ + 8];
345 	int ret;
346 
347 #if !defined(ATTSVKILL)
348 	/* call ultouch occasionally */
349 	if (--tc < 0) {
350 		tc = TC;
351 		ultouch();
352 	}
353 #endif /*!defined(ATTSVKILL)*/
354 	op = obuf;
355 	nl = 0;
356 	len = 0;
357 	sum = fchksum;
358 	while ((c = getc(fp)) != EOF) {
359 		len++;
360 		if (sum & 0x8000) {
361 			sum <<= 1;
362 			sum++;
363 		} else
364 			sum <<= 1;
365 		sum += c;
366 		sum &= 0xffff;
367 		if (c & 0200) {
368 			c &= 0177;
369 			if (c < 040) {
370 				*op++ = '\174';
371 				*op++ = c + 0100;
372 			} else
373 			if (c <= 0171) {
374 				*op++ = '\175';
375 				*op++ = c;
376 			}
377 			else {
378 				*op++ = '\176';
379 				*op++ = c - 0100;
380 			}
381 			nl += 2;
382 		} else {
383 			if (c < 040) {
384 				*op++ = '\172';
385 				*op++ = c + 0100;
386 				nl += 2;
387 			} else
388 			if (c <= 0171) {
389 				*op++ = c;
390 				nl++;
391 			} else {
392 				*op++ = '\173';
393 				*op++ = c - 0100;
394 				nl += 2;
395 			}
396 		}
397 		if (nl >= FOBUFSIZ - 1) {
398 			/*
399 			 * peek at next char, see if it will fit
400 			 */
401 			c = getc(fp);
402 			if (c == EOF)
403 				break;
404 			(void) ungetc(c, fp);
405 			if (nl >= FOBUFSIZ || c < 040 || c > 0171)
406 				goto writeit;
407 		}
408 	}
409 	/*
410 	 * At EOF - append checksum, there is space for it...
411 	 */
412 	sprintf(op, "\176\176%04x\r", sum);
413 	nl += strlen(op);
414 writeit:
415 	*lenp = len;
416 	fchksum = sum;
417 	DEBUG(8, "%d/", len);
418 	DEBUG(8, "%d,", nl);
419 	ret = write(fn, obuf, nl);
420 	return ret == nl ? nl : ret < 0 ? 0 : -ret;
421 }
422 
423 static
424 frdblk(ip, fn, rlen)
425 register char *ip;
426 int fn;
427 long *rlen;
428 {
429 	register char *op, c;
430 	register int sum, len, nl;
431 	char buf[5], *erbp = ip;
432 	int i;
433 	static char special = 0;
434 
435 #if !defined(ATTSVKILL)
436 	/* call ultouch occasionally */
437 	if (--tc < 0) {
438 		tc = TC;
439 		ultouch();
440 	}
441 #endif /*!defined(ATTSVKILL)*/
442 	if ((len = frdbuf(ip, FIBUFSIZ, fn)) == FAIL) {
443 		*rlen = 0;
444 		goto dcorr;
445 	}
446 	*rlen = len;
447 	DEBUG(8, "%d/", len);
448 	op = ip;
449 	nl = 0;
450 	sum = fchksum;
451 	do {
452 		if ((*ip &= 0177) >= '\172') {
453 			if (special) {
454 				DEBUG(8, "%d", nl);
455 				special = 0;
456 				op = buf;
457 				if (*ip++ != '\176' || (i = --len) > 5)
458 					goto dcorr;
459 				while (i--)
460 					*op++ = *ip++ & 0177;
461 				while (len < 5) {
462 					i = frdbuf(&buf[len], 5 - len, fn);
463 					if (i == FAIL) {
464 						len = FAIL;
465 						goto dcorr;
466 					}
467 					DEBUG(8, ",%d", i);
468 					len += i;
469 					*rlen += i;
470 					while (i--)
471 						*op++ &= 0177;
472 				}
473 				if (buf[4] != '\r')
474 					goto dcorr;
475 				sscanf(buf, "%4x", &fchksum);
476 				DEBUG(8, "\nchecksum: %04x\n", sum);
477 				if (fchksum == sum)
478 					return FIBUFSIZ + 1 + nl;
479 				else {
480 					DEBUG(8, "\n", 0);
481 					DEBUG(4, "Bad checksum\n", 0);
482 					return FAIL;
483 				}
484 			}
485 			special = *ip++;
486 		} else {
487 			if (*ip < '\040') {
488 				/* error: shouldn't get control chars */
489 				goto dcorr;
490 			}
491 			switch (special) {
492 			case 0:
493 				c = *ip++;
494 				break;
495 			case '\172':
496 				c = *ip++ - 0100;
497 				break;
498 			case '\173':
499 				c = *ip++ + 0100;
500 				break;
501 			case '\174':
502 				c = *ip++ + 0100;
503 				break;
504 			case '\175':
505 				c = *ip++ + 0200;
506 				break;
507 			case '\176':
508 				c = *ip++ + 0300;
509 				break;
510 			}
511 			*op++ = c;
512 			if (sum & 0x8000) {
513 				sum <<= 1;
514 				sum++;
515 			} else
516 				sum <<= 1;
517 			sum += c & 0377;
518 			sum &= 0xffff;
519 			special = 0;
520 			nl++;
521 		}
522 	} while (--len);
523 	fchksum = sum;
524 	DEBUG(8, "%d,", nl);
525 	return nl;
526 dcorr:
527 	DEBUG(8, "\n", 0);
528 	DEBUG(4, "Data corrupted\n", 0);
529 	while (len != FAIL) {
530 		if ((len = frdbuf(erbp, FIBUFSIZ, fn)) != FAIL)
531 			*rlen += len;
532 	}
533 	return FAIL;
534 }
535 #endif /* F_PROTOCOL */
536