xref: /illumos-gate/usr/src/cmd/bnu/fio.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 #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 int
114 fturnon(void)
115 {
116 	int ret;
117 #ifdef ATTSVTTY
118 	struct termio save_ttbuf;
119 #else
120 	struct sgttyb save_ttbuf;
121 #endif
122 
123 #ifdef ATTSVTTY
124 	if (ioctl(Ifn, TCGETA, &ttbuf) >= 0) {
125 		ioctlok = 1;
126 		save_ttbuf = ttbuf;
127 		ioctl(Ifn, TCGETA, &ttbuf);
128 		ttbuf.c_iflag = IXOFF|IXON|ISTRIP;
129 		ttbuf.c_cc[VMIN] = FIBUFSIZ > 64 ? 64 : FIBUFSIZ;
130 		ttbuf.c_cc[VTIME] = 5;
131 		ret = ioctl(Ifn, TCSETA, &ttbuf);
132 		ASSERT(ret >= 0, "STTY FAILED", "", ret);
133 		ttbuf = save_ttbuf;
134 	}
135 #else /* !ATTSVTTY */
136 	if (ioctl(Ifn, TIOCGETP, &ttbuf) >= 0) {
137 		ioctlok = 1;
138 		save_ttbuf = ttbuf;
139 		ttbuf.sg_flags = ANYP|CBREAK|TANDEM;
140 		ret = ioctl(Ifn, TIOCSETP, &ttbuf);
141 		ASSERT(ret >= 0, "STTY FAILED", "", ret);
142 		ttbuf = save_ttbuf;
143 	}
144 #endif /* ATTSVTTY */
145 	fsig = signal(SIGALRM, falarm);
146 	/* give the other side time to perform its ioctl;
147 	 * otherwise it may flush out the first data this
148 	 * side is about to send.
149 	 */
150 	sleep(2);
151 	return SUCCESS;
152 }
153 
154 int
155 fturnoff(void)
156 {
157 	if (ioctlok) {
158 #ifdef ATTSVTTY
159 		(void) ioctl(Ifn, TCSETA, &ttbuf);
160 #else
161 		(void) ioctl(Ifn, TIOCSETP, &ttbuf);
162 #endif
163 	}
164 	(void) signal(SIGALRM, fsig);
165 	sleep(2);
166 	return SUCCESS;
167 }
168 
169 int
170 fwrmsg(type, str, fn)
171 char *str;
172 int fn;
173 char type;
174 {
175 	char *s;
176 	char bufr[MAXMSGLEN];
177 
178 	s = bufr;
179 	*s++ = type;
180 	while (*str)
181 		*s++ = *str++;
182 	if (*(s-1) == '\n')
183 		s--;
184 	*s++ = '\r';
185 	*s = 0;
186 	(void) write(fn, bufr, s - bufr);
187 	return SUCCESS;
188 }
189 
190 int
191 frdmsg(str, fn)
192 char *str;
193 int fn;
194 {
195 	char *smax;
196 
197 	if (setjmp(Ffailbuf))
198 		return FAIL;
199 	smax = str + MAXMSGLEN - 1;
200 	(void) alarm(msgtime);
201 	for (;;) {
202 		if (read(fn, str, 1) <= 0)
203 			goto msgerr;
204 		*str &= 0177;
205 		if (*str == '\r')
206 			break;
207 		if (*str < ' ') {
208 			continue;
209 		}
210 		if (str++ >= smax)
211 			goto msgerr;
212 	}
213 	*str = '\0';
214 	(void) alarm(0);
215 	return SUCCESS;
216 msgerr:
217 	(void) alarm(0);
218 	return FAIL;
219 }
220 
221 int
222 fwrdata(fp1, fn)
223 FILE *fp1;
224 int fn;
225 {
226 	int alen, ret;
227 	char ack, ibuf[MAXMSGLEN];
228 	int flen, retries = 0;
229 	long fbytes;
230 
231 	ret = FAIL;
232 retry:
233 	fchksum = 0xffff;
234 	fbytes = 0L;
235 	ack = '\0';
236 	do {
237 		alen = fwrblk(fn, fp1, &flen);
238 		fbytes += flen;
239 		if (alen <= 0) {
240 			goto acct;
241 		}
242 	} while (!feof(fp1) && !ferror(fp1));
243 	DEBUG(8, "\nchecksum: %04x\n", fchksum);
244 	if (frdmsg(ibuf, fn) != FAIL) {
245 		if ((ack = ibuf[0]) == 'G')
246 			ret = SUCCESS;
247 		DEBUG(4, "ack - '%c'\n", ack);
248 	}
249 acct:
250 	DEBUG(7, "%d retries\n", retries);
251 	if (ack == 'R') {
252 		DEBUG(4, "RETRY:\n", 0);
253 		fseek(fp1, 0L, 0);
254 		retries++;
255 		goto retry;
256 	}
257 	return ret;
258 }
259 
260 /* max. attempts to retransmit a file: */
261 #define MAXRETRIES	(fbytes < 10000L ? 2 : 1)
262 
263 int
264 frddata(fn, fp2)
265 int fn;
266 FILE *fp2;
267 {
268 	int flen;
269 	char eof;
270 	char ibuf[FIBUFSIZ];
271 	int ret, retries = 0;
272 	long alen, fbytes;
273 
274 	ret = FAIL;
275 retry:
276 	fchksum = 0xffff;
277 	fbytes = 0L;
278 	do {
279 		flen = frdblk(ibuf, fn, &alen);
280 		if (flen < 0)
281 			goto acct;
282 		if (eof = flen > FIBUFSIZ)
283 			flen -= FIBUFSIZ + 1;
284 		fbytes += flen;
285 		if (fwrite(ibuf, sizeof (char), flen, fp2) != flen)
286 			goto acct;
287 	} while (!eof);
288 	ret = SUCCESS;
289 acct:
290 	DEBUG(7, "%d retries\n", retries);
291 	if (ret == FAIL) {
292 		if (retries++ < MAXRETRIES) {
293 			DEBUG(8, "send ack: 'R'\n", 0);
294 			fwrmsg('R', "", fn);
295 			fseek(fp2, 0L, 0);
296 			DEBUG(4, "RETRY:\n", 0);
297 			goto retry;
298 		}
299 		DEBUG(8, "send ack: 'Q'\n", 0);
300 		fwrmsg('Q', "", fn);
301 	}
302 	else {
303 		DEBUG(8, "send ack: 'G'\n", 0);
304 		fwrmsg('G', "", fn);
305 	}
306 	return ret;
307 }
308 
309 static int
310 frdbuf(blk, len, fn)
311 char *blk;
312 int len;
313 int fn;
314 {
315 	static int ret = FIBUFSIZ / 2;
316 
317 	if (setjmp(Ffailbuf))
318 		return FAIL;
319 	(void) alarm(msgtime);
320 	ret = read(fn, blk, len);
321 	alarm(0);
322 	return ret <= 0 ? FAIL : ret;
323 }
324 
325 #if !defined(ATTSVKILL)
326 /* call ultouch every TC calls to either frdblk or fwrblk  */
327 #define TC	20
328 static int tc = TC;
329 #endif	/* !defined(ATTSVKILL) */
330 
331 /* Byte conversion:
332  *
333  *   from	 pre	   to
334  * 000-037	 172	 100-137
335  * 040-171		 040-171
336  * 172-177	 173	 072-077
337  * 200-237	 174	 100-137
338  * 240-371	 175	 040-171
339  * 372-377	 176	 072-077
340  */
341 
342 static int
343 fwrblk(fn, fp, lenp)
344 int fn;
345 FILE *fp;
346 int *lenp;
347 {
348 	char *op;
349 	int c, sum, nl, len;
350 	char obuf[FOBUFSIZ + 8];
351 	int ret;
352 
353 #if !defined(ATTSVKILL)
354 	/* call ultouch occasionally */
355 	if (--tc < 0) {
356 		tc = TC;
357 		ultouch();
358 	}
359 #endif /*!defined(ATTSVKILL)*/
360 	op = obuf;
361 	nl = 0;
362 	len = 0;
363 	sum = fchksum;
364 	while ((c = getc(fp)) != EOF) {
365 		len++;
366 		if (sum & 0x8000) {
367 			sum <<= 1;
368 			sum++;
369 		} else
370 			sum <<= 1;
371 		sum += c;
372 		sum &= 0xffff;
373 		if (c & 0200) {
374 			c &= 0177;
375 			if (c < 040) {
376 				*op++ = '\174';
377 				*op++ = c + 0100;
378 			} else
379 			if (c <= 0171) {
380 				*op++ = '\175';
381 				*op++ = c;
382 			}
383 			else {
384 				*op++ = '\176';
385 				*op++ = c - 0100;
386 			}
387 			nl += 2;
388 		} else {
389 			if (c < 040) {
390 				*op++ = '\172';
391 				*op++ = c + 0100;
392 				nl += 2;
393 			} else
394 			if (c <= 0171) {
395 				*op++ = c;
396 				nl++;
397 			} else {
398 				*op++ = '\173';
399 				*op++ = c - 0100;
400 				nl += 2;
401 			}
402 		}
403 		if (nl >= FOBUFSIZ - 1) {
404 			/*
405 			 * peek at next char, see if it will fit
406 			 */
407 			c = getc(fp);
408 			if (c == EOF)
409 				break;
410 			(void) ungetc(c, fp);
411 			if (nl >= FOBUFSIZ || c < 040 || c > 0171)
412 				goto writeit;
413 		}
414 	}
415 	/*
416 	 * At EOF - append checksum, there is space for it...
417 	 */
418 	sprintf(op, "\176\176%04x\r", sum);
419 	nl += strlen(op);
420 writeit:
421 	*lenp = len;
422 	fchksum = sum;
423 	DEBUG(8, "%d/", len);
424 	DEBUG(8, "%d,", nl);
425 	ret = write(fn, obuf, nl);
426 	return ret == nl ? nl : ret < 0 ? 0 : -ret;
427 }
428 
429 static int
430 frdblk(ip, fn, rlen)
431 char *ip;
432 int fn;
433 long *rlen;
434 {
435 	char *op, c;
436 	int sum, len, nl;
437 	char buf[5], *erbp = ip;
438 	int i;
439 	static char special = 0;
440 
441 #if !defined(ATTSVKILL)
442 	/* call ultouch occasionally */
443 	if (--tc < 0) {
444 		tc = TC;
445 		ultouch();
446 	}
447 #endif /*!defined(ATTSVKILL)*/
448 	if ((len = frdbuf(ip, FIBUFSIZ, fn)) == FAIL) {
449 		*rlen = 0;
450 		goto dcorr;
451 	}
452 	*rlen = len;
453 	DEBUG(8, "%d/", len);
454 	op = ip;
455 	nl = 0;
456 	sum = fchksum;
457 	do {
458 		if ((*ip &= 0177) >= '\172') {
459 			if (special) {
460 				DEBUG(8, "%d", nl);
461 				special = 0;
462 				op = buf;
463 				if (*ip++ != '\176' || (i = --len) > 5)
464 					goto dcorr;
465 				while (i--)
466 					*op++ = *ip++ & 0177;
467 				while (len < 5) {
468 					i = frdbuf(&buf[len], 5 - len, fn);
469 					if (i == FAIL) {
470 						len = FAIL;
471 						goto dcorr;
472 					}
473 					DEBUG(8, ",%d", i);
474 					len += i;
475 					*rlen += i;
476 					while (i--)
477 						*op++ &= 0177;
478 				}
479 				if (buf[4] != '\r')
480 					goto dcorr;
481 				sscanf(buf, "%4x", &fchksum);
482 				DEBUG(8, "\nchecksum: %04x\n", sum);
483 				if (fchksum == sum)
484 					return FIBUFSIZ + 1 + nl;
485 				else {
486 					DEBUG(8, "\n", 0);
487 					DEBUG(4, "Bad checksum\n", 0);
488 					return FAIL;
489 				}
490 			}
491 			special = *ip++;
492 		} else {
493 			if (*ip < '\040') {
494 				/* error: shouldn't get control chars */
495 				goto dcorr;
496 			}
497 			switch (special) {
498 			case 0:
499 				c = *ip++;
500 				break;
501 			case '\172':
502 				c = *ip++ - 0100;
503 				break;
504 			case '\173':
505 				c = *ip++ + 0100;
506 				break;
507 			case '\174':
508 				c = *ip++ + 0100;
509 				break;
510 			case '\175':
511 				c = *ip++ + 0200;
512 				break;
513 			case '\176':
514 				c = *ip++ + 0300;
515 				break;
516 			}
517 			*op++ = c;
518 			if (sum & 0x8000) {
519 				sum <<= 1;
520 				sum++;
521 			} else
522 				sum <<= 1;
523 			sum += c & 0377;
524 			sum &= 0xffff;
525 			special = 0;
526 			nl++;
527 		}
528 	} while (--len);
529 	fchksum = sum;
530 	DEBUG(8, "%d,", nl);
531 	return nl;
532 dcorr:
533 	DEBUG(8, "\n", 0);
534 	DEBUG(4, "Data corrupted\n", 0);
535 	while (len != FAIL) {
536 		if ((len = frdbuf(erbp, FIBUFSIZ, fn)) != FAIL)
537 			*rlen += len;
538 	}
539 	return FAIL;
540 }
541 #endif /* F_PROTOCOL */
542