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