xref: /titanic_44/usr/src/lib/libnsl/dial/conn.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include "uucp.h"
34 #include <rpc/trace.h>
35 GLOBAL char _Protocol[40];	/* working protocol string */
36 static char _ProtoSys[40];	/* protocol string from Systems file entry */
37 static char _ProtoDev[40];	/* protocol string from Devices file entry */
38 EXTERN char _ProtoCfg[];	/* protocol string from Config  file entry */
39 
40 EXTERN jmp_buf Sjbuf;
41 EXTERN unsigned expecttime;
42 
43 GLOBAL int	Modemctrl;
44 
45 EXTERN void alarmtr();
46 static void addProto(), mergeProto(), removeProto();
47 static char *nextProto();
48 EXTERN char *findProto();
49 static void getProto();
50 static int finds();
51 EXTERN int getto();		/* make this static when ct uses altconn() */
52 EXTERN int chat(), rddev(), expect(), wrstr(), wrchr();
53 EXTERN int processdev(), getdevline(), getsysline(), sysaccess();
54 EXTERN int clear_hup();
55 EXTERN char *currsys(), *currdev();
56 static int finds();
57 static int wait_for_hangup(), expect_str();
58 
59 EXTERN void sendthem(), nap();
60 static int notin(), ifdate(), classmatch();
61 
62 GLOBAL char *Myline = CNULL;	/* to force which line will be used */
63 GLOBAL char *Mytype = CNULL;	/* to force selection of specific device type */
64 
65 /*
66  * conn - place a telephone call to system and login, etc.
67  *
68  * return codes:
69  *	FAIL - connection failed
70  *	>0  - file no.  -  connect ok
71  * When a failure occurs, Uerror is set.
72  */
73 
74 GLOBAL int
75 conn(system)
76 char *system;
77 {
78 	int nf, fn = FAIL;
79 	char *flds[F_MAX+1];
80 	EXTERN void sysreset();
81 
82 
83 	trace1(TR_conn, 0);
84 	CDEBUG(4, "conn(%s)\n", system);
85 	Uerror = 0;
86 	while ((nf = finds(system, flds, F_MAX)) > 0) {
87 		fn = getto(flds);
88 		CDEBUG(4, "getto ret %d\n", fn);
89 		if (fn < 0)
90 			continue;
91 		if (EQUALS(Progname, "uucico")) {
92 			if (chat(nf - F_LOGIN, flds + F_LOGIN, fn,"","") == SUCCESS) {
93 				sysreset();
94 				trace1(TR_conn, 1);
95 				return (fn); /* successful return */
96 			}
97 
98 			/* login failed */
99 			DEBUG(6, "close caller (%d)\n", fn);
100 			fd_rmlock(fn);
101 			close(fn);
102 			if (Dc[0] != NULLCHAR) {
103 			/*EMPTY*/
104 			DEBUG(6, "delock line (%s)\n", Dc);
105 			}
106 		} else {
107 			sysreset();
108 			trace1(TR_conn, 1);
109 			return (fn);
110 		}
111 	}
112 
113 	/* finds or getto failed */
114 	sysreset();
115 	CDEBUG(1, "Call Failed: %s\n", UERRORTEXT);
116 	trace1(TR_conn, 1);
117 	return (FAIL);
118 }
119 
120 /*
121  * getto - connect to remote machine
122  *
123  * return codes:
124  *	>0  -  file number - ok
125  *	FAIL  -  failed
126  */
127 
128 GLOBAL int
129 getto(flds)
130 char *flds[];
131 {
132 	char *dev[D_MAX+2], devbuf[BUFSIZ];
133 	register int status;
134 	register int dcf = -1;
135 	int reread = 0;
136 	int tries = 0;	/* count of call attempts - for limit purposes */
137 	EXTERN void devreset();
138 
139 
140 	trace1(TR_getto, 0);
141 	CDEBUG(1, "Device Type %s wanted\n", flds[F_TYPE]);
142 	Uerror = 0;
143 	while (tries < TRYCALLS) {
144 		if ((status=rddev(flds[F_TYPE], dev, devbuf, D_MAX)) == FAIL) {
145 			if (tries == 0 || ++reread >= TRYCALLS)
146 				break;
147 			devreset();
148 			continue;
149 		}
150 		/* check class, check (and possibly set) speed */
151 		if (classmatch(flds, dev) != SUCCESS) {
152 			DEBUG(7, "Skipping entry in '%s'", currdev());
153 			DEBUG(7, " - class (%s) not wanted.\n", dev[D_CLASS]);
154 			continue;
155 		}
156 		DEBUG(5, "Trying device entry from '%s'.\n", currdev());
157 		if ((dcf = processdev(flds, dev)) >= 0)
158 			break;
159 
160 		switch(Uerror) {
161 		case SS_CANT_ACCESS_DEVICE:
162 		case SS_DEVICE_FAILED:
163 		case SS_LOCKED_DEVICE:
164 			break;
165 		default:
166 			tries++;
167 			break;
168 		}
169 	}
170 	devreset();	/* reset devices file(s) */
171 	if (status == FAIL && !Uerror) {
172 		CDEBUG(1, "Requested Device Type Not Found\n%s", "");
173 		Uerror = SS_NO_DEVICE;
174 	}
175 	trace1(TR_getto, 1);
176 	return (dcf);
177 }
178 
179 /*
180  * classmatch - process 'Any' in Devices and Systems and
181  * 	determine the correct speed, or match for ==
182  */
183 
184 static int
185 classmatch(flds, dev)
186 char *flds[];
187 char *dev[];
188 {
189 	/* check class, check (and possibly set) speed */
190 	trace1(TR_classmatch, 0);
191 	if (EQUALS(flds[F_CLASS], "Any")
192 	   && EQUALS(dev[D_CLASS], "Any")) {
193 		dev[D_CLASS] = DEFAULT_BAUDRATE;
194 		trace1(TR_classmatch, 1);
195 		return (SUCCESS);
196 	} else if (EQUALS(dev[D_CLASS], "Any")) {
197 		dev[D_CLASS] = flds[F_CLASS];
198 		trace1(TR_classmatch, 1);
199 		return (SUCCESS);
200 	} else if (EQUALS(flds[F_CLASS], "Any") ||
201 	EQUALS(flds[F_CLASS], dev[D_CLASS])) {
202 		trace1(TR_classmatch, 1);
203 		return (SUCCESS);
204 	}
205 	else {
206 		trace1(TR_classmatch, 1);
207 		return (FAIL);
208 	}
209 }
210 
211 
212 /*
213  *	rddev - find and unpack a line from device file for this caller type
214  *	lines starting with whitespace of '#' are comments
215  *
216  *	return codes:
217  *		>0  -  number of arguments in vector - succeeded
218  *		FAIL - EOF
219  */
220 
221 GLOBAL int
222 rddev(char *type, char *dev[], char *buf, int devcount)
223 {
224 	char *commap, d_type[BUFSIZ];
225 	int na;
226 
227 
228 	trace1(TR_rddev, 0);
229 	while (getdevline(buf, BUFSIZ)) {
230 		if (buf[0] == ' ' || buf[0] == '\t'
231 			||  buf[0] == '\n' || buf[0] == '\0' || buf[0] == '#')
232 			continue;
233 		na = getargs(buf, dev, devcount);
234 		ASSERT(na >= D_CALLER, "BAD LINE", buf, na);
235 
236 		if (strncmp(dev[D_LINE],"/dev/",5) == 0) {
237 			/* since cu (altconn()) strips off leading */
238 			/* "/dev/",  do the same here.  */
239 			strcpy(dev[D_LINE], &(dev[D_LINE][5]));
240 		}
241 
242 		/* may have ",M" subfield in D_LINE */
243 		Modemctrl = FALSE;
244 		if ((commap = strchr(dev[D_LINE], ',')) != (char *)NULL) {
245 			if (strcmp(commap, ",M") == SAME)
246 				Modemctrl = TRUE;
247 			*commap = '\0';
248 		}
249 
250 		/*
251 		 * D_TYPE field may have protocol subfield, which
252 		 * must be pulled off before comparing to desired type.
253 		 */
254 		(void)strcpy(d_type, dev[D_TYPE]);
255 		if ((commap = strchr(d_type, ',')) != (char *)NULL)
256 			*commap = '\0';
257 
258 		/* to force the requested device type to be used. */
259 		if ((Mytype != NULL) && (!EQUALS(Mytype, d_type)))
260 			continue;
261 		/* to force the requested line to be used */
262 		if ((Myline != NULL) && (!EQUALS(Myline, dev[D_LINE])))
263 			continue;
264 
265 		bsfix(dev);	/* replace \X fields */
266 
267 		if (EQUALS(d_type, type)) {
268 			getProto(_ProtoDev, dev[D_TYPE]);
269 			trace1(TR_rddev, 1);
270 			return (na);
271 		}
272 	}
273 	trace1(TR_rddev, 1);
274 	return (FAIL);
275 }
276 
277 
278 /*
279  * finds	- set system attribute vector
280  *
281  * input:
282  *	fsys - open Systems file descriptor
283  *	sysnam - system name to find
284  * output:
285  *	flds - attibute vector from Systems file
286  *	fldcount - number of fields in flds
287  * return codes:
288  *	>0  -  number of arguments in vector - succeeded
289  *	FAIL - failed
290  * Uerror set:
291  *	0 - found a line in Systems file
292  *	SS_BADSYSTEM - no line found in Systems file
293  *	SS_TIME_WRONG - wrong time to call
294  */
295 
296 static int
297 finds(char *sysnam, char *flds[], int fldcount)
298 {
299 	static char *info;	/* dynamically allocated BUFSIZ */
300 	int na;
301 
302 	/* format of fields
303 	 *	0 name;
304 	 *	1 time
305 	 *	2 acu/hardwired
306 	 *	3 speed
307 	 *	etc
308 	 */
309 
310 	trace1(TR_finds, 0);
311 	if (sysnam == 0 || *sysnam == 0) {
312 		Uerror = SS_BADSYSTEM;
313 		trace1(TR_finds, 1);
314 		return (FAIL);
315 	}
316 
317 	if (info == NULL) {
318 		info = (char *)malloc(BUFSIZ);
319 		if (info == NULL) {
320 			DEBUG(1, "malloc failed for info in finds\n", 0);
321 			return (0);
322 		}
323 	}
324 	while (getsysline(info, BUFSIZ)) {
325 		na = getargs(info, flds, fldcount);
326 		bsfix(flds);	/* replace \X fields */
327 		if (!EQUALSN(sysnam, flds[F_NAME], MAXBASENAME))
328 			continue;
329 		/* check if requested Mytype device type */
330 		if ((Mytype != CNULL)
331 			&& (!EQUALSN(flds[F_TYPE], Mytype, strlen(Mytype)))) {
332 			DEBUG(7, "Skipping entry in '%s'", currsys());
333 			DEBUG(7, " - type (%s) not wanted.\n", flds[F_TYPE]);
334 			continue;
335 		} else {
336 			/*EMPTY*/
337 			DEBUG(5, "Trying entry from '%s'", currsys());
338 			DEBUG(5, " - device type %s.\n", flds[F_TYPE]);
339 		}
340 		/* OK if not uucico (ie. ct or cu) or the time is right */
341 		if (!EQUALS(Progname, "uucico") || ifdate(flds[F_TIME])) {
342 			/*  found a good entry  */
343 			getProto(_ProtoSys, flds[F_TYPE]);
344 			Uerror = 0;
345 			trace1(TR_finds, 1);
346 			return (na);	/* FOUND OK LINE */
347 		}
348 		CDEBUG(1, "Wrong Time To Call: %s\n", flds[F_TIME]);
349 		Uerror = SS_TIME_WRONG;
350 	}
351 	if (!Uerror)
352 		Uerror = SS_BADSYSTEM;
353 	trace1(TR_finds, 1);
354 	return (FAIL);
355 }
356 
357 /*
358  * getProto - get the protocol letters from the input string.
359  * input:
360  *	str - string from Systems/Devices/Config file,
361  *		a ',' delimits the protocol string
362  *		e.g. ACU,g or DK,d
363  * output:
364  *	str - the , (if present) will be replaced with NULLCHAR
365  *
366  * return:  none
367  */
368 
369 static void
370 getProto(save, str)
371 char *save;
372 char *str;
373 {
374 	register char *p;
375 
376 
377 	trace1(TR_getProto, 0);
378 	*save = NULLCHAR;
379 	if ((p=strchr(str, ',')) != NULL) {
380 		*p = NULLCHAR;
381 		(void) strcpy(save, p+1);
382 		DEBUG(7, "Protocol = %s\n", save);
383 	}
384 	trace1(TR_getProto, 1);
385 	return;
386 }
387 
388 /*
389  * check for a specified protocol selection string
390  * return:
391  *	protocol string pointer
392  *	NULL if none specified for LOGNAME
393  */
394 GLOBAL char *
395 protoString(valid)
396 char *valid;
397 {
398 	char *save;
399 
400 
401 	trace1(TR_protoString, 0);
402 	save =strdup(valid);
403 	_Protocol[0] = '\0';
404 
405 	if (_ProtoSys[0] != '\0')
406 		addProto(_ProtoSys, valid);
407 	if (_ProtoDev[0] != '\0')
408 		addProto(_ProtoDev, valid);
409 	if (_ProtoCfg[0] != '\0')
410 		addProto(_ProtoCfg, valid);
411 
412 	if (_Protocol[0] == '\0') {
413 		(void) strcpy(valid, save);
414 		(void) strcpy(_Protocol, save);
415 	}
416 
417 	trace1(TR_protoString, 1);
418 	return (_Protocol[0] == NULLCHAR ? NULL : _Protocol);
419 }
420 
421 /*
422  * addProto
423  *
424  * Verify that the desired protocols from the Systems and Devices file
425  * have been compiled into this application.
426  *
427  * 	desired -	list of desired protocols
428  *	valid -		list of protocols that are compiled in.
429  */
430 
431 static void
432 addProto (desired, valid)
433 char *desired;
434 char *valid;
435 {
436 	register char * protoPtr;
437 	register char *	wantPtr;
438 
439 	trace1(TR_addProto, 0);
440 	if (*desired == '\0') {
441 		trace1(TR_addProto, 1);
442 		return;
443 	}
444 
445 	if (*(protoPtr = _Protocol) != NULLCHAR) {
446 		while (*(protoPtr = nextProto(protoPtr)) != NULLCHAR) {
447 		if (*(wantPtr = findProto(desired, *protoPtr)) == NULLCHAR) {
448 			removeProto(valid, *protoPtr);
449 			removeProto(protoPtr, *protoPtr);
450 		} else {
451 			mergeProto(protoPtr, wantPtr);
452 			protoPtr++;
453 		}
454 		}
455 	} else {
456 		wantPtr = desired;
457 		while (*(wantPtr = nextProto(wantPtr)) != NULLCHAR) {
458 		if (*(findProto(valid, *wantPtr)) != NULLCHAR) {
459 			mergeProto(protoPtr, wantPtr);
460 		}
461 		wantPtr++;
462 		}
463 	}
464 	if (*(protoPtr = _Protocol) != NULLCHAR) {
465 		while (*(protoPtr = nextProto(protoPtr)) != NULLCHAR)
466 		*(valid++) = *(protoPtr++);
467 		*valid = NULLCHAR;
468 	}
469 	trace1(TR_addProto, 1);
470 	return;
471 }
472 
473 /*
474  * mergeProto
475  *
476  * input
477  * 	char *tostring, *fromstring;
478  */
479 static void
480 mergeProto(tostring, fromstring)
481 char *tostring, *fromstring;
482 {
483 	char buffer[BUFSIZ];
484 	int length;
485 
486 	trace1(TR_mergeProto, 0);
487 	while (*(tostring = nextProto(tostring)) != NULLCHAR) {
488 		if (*tostring == *fromstring)
489 		break;
490 		else
491 		tostring++;
492 	}
493 
494 	if (*tostring == NULLCHAR) {
495 		length = nextProto(fromstring + 1) - fromstring;
496 		(void) strncpy(tostring, fromstring, length);
497 		*(tostring + length) = NULLCHAR;
498 	} else {
499 		tostring++;
500 		fromstring++;
501 		if ((*tostring !=  '(') && (*fromstring == '(')) {
502 		(void) strcpy(buffer, tostring);
503 		length = nextProto(fromstring) - fromstring;
504 			(void) strncpy(tostring, fromstring, length);
505 		(void) strcpy(tostring+length, buffer);
506 		}
507 	}
508 	trace1(TR_mergeProto, 1);
509 	return;
510 }
511 
512 /*
513  * removeProto
514  *
515  * char *old
516  * char letter
517  *
518  * return
519  *	none
520  */
521 static void
522 removeProto(string, letter)
523 char *string, letter;
524 {
525 	trace1(TR_removeProto, 0);
526 	while (*(string = nextProto(string)) != NULLCHAR) {
527 		if (*string == letter)
528 		(void) strcpy(string, nextProto(string+1));
529 		else
530 		string++;
531 	}
532 	trace1(TR_removeProto, 1);
533 }
534 
535 /*
536  * nextProto
537  *	char *string;
538  * return
539  *	char * to next non-parameter letter
540  */
541 static char *
542 nextProto(string)
543 char *string;
544 {
545 	trace1(TR_nextProto, 0);
546 	if (*string == '(')
547 		while (*string != NULLCHAR)
548 		if (*(string++) == ')')
549 			break;
550 	trace1(TR_nextProto, 1);
551 	return (string);
552 }
553 
554 /*
555  * findProto
556  *	char *desired,
557  *	char protoPtr;
558  * return
559  *	char *pointer to found or string terminating NULLCHAR
560  */
561 GLOBAL char *
562 findProto(string, letter)
563 char *string;
564 char letter;
565 {
566 	trace1(TR_findProto, 0);
567 	while (*(string = nextProto(string)) != NULLCHAR)
568 		if (*string == letter)
569 		break;
570 		else
571 		string++;
572 	trace1(TR_findProto, 1);
573 	return (string);
574 }
575 
576 /*
577  * chat -	do conversation
578  * input:
579  *	nf - number of fields in flds array
580  *	flds - fields from Systems file
581  *	fn - write file number
582  *	phstr1 - phone number to replace \D
583  *	phstr2 - phone number to replace \T
584  *
585  *	return codes:  0  |  FAIL
586  */
587 
588 GLOBAL int
589 chat(nf, flds, fn, phstr1, phstr2)
590 char *flds[], *phstr1, *phstr2;
591 int nf, fn;
592 {
593 	char *want, *altern;
594 	int k, ok;
595 
596 	trace3(TR_chat, 0, nf, fn);
597 	for (k = 0; k < nf; k += 2) {
598 		want = flds[k];
599 		ok = FAIL;
600 		while (ok != 0) {
601 			altern = index(want, '-');
602 			if (altern != NULL)
603 				*altern++ = NULLCHAR;
604 			ok = expect(want, fn);
605 			if (ok == 0)
606 				break;
607 			if (altern == NULL) {
608 				Uerror = SS_LOGIN_FAILED;
609 				logent(UERRORTEXT, "FAILED");
610 				trace1(TR_chat, 1);
611 				return (FAIL);
612 			}
613 			want = index(altern, '-');
614 			if (want != NULL)
615 				*want++ = NULLCHAR;
616 			sendthem(altern, fn, phstr1, phstr2);
617 		}
618 		sleep(2);
619 		if (flds[k+1])
620 			sendthem(flds[k+1], fn, phstr1, phstr2);
621 	}
622 	trace1(TR_chat, 1);
623 	return (0);
624 }
625 
626 #define MR 1000
627 
628 /*
629  *	expect(str, fn)	look for expected string w/ possible special chars
630  *	char *str;
631  *
632  *	return codes:
633  *		0  -  found
634  *		FAIL  -  lost line or too many characters read
635  *		some character  -  timed out
636  */
637 
638 GLOBAL int
639 expect(str, fn)
640 char *str;
641 int fn;
642 {
643 	register char *bptr, *sptr;
644 	char    buf[BUFSIZ];
645 
646 	bptr = buf;
647 
648 	for (sptr = str; *sptr; sptr++) {
649 		if (*sptr == '\\') {
650 			switch (*++sptr) {
651 			case 'H':
652 				*bptr++ = '\0';
653 				if (expect_str(buf, fn) == FAIL) {
654 					return (FAIL);
655 				}
656 				if (wait_for_hangup(fn) == FAIL) {
657 					return (FAIL);
658 				}
659 				bptr = buf;
660 				continue;
661 			case '\\':
662 				*bptr++ = '\\';
663 				continue;
664 			default:
665 				*bptr++ = '\\';
666 				*bptr++ = *sptr;
667 				continue;
668 			}
669 		} else
670 			*bptr++ = *sptr;
671 	}
672 	*bptr = '\0';
673 	if (expect_str(buf, fn) == FAIL) {
674 		return (FAIL);
675 	}
676 	return (0);
677 }
678 
679 /*
680  *	expect_str(str, fn)	look for expected string, w/ no special chars
681  *
682  *	return codes:
683  *		0  -  found
684  *		FAIL  -  too many characters read
685  *		some character  -  timed out
686  */
687 
688 GLOBAL int
689 expect_str(str, fn)
690 char *str;
691 int fn;
692 {
693 	static char rdvec[MR];
694 	char *rp = rdvec;
695 	register int kr, c;
696 	char nextch;
697 
698 	trace2(TR_expect, 0, fn);
699 	*rp = 0;
700 
701 	CDEBUG(4, "expect: (%s", "");
702 	for (c=0; (kr=str[c]) != 0 ; c++)
703 		if (kr < 040) {
704 			/*EMPTY*/
705 			CDEBUG(4, "^%c", kr | 0100);
706 		} else {
707 			/*EMPTY*/
708 			CDEBUG(4, "%c", kr);
709 		}
710 	CDEBUG(4, ")\n%s", "");
711 
712 	if (EQUALS(str, "\"\"")) {
713 		CDEBUG(4, "got it\n%s", "");
714 		trace1(TR_expect, 1);
715 		return (0);
716 	}
717 	if (*str== '\0') {
718 		return(0);
719 	}
720 	if (setjmp(Sjbuf)) {
721 		trace1(TR_expect, 1);
722 		return (FAIL);
723 	}
724 	(void) signal(SIGALRM, alarmtr);
725 	alarm(expecttime);
726 	while (notin(str, rdvec)) {
727 		errno = 0;
728 		kr = (*Read)(fn, &nextch, 1);
729 		if (kr <= 0) {
730 			alarm(0);
731 			CDEBUG(4, "lost line errno - %d\n", errno);
732 			logent("LOGIN", "LOST LINE");
733 			trace1(TR_expect, 1);
734 			return (FAIL);
735 		}
736 		c = nextch & 0177;
737 		CDEBUG(4, "%s", c < 040 ? "^" : "");
738 		CDEBUG(4, "%c", c < 040 ? c | 0100 : c);
739 		if ((*rp = nextch & 0177) != NULLCHAR)
740 			rp++;
741 		if (rp >= rdvec + MR) {
742 			CDEBUG(4, "enough already\n%s", "");
743 			alarm(0);
744 			trace1(TR_expect, 1);
745 			return (FAIL);
746 		}
747 		*rp = NULLCHAR;
748 	}
749 	alarm(0);
750 	CDEBUG(4, "got it\n%s", "");
751 	trace1(TR_expect, 1);
752 	return (0);
753 }
754 
755 
756 /*
757  *	alarmtr()  -  catch alarm routine for "expect".
758  */
759 /*ARGSUSED*/
760 GLOBAL void
761 alarmtr(sig)
762 int sig;
763 {
764 	trace2(TR_alarmtr, 0, sig);
765 	CDEBUG(6, "timed out\n%s", "");
766 	longjmp(Sjbuf, 1);
767 	trace2(TR_alarmtr, 1, sig);
768 }
769 
770 /*
771  *	wait_for_hangup() - wait for a hangup to occur on the given device
772  */
773 int
774 wait_for_hangup(dcf)
775 	int dcf;
776 {
777 	int rval;
778 	char buff[BUFSIZ];
779 
780 	CDEBUG(4, "Waiting for hangup\n%s", "");
781 	while((rval = read(dcf, buff, BUFSIZ)) > 0);
782 
783 	if (rval < 0) {
784 		return (FAIL);
785 	}
786 	CDEBUG(4, "Received hangup\n%s", "");
787 
788 	if (clear_hup(dcf) != SUCCESS) {
789 	    CDEBUG(4, "Unable to clear hup on device\n%s", "");
790 	    return (FAIL);
791 	}
792 	return (SUCCESS);
793 }
794 
795 /*
796  *	sendthem(str, fn, phstr1, phstr2)	send line of chat sequence
797  *	char *str, *phstr;
798  *
799  *	return codes:  none
800  */
801 
802 #define FLUSH() {\
803 	if ((bptr - buf) > 0)\
804 		if (wrstr(fn, buf, bptr - buf, echocheck) != SUCCESS)\
805 			goto err;\
806 	bptr = buf;\
807 }
808 
809 GLOBAL void
810 sendthem(str, fn, phstr1, phstr2)
811 char *str, *phstr1, *phstr2;
812 int fn;
813 {
814 	int sendcr = 1, echocheck = 0;
815 	register char	*sptr, *bptr;
816 	char	buf[BUFSIZ];
817 	struct termio	ttybuf;
818 
819 	/* should be EQUALS, but previous versions had BREAK n for integer n */
820 
821 	trace2(TR_sendthem, 0, fn);
822 	if (PREFIX("BREAK", str)) {
823 		/* send break */
824 		CDEBUG(5, "BREAK\n%s", "");
825 		(*genbrk)(fn);
826 		trace1(TR_sendthem, 1);
827 		return;
828 	}
829 
830 	if (EQUALS(str, "EOT")) {
831 		CDEBUG(5, "EOT\n%s", "");
832 		(void) (*Write)(fn, EOTMSG, strlen(EOTMSG));
833 		trace1(TR_sendthem, 1);
834 		return;
835 	}
836 
837 	if (EQUALS(str, "\"\"")) {
838 		CDEBUG(5, "\"\"\n%s", "");
839 		str += 2;
840 	}
841 
842 	bptr = buf;
843 	CDEBUG(5, "sendthem (%s", "");
844 	for (sptr = str; *sptr; sptr++) {
845 		if (*sptr == '\\') {
846 			switch(*++sptr) {
847 
848 			/* adjust switches */
849 			case 'c':	/* no CR after string */
850 				FLUSH();
851 				if (sptr[1] == NULLCHAR) {
852 					CDEBUG(5, "<NO CR>%s", "");
853 					sendcr = 0;
854 				} else {
855 					/*EMPTY*/
856 					CDEBUG(5, "<NO CR IGNORED>\n%s", "");
857 				}
858 				continue;
859 			}
860 
861 			/* stash in buf and continue */
862 			switch(*sptr) {
863 			case 'D':	/* raw phnum */
864 				strcpy(bptr, phstr1);
865 				bptr += strlen(bptr);
866 				continue;
867 			case 'T':	/* translated phnum */
868 				strcpy(bptr, phstr2);
869 				bptr += strlen(bptr);
870 				continue;
871 			case 'N':	/* null */
872 				*bptr++ = 0;
873 				continue;
874 			case 's':	/* space */
875 				*bptr++ = ' ';
876 				continue;
877 			case '\\':	/* backslash escapes itself */
878 				*bptr++ = *sptr;
879 				continue;
880 			default:	/* send the backslash */
881 				*bptr++ = '\\';
882 				*bptr++ = *sptr;
883 				continue;
884 
885 			/* flush buf, perform action, and continue */
886 			case 'E':	/* echo check on */
887 				FLUSH();
888 				CDEBUG(5, "ECHO CHECK ON\n%s", "");
889 				echocheck = 1;
890 				continue;
891 			case 'e':	/* echo check off */
892 				FLUSH();
893 				CDEBUG(5, "ECHO CHECK OFF\n%s", "");
894 				echocheck = 0;
895 				continue;
896 			case 'd':	/* sleep briefly */
897 				FLUSH();
898 				CDEBUG(5, "DELAY\n%s", "");
899 				sleep(2);
900 				continue;
901 			case 'p':	/* pause momentarily */
902 				FLUSH();
903 				CDEBUG(5, "PAUSE\n%s", "");
904 				nap(HZ/4);	/* approximately 1/4 second */
905 				continue;
906 			case 'K':	/* inline break */
907 				FLUSH();
908 				CDEBUG(5, "BREAK\n%s", "");
909 				(*genbrk)(fn);
910 				continue;
911 			case 'M':	/* modem control - set CLOCAL */
912 			case 'm':	/* no modem control - clear CLOCAL */
913 				FLUSH();
914 				CDEBUG(5, ")\n%s CLOCAL ",
915 					(*sptr == 'M' ? "set" : "clear"));
916 				if ((*Ioctl)(fn, TCGETA, &ttybuf) != 0 ) {
917 					/*EMPTY*/
918 					CDEBUG(5, "ignored. TCGETA failed, errno %d", errno);
919 				} else {
920 					if (*sptr == 'M')
921 					ttybuf.c_cflag |= CLOCAL;
922 					else
923 					ttybuf.c_cflag &= ~CLOCAL;
924 					if ((*Ioctl)(fn, TCSETAW, &ttybuf) != 0)
925 					/*EMPTY*/
926 					CDEBUG(5, "failed. TCSETAW failed, errno %d", errno);
927 				}
928 				CDEBUG(5, "\n%s", "");
929 				continue;
930 			}
931 		} else
932 			*bptr++ = *sptr;
933 	}
934 	if (sendcr)
935 		*bptr++ = '\r';
936 	if ((bptr - buf) > 0)
937 		(void) wrstr(fn, buf, bptr - buf, echocheck);
938 
939 err:
940 	CDEBUG(5, ")\n%s", "");
941 	trace1(TR_sendthem, 1);
942 	return;
943 }
944 
945 #undef FLUSH
946 
947 GLOBAL int
948 wrstr(int fn, char *buf, int len, int echocheck)
949 {
950 	int 	i;
951 	int	 dummy;
952 	char dbuf[BUFSIZ], *dbptr = dbuf;
953 
954 	trace1(TR_wrstr, 0);
955 	buf[len] = 0;
956 
957 	if (echocheck) {
958 		dummy = wrchr(fn, buf, len);
959 		trace1(TR_wrstr, 1);
960 		return (dummy);
961 	}
962 
963 	if (Debug >= 5) {
964 		if (sysaccess(ACCESS_SYSTEMS) == 0) { /* Systems file access ok */
965 			for (i = 0; i < len; i++) {
966 				*dbptr = buf[i];
967 				if (*dbptr < 040) {
968 					*dbptr++ = '^';
969 					*dbptr = buf[i] | 0100;
970 				}
971 				dbptr++;
972 			}
973 			*dbptr = 0;
974 		} else
975 			strcpy(dbuf, "????????");
976 		CDEBUG(5, "%s", dbuf);
977 	}
978 	if ((*Write)(fn, buf, len) != len) {
979 		trace1(TR_wrstr, 1);
980 		return (FAIL);
981 	}
982 	trace1(TR_wrstr, 1);
983 	return (SUCCESS);
984 }
985 
986 GLOBAL int
987 wrchr(int fn, char *buf, int len)
988 {
989 	int 	i, saccess;
990 	char	cin, cout;
991 
992 	trace2(TR_wrchr, 0, fn);
993 	saccess = (sysaccess(ACCESS_SYSTEMS) == 0); /* protect Systems file */
994 	if (setjmp(Sjbuf)) {
995 		trace1(TR_wrchr, 1);
996 		return (FAIL);
997 	}
998 	(void) signal(SIGALRM, alarmtr);
999 
1000 	for (i = 0; i < len; i++) {
1001 		cout = buf[i];
1002 		if (saccess) {
1003 			/*EMPTY*/
1004 			CDEBUG(5, "%s", cout < 040 ? "^" : "");
1005 			CDEBUG(5, "%c", cout < 040 ? cout | 0100 : cout);
1006 		} else {
1007 			/*EMPTY*/
1008 			CDEBUG(5, "?%s", "");
1009 		}
1010 		if (((*Write)(fn, &cout, 1)) != 1) {
1011 			trace1(TR_wrchr, 1);
1012 			return (FAIL);
1013 		}
1014 		do {
1015 			(void) alarm(expecttime);
1016 			if ((*Read)(fn, &cin, 1) != 1) {
1017 				trace1(TR_wrchr, 1);
1018 				return (FAIL);
1019 			}
1020 			(void) alarm(0);
1021 			cin &= 0177;
1022 			if (saccess) {
1023 				/*EMPTY*/
1024 				CDEBUG(5, "%s", cin < 040 ? "^" : "");
1025 				CDEBUG(5, "%c", cin < 040 ? cin | 0100 : cin);
1026 			} else {
1027 				/*EMPTY*/
1028 				CDEBUG(5, "?%s", "");
1029 			}
1030 		} while (cout != (cin & 0177));
1031 	}
1032 	trace1(TR_wrchr, 1);
1033 	return (SUCCESS);
1034 }
1035 
1036 
1037 /*
1038  *	notin(sh, lg)	check for occurrence of substring "sh"
1039  *	char *sh, *lg;
1040  *
1041  *	return codes:
1042  *		0  -  found the string
1043  *		1  -  not in the string
1044  */
1045 
1046 static int
1047 notin(sh, lg)
1048 char *sh, *lg;
1049 {
1050 	trace1(TR_notin, 0);
1051 	while (*lg != NULLCHAR) {
1052 		if (PREFIX(sh, lg)) {
1053 			trace1(TR_notin, 1);
1054 			return (0);
1055 		}
1056 		else
1057 			lg++;
1058 	}
1059 	trace1(TR_notin, 1);
1060 	return (1);
1061 }
1062 
1063 
1064 /*
1065  *	ifdate(s)
1066  *	char *s;
1067  *
1068  *	ifdate  -  this routine will check a string (s)
1069  *	like "MoTu0800-1730" to see if the present
1070  *	time is within the given limits.
1071  *	SIDE EFFECT - Retrytime is set to number following ";"
1072  *
1073  *	String alternatives:
1074  *		Wk - Mo thru Fr
1075  *		zero or one time means all day
1076  *		Any - any day
1077  *
1078  *	return codes:
1079  *		0  -  not within limits
1080  *		1  -  within limits
1081  */
1082 
1083 static int
1084 ifdate(s)
1085 char *s;
1086 {
1087 	static char *days[] = {
1088 		"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", 0
1089 	};
1090 	time_t	clock;
1091 	int	t__now;
1092 	char	*p;
1093 	struct tm	*tp;
1094 
1095 	trace1(TR_ifdate, 0);
1096 	time(&clock);
1097 	tp = localtime(&clock);
1098 	t__now = tp->tm_hour * 100 + tp->tm_min;	/* "navy" time */
1099 
1100 	/*
1101 	 *	pick up retry time for failures
1102 	 *	global variable Retrytime is set here
1103 	 */
1104 	if ((p = rindex(s, ';')) != NULL)
1105 		if (isdigit(p[1])) {
1106 		if (sscanf(p+1, "%ld", &Retrytime) < 1)
1107 			Retrytime = 5;	/* 5 minutes is error default */
1108 		Retrytime  *= 60;
1109 		*p = NULLCHAR;
1110 		}
1111 
1112 	while (*s) {
1113 		int	i, dayok;
1114 
1115 		for (dayok = 0; (!dayok) && isalpha(*s); s++) {
1116 			if (PREFIX("Any", s))
1117 				dayok = 1;
1118 			else if (PREFIX("Wk", s)) {
1119 				if (tp->tm_wday >= 1 && tp->tm_wday <= 5)
1120 					dayok = 1;
1121 			} else
1122 				for (i = 0; days[i]; i++)
1123 					if (PREFIX(days[i], s))
1124 						if (tp->tm_wday == i)
1125 							dayok = 1;
1126 		}
1127 
1128 		if (dayok) {
1129 			int	t__low, t__high;
1130 
1131 			while (isalpha(*s))	/* flush remaining day stuff */
1132 				s++;
1133 
1134 			if ((sscanf(s, "%d-%d", &t__low, &t__high) < 2)
1135 			 || (t__low == t__high)) {
1136 				trace1(TR_ifdate, 1);
1137 				return (1);
1138 			}
1139 
1140 			/* 0000 crossover? */
1141 			if (t__low < t__high) {
1142 				if (t__low <= t__now && t__now <= t__high) {
1143 					trace1(TR_ifdate, 1);
1144 					return (1);
1145 				}
1146 			} else if (t__low <= t__now || t__now <= t__high) {
1147 				trace1(TR_ifdate, 1);
1148 				return (1);
1149 			}
1150 
1151 			/* aim at next time slot */
1152 			if ((s = index(s, ',')) == NULL)
1153 				break;
1154 		}
1155 		if (*s)
1156 			s++;
1157 	}
1158 	trace1(TR_ifdate, 1);
1159 	return (0);
1160 }
1161 
1162 /*
1163  *	char *
1164  *	fdig(cp)	find first digit in string
1165  *
1166  *	return - pointer to first digit in string or end of string
1167  */
1168 
1169 GLOBAL char *
1170 fdig(cp)
1171 char *cp;
1172 {
1173 	char *c;
1174 
1175 	trace1(TR_fdig, 0);
1176 	for (c = cp; *c; c++)
1177 		if (*c >= '0' && *c <= '9')
1178 			break;
1179 	trace1(TR_fdig, 1);
1180 	return (c);
1181 }
1182 
1183 
1184 #ifdef FASTTIMER
1185 /*	Sleep in increments of 60ths of second.	*/
1186 GLOBAL void
1187 nap (time)
1188 register int time;
1189 {
1190 	static int fd;
1191 
1192 	trace2(TR_nap, 0, time);
1193 	if (fd == 0)
1194 		fd = open (FASTTIMER, 0);
1195 
1196 	(void) (*Read)(fd, 0, time);
1197 	trace1(TR_nap, 1);
1198 	return;
1199 }
1200 
1201 #endif /* FASTTIMER */
1202 
1203 #if defined(BSD4_2) || defined(ATTSVR4)
1204 
1205 	/* nap(n) -- sleep for 'n' ticks of 1/60th sec each. */
1206 	/* This version uses the select system call */
1207 
1208 
1209 GLOBAL void
1210 nap(n)
1211 unsigned n;
1212 {
1213 	struct timeval tv;
1214 	int rc;
1215 
1216 	trace2(TR_nap, 0, n);
1217 	if (n==0) {
1218 		trace1(TR_nap, 1);
1219 		return;
1220 	}
1221 	tv.tv_sec = n/60;
1222 	tv.tv_usec = ((n%60)*1000000L)/60;
1223 	rc = select(32, 0, 0, 0, &tv);
1224 	trace1(TR_nap, 1);
1225 	return;
1226 }
1227 
1228 #endif /* BSD4_2 || ATTSVR4 */
1229 
1230 #ifdef NONAP
1231 
1232 /*	nap(n) where n is ticks
1233  *
1234  *	loop using n/HZ part of a second
1235  *	if n represents more than 1 second, then
1236  *	use sleep(time) where time is the equivalent
1237  *	seconds rounded off to full seconds
1238  *	NOTE - this is a rough approximation and chews up
1239  *	processor resource!
1240  */
1241 
1242 GLOBAL void
1243 nap(n)
1244 unsigned n;
1245 {
1246 	struct tms	tbuf;
1247 	long endtime;
1248 	int i;
1249 
1250 	trace2(TR_nap, 0, n);
1251 	if (n > HZ) {
1252 		/* > second, use sleep, rounding time */
1253 		sleep((int) (((n)+HZ/2)/HZ));
1254 		trace1(TR_nap, 1);
1255 		return;
1256 	}
1257 
1258 	/* use timing loop for < 1 second */
1259 	endtime = times(&tbuf) + 3*n/4;	/* use 3/4 because of scheduler! */
1260 	while (times(&tbuf) < endtime) {
1261 		for (i=0; i<1000; i++, (void) (i*i))
1262 		;
1263 	}
1264 	trace1(TR_nap, 1);
1265 	return;
1266 }
1267 
1268 #endif /* NONAP */
1269 
1270 /*
1271 
1272  * altconn - place a telephone call to system
1273  * from cu when telephone number or direct line used
1274  *
1275  * return codes:
1276  *	FAIL - connection failed
1277  *	>0  - file no.  -  connect ok
1278  * When a failure occurs, Uerror is set.
1279  */
1280 GLOBAL int
1281 altconn(call)
1282 struct call *call;
1283 {
1284 	int fn = FAIL;
1285 	char *alt[7];
1286 	EXTERN char *Myline;
1287 
1288 	/*
1289 	 * replace the Systems file fields
1290 	 * needed for getto(); [F_TYPE] and
1291 	 * [F_PHONE] assignment below.
1292 	 */
1293 	alt[F_NAME] = "dummy";
1294 	alt[F_TIME] = "Any";
1295 	alt[F_TYPE] = "";
1296 	alt[F_CLASS] = call->speed;
1297 	alt[F_PHONE] = "";
1298 	alt[F_LOGIN] = "";
1299 	alt[6] = NULL;
1300 
1301 	trace1(TR_altconn, 0);
1302 	CDEBUG(4,"altconn called\r\n%s", "");
1303 
1304 	/* cu -l dev ...					*/
1305 	/* if is "/dev/device", strip off "/dev/" because must	*/
1306 	/* exactly match entries in Devices file, which usually	*/
1307 	/* omit the "/dev/".  if doesn't begin with "/dev/",	*/
1308 	/* either they've omitted the "/dev/" or it's a non-	*/
1309 	/* standard path name.  in either case, leave it as is	*/
1310 
1311 	if (call->line != NULL) {
1312 		if (strncmp(call->line, "/dev/", 5) == 0) {
1313 			Myline = (call->line + 5);
1314 		} else {
1315 			Myline = call->line;
1316 		}
1317 	}
1318 
1319 	/* cu  ... telno */
1320 	if (call->telno != NULL) {
1321 		alt[F_PHONE] = call->telno;
1322 		alt[F_TYPE] = "ACU";
1323 	} else {
1324 	/* cu direct line */
1325 		alt[F_TYPE] = "Direct";
1326 	}
1327 	if (call->type != NULL)
1328 		alt[F_TYPE] = call->type;
1329 	fn = getto(alt);
1330 	CDEBUG(4, "getto ret %d\n", fn);
1331 
1332 	trace1(TR_altconn, 1);
1333 	return (fn);
1334 
1335 }
1336