xref: /original-bsd/usr.bin/uucp/uucico/condevs.c (revision da818fbb)
1 #ifndef lint
2 static char sccsid[] = "@(#)condevs.c	5.21	(Berkeley) 05/11/89";
3 #endif
4 
5 extern int errno;
6 extern char *sys_errlist[];
7 
8 /*
9  * Here are various dialers to establish the machine-machine connection.
10  * conn.c/condevs.c was glued together by Mike Mitchell.
11  * The dialers were supplied by many people, to whom we are grateful.
12  *
13  * ---------------------------------------------------------------------
14  * NOTE:
15  * There is a bug that occurs at least on PDP11s due to a limitation of
16  * setjmp/longjmp.   If the routine that does a setjmp is interrupted
17  * and longjmp-ed to,  it loses its register variables (on a pdp11).
18  * What works is if the routine that does the setjmp
19  * calls a routine and it is the *subroutine* that is interrupted.
20  *
21  * Anyway, in conclusion, condevs.c is plagued with register variables
22  * that are used inside
23  * 	if (setjmp(...)) {
24  * 		....
25  * 	}
26  *
27  * THE FIX: Don't declare variables to be register
28  */
29 
30 #include "condevs.h"
31 #include "pathnames.h"
32 
33 struct condev condevs[] = {
34 	{ "DIR", "direct", diropn, nulldev, dircls },
35 #ifdef DATAKIT
36 	{ "DK", "datakit", dkopn, nulldev, nulldev },
37 #endif DATAKIT
38 #ifdef PNET
39 	{ "PNET", "pnet", pnetopn, nulldev, nulldev },
40 #endif PNET
41 #ifdef	UNETTCP
42 	{ "TCP", "TCP", unetopn, nulldev, unetcls },
43 #endif UNETTCP
44 #ifdef BSDTCP
45 	{ "TCP", "TCP", bsdtcpopn, nulldev, bsdtcpcls },
46 #endif BSDTCP
47 #ifdef MICOM
48 	{ "MICOM", "micom", micopn, nulldev, miccls },
49 #endif MICOM
50 #ifdef DN11
51 	{ "ACU", "dn11", Acuopn, dnopn, dncls },
52 #endif DN11
53 #ifdef HAYES
54 	{ "ACU", "hayes", Acuopn, hyspopn, hyscls },
55 	{ "ACU", "hayespulse", Acuopn, hyspopn, hyscls },
56 	{ "ACU", "hayestone", Acuopn, hystopn, hyscls },
57 	{ "WATS", "hayestone", Acuopn, hystopn, hyscls },
58 #endif HAYES
59 #ifdef HAYES2400
60 	{ "ACU", "hayes2400", Acuopn, hyspopn24, hyscls24 },
61 	{ "ACU", "hayes2400pulse", Acuopn, hyspopn24, hyscls24 },
62 	{ "ACU", "hayes2400tone", Acuopn, hystopn24, hyscls24 },
63 #endif HAYES2400
64 #ifdef HAYESQ	/* a version of hayes that doesn't use result codes */
65 	{ "ACU", "hayesq", Acuopn, hysqpopn, hysqcls },
66 	{ "ACU", "hayesqpulse", Acuopn, hysqpopn, hysqcls },
67 	{ "ACU", "hayesqtone", Acuopn, hysqtopn, hysqcls },
68 #endif HAYESQ
69 #ifdef CDS224
70 	{ "ACU", "cds224", Acuopn, cdsopn224, cdscls224},
71 #endif CDS224
72 #ifdef NOVATION
73 	{ "ACU", "novation", Acuopn, novopn, novcls},
74 #endif NOVATION
75 #ifdef DF02
76 	{ "ACU", "DF02", Acuopn, df2opn, df2cls },
77 #endif DF02
78 #ifdef DF112
79 	{ "ACU", "DF112P", Acuopn, df12popn, df12cls },
80 	{ "ACU", "DF112T", Acuopn, df12topn, df12cls },
81 #endif DF112
82 #ifdef VENTEL
83 	{ "ACU", "ventel", Acuopn, ventopn, ventcls },
84 #endif VENTEL
85 #ifdef PENRIL
86 	{ "ACU", "penril", Acuopn, penopn, pencls },
87 #endif PENRIL
88 #ifdef VADIC
89 	{ "ACU", "vadic", Acuopn, vadopn, vadcls },
90 #endif VADIC
91 #ifdef VA212
92 	{ "ACU", "va212", Acuopn, va212opn, va212cls },
93 #endif VA212
94 #ifdef VA811S
95 	{ "ACU", "va811s", Acuopn, va811opn, va811cls },
96 #endif VA811S
97 #ifdef VA820
98 	{ "ACU", "va820", Acuopn, va820opn, va820cls },
99 	{ "WATS", "va820", Acuopn, va820opn, va820cls },
100 #endif VA820
101 #ifdef RVMACS
102 	{ "ACU", "rvmacs", Acuopn, rvmacsopn, rvmacscls },
103 #endif RVMACS
104 #ifdef VMACS
105 	{ "ACU", "vmacs", Acuopn, vmacsopn, vmacscls },
106 #endif VMACS
107 #ifdef SYTEK
108 	{ "SYTEK", "sytek", sykopn, nulldev, sykcls },
109 #endif SYTEK
110 #ifdef ATT2224
111 	{ "ACU", "att", Acuopn, attopn, attcls },
112 #endif ATT2224
113 
114 
115 	/* Insert new entries before this line */
116 	{ NULL, NULL, NULL, NULL, NULL }
117 };
118 
119 /*
120  *	nulldev		a null device (returns CF_DIAL)
121  */
122 nulldev()
123 {
124 	return CF_DIAL;
125 }
126 
127 /*
128  *	nodev		a null device (returns CF_NODEV)
129  */
130 nodev()
131 {
132 	return CF_NODEV;
133 }
134 
135 /*
136  * Generic devices look through L-devices and call the CU_open routines for
137  * appropriate devices.  Some things, like the tcp/ip interface, or direct
138  * connect, do not use the CU_open entry.  ACUs must search to find the
139  * right routine to call.
140  */
141 
142 /*
143  *	diropn(flds)	connect to hardware line
144  *
145  *	return codes:
146  *		> 0  -  file number  -  ok
147  *		FAIL  -  failed
148  */
149 diropn(flds)
150 register char *flds[];
151 {
152 	register int dcr, status;
153 	struct Devices dev;
154 	char dcname[20];
155 	FILE *dfp;
156 #ifdef VMSDTR	/* Modem control on vms(works dtr) */
157 	int modem_control;
158 	short iosb[4];
159 	int sys$qiow();	/* use this for long reads on vms */
160 	int ret;
161 	long mode[2];
162 	modem_control = 0;
163 #endif
164 	dfp = fopen(DEVFILE, "r");
165 	if (dfp == NULL) {
166 		syslog(LOG_ERR, "fopen(%s) failed: %m", DEVFILE);
167 		cleanup(FAIL);
168 	}
169 	while ((status = rddev(dfp, &dev)) != FAIL) {
170 #ifdef VMSDTR	/* Modem control on vms(works dtr) */
171 		/* If we find MOD in the device type field we go into action */
172 		if (strcmp(dev.D_type, "MOD") == SAME) {
173 			modem_control = 1;
174 		        DEBUG(7, "Setting Modem control to %d",modem_control);
175 		}
176 		if (strcmp(flds[F_CLASS], dev.D_class) != SAME)
177 				continue;
178 		/*
179 		 * Modem control on vms(works dtr) Take anything in MOD class.
180 	  	 * It probably should work differently anyway so we can have
181 		 *  multiple hardwired lines.
182 		 */
183 		if (!modem_control&&strcmp(flds[F_PHONE], dev.D_line) != SAME)
184 #else !VMSDTR
185 		if (strcmp(flds[F_CLASS], dev.D_class) != SAME)
186 			continue;
187 		if (strcmp(flds[F_PHONE], dev.D_line) != SAME)
188 #endif !VMSDTR
189 			continue;
190 		if (mlock(dev.D_line) != FAIL)
191 			break;
192 	}
193 	fclose(dfp);
194 	if (status == FAIL) {
195 		logent("DEVICE", "NO");
196 		return CF_NODEV;
197 	}
198 
199 	sprintf(dcname, "%s/%s", _PATH_DEV, dev.D_line);
200 	if (setjmp(Sjbuf)) {
201 		DEBUG(4, "Open timed out\n", CNULL);
202 		delock(dev.D_line);
203 		return CF_DIAL;
204 	}
205 	signal(SIGALRM, alarmtr);
206 	/* For PC Pursuit, it could take a while to call back */
207 	alarm( strcmp(flds[F_LINE], "PCP") ? 10 : MAXMSGTIME*4 );
208 	getnextfd();
209 	errno = 0;
210         DEBUG(4,"Opening %s\n",dcname);
211 	dcr = open(dcname, 2); /* read/write */
212 #ifdef VMSDTR	/* Modem control on vms(works dtr) */
213 	fflush(stdout);
214 	if (modem_control) { /* Did we have MOD in the device type field ? */
215 		/* Sense the current terminal setup and save it */
216 		if ((ret = sys$qiow(_$EFN,(fd_fab_pointer[dcr]->fab).fab$l_stv,
217 			IO$_SENSEMODE,iosb,0,0,mode,8,0,0,0,0))
218 				!= SS$_NORMAL) {
219 			DEBUG(7, "ret status on sense failed on Modem sense=%x<", ret);
220 			return CF_DIAL;
221 		}
222 		mode[1] |= TT$M_MODEM; /* Or in modem control(DTR) */
223 		/* Now set the new terminal characteristics */
224 		/* This is temporary and will go away when we let go of it */
225 		if ((ret = sys$qiow(_$EFN,(fd_fab_pointer[dcr]->fab).fab$l_stv,
226 			IO$_SETMODE,iosb,0,0,mode,8,0,0,0,0))
227 				!= SS$_NORMAL) {
228 			DEBUG(7, "ret status on sense failed on Modem setup=%x<", ret);
229 			return CF_DIAL;
230 		}
231 	}
232 #endif VMSDTR
233 	next_fd = -1;
234 	alarm(0);
235 	if (dcr < 0) {
236 		if (errno == EACCES)
237 			logent(dev.D_line, "CANT OPEN");
238 		DEBUG(4, "OPEN FAILED: errno %d\n", errno);
239 		delock(dev.D_line);
240 		return CF_DIAL;
241 	}
242 	fflush(stdout);
243 	if (fixline(dcr, dev.D_speed) == FAIL) {
244 		DEBUG(4, "FIXLINE FAILED\n", CNULL);
245 		return CF_DIAL;
246 	}
247 	strcpy(devSel, dev.D_line);	/* for latter unlock */
248 	CU_end = dircls;
249 	return dcr;
250 }
251 
252 dircls(fd)
253 register int fd;
254 {
255 	if (fd > 0) {
256 		close(fd);
257 		delock(devSel);
258 	}
259 }
260 
261 /*
262  *	open an ACU and dial the number.  The condevs table
263  *	will be searched until a dialing unit is found that is free.
264  *
265  *	return codes:	>0 - file number - o.k.
266  *			FAIL - failed
267  */
268 char devSel[20];	/* used for later unlock() */
269 
270 Acuopn(flds)
271 register char *flds[];
272 {
273 	char phone[MAXPH+1];
274 	register struct condev *cd;
275 	register int fd, acustatus;
276 	register FILE *dfp;
277 	struct Devices dev;
278 	int retval = CF_NODEV;
279 	char nobrand[MAXPH], *line;
280 
281 	exphone(flds[F_PHONE], phone);
282 	if (snccmp(flds[F_LINE], "LOCAL") == SAME)
283 		line = "ACU";
284 	else
285 		line = flds[F_LINE];
286 	devSel[0] = '\0';
287 	nobrand[0] = '\0';
288 	DEBUG(4, "Dialing %s\n", phone);
289 	dfp = fopen(DEVFILE, "r");
290 	if (dfp == NULL) {
291 		syslog(LOG_ERR, "fopen(%s) failed: %m", DEVFILE);
292 		cleanup(FAIL);
293 	}
294 
295 	acustatus = 0;	/* none found, none locked */
296 	while (rddev(dfp, &dev) != FAIL) {
297 		/*
298 		 * for each ACU L.sys line, try at most twice
299 		 * (TRYCALLS) to establish carrier.  The old way tried every
300 		 * available dialer, which on big sites takes forever!
301 		 * Sites with a single auto-dialer get one try.
302 		 * Sites with multiple dialers get a try on each of two
303 		 * different dialers.
304 		 * To try 'harder' to connect to a remote site,
305 		 * use multiple L.sys entries.
306 		 */
307 		if (acustatus > TRYCALLS)
308 			break;
309 		if (strcmp(flds[F_CLASS], dev.D_class) != SAME)
310 			continue;
311 		if (snccmp(line, dev.D_type) != SAME)
312 			continue;
313 		if (dev.D_brand[0] == '\0') {
314 			logent("Acuopn","No 'brand' name on ACU");
315 			continue;
316 		}
317 		for(cd = condevs; cd->CU_meth != NULL; cd++) {
318 			if (snccmp(line, cd->CU_meth) == SAME) {
319 				if (snccmp(dev.D_brand, cd->CU_brand) == SAME) {
320 					nobrand[0] = '\0';
321 					break;
322 				}
323 				strncpy(nobrand, dev.D_brand, sizeof nobrand);
324 			}
325 		}
326 
327 		if (acustatus < 1)
328 			acustatus = 1;	/* has been found */
329 
330 		if (mlock(dev.D_line) == FAIL)
331 			continue;
332 
333 #ifdef DIALINOUT
334 #ifdef ALLACUINOUT
335 		if (1) {
336 #else !ALLACUINOUT
337 		if (snccmp("inout", dev.D_calldev) == SAME) {
338 #endif !ALLACUINOUT
339 			if (disable(dev.D_line) == FAIL) {
340 				delock(dev.D_line);
341 				continue;
342 			}
343 		}  else
344 			reenable();
345 #endif DIALINOUT
346 
347 		DEBUG(4, "Using %s\n", cd->CU_brand);
348 		acustatus++;
349 		fd = (*(cd->CU_open))(phone, flds, &dev);
350 		if (fd > 0) {
351 			CU_end = cd->CU_clos;   /* point CU_end at close func */
352 			fclose(dfp);
353 			strcpy(devSel, dev.D_line);   /* save for later unlock() */
354 			return fd;
355 		} else
356 			delock(dev.D_line);
357 		retval = CF_DIAL;
358 	}
359 	fclose(dfp);
360 	if (acustatus == 0) {
361 		if (nobrand[0])
362 			logent(nobrand, "unsupported ACU type");
363 		else
364 			logent("L-devices", "No appropriate ACU");
365 	}
366 	if (acustatus == 1)
367 		logent("DEVICE", "NO");
368 	return retval;
369 }
370 
371 /*
372  * intervaldelay:  delay execution for numerator/denominator seconds.
373  */
374 
375 #ifdef INTERVALTIMER
376 #include <sys/time.h>
377 #define uucpdelay(num,denom) intervaldelay(num,denom)
378 intervaldelay(num,denom)
379 int num, denom;
380 {
381 	struct timeval tv;
382 	tv.tv_sec = num / denom;
383 	tv.tv_usec = (num * 1000000L / denom ) % 1000000L;
384 	(void) select (0, (int *)0, (int *)0, (int *)0, &tv);
385 }
386 #endif INTERVALTIMER
387 
388 #ifdef FASTTIMER
389 #define uucpdelay(num,denom) nap(60*num/denom)
390 /*	Sleep in increments of 60ths of second.	*/
391 nap (time)
392 register int time;
393 {
394 	static int fd;
395 
396 	if (fd == 0)
397 		fd = open (FASTTIMER, 0);
398 
399 	read (fd, 0, time);
400 }
401 #endif FASTTIMER
402 
403 #ifdef FTIME
404 #define uucpdelay(num,denom) ftimedelay(1000*num/denom)
405 ftimedelay(n)
406 {
407 	static struct timeb loctime;
408 	register i = loctime.millitm;
409 
410 	ftime(&loctime);
411 	while (abs((int)(loctime.millitm - i))<n) ftime(&loctime)
412 		;
413 }
414 #endif FTIME
415 
416 #ifdef BUSYLOOP
417 #define uucpdelay(num,denom) busyloop(CPUSPEED*num/denom)
418 #define CPUSPEED 1000000	/* VAX 780 is 1MIPS */
419 #define	DELAY(n)	{ register long N = (n); while (--N > 0); }
420 busyloop(n)
421 {
422 	DELAY(n);
423 }
424 #endif BUSYLOOP
425 
426 slowrite(fd, str)
427 register char *str;
428 {
429 	DEBUG(6, "slowrite ", CNULL);
430 	while (*str) {
431 		DEBUG(6, "%c", *str);
432 		uucpdelay(1, 10);	/* delay 1/10 second */
433 		write(fd, str, 1);
434 		str++;
435 	}
436 	DEBUG(6, "\n", CNULL);
437 }
438 
439 #define BSPEED B150
440 
441 /*
442  *	send a break
443  */
444 genbrk(fn, bnulls)
445 register int fn, bnulls;
446 {
447 #ifdef	USG
448 	if (ioctl(fn, TCSBRK, STBNULL) < 0)
449 		DEBUG(5, "break TCSBRK %s\n", sys_errlist[errno]);
450 #else	!USG
451 # ifdef	TIOCSBRK
452 	if (ioctl(fn, TIOCSBRK, STBNULL) < 0)
453 		DEBUG(5, "break TIOCSBRK %s\n", sys_errlist[errno]);
454 # ifdef	TIOCCBRK
455 	uucpdelay(bnulls, 10);
456 	if (ioctl(fn, TIOCCBRK, STBNULL) < 0)
457 		DEBUG(5, "break TIOCCBRK %s\n", sys_errlist[errno]);
458 # endif TIOCCBRK
459 	DEBUG(4, "ioctl %f second break\n", (float) bnulls/10 );
460 # else !TIOCSBRK
461 	struct sgttyb ttbuf;
462 	register int sospeed;
463 
464 	if (ioctl(fn, TIOCGETP, &ttbuf) < 0)
465 		DEBUG(5, "break TIOCGETP %s\n", sys_errlist[errno]);
466 	sospeed = ttbuf.sg_ospeed;
467 	ttbuf.sg_ospeed = BSPEED;
468 	if (ioctl(fn, TIOCSETP, &ttbuf) < 0)
469 		DEBUG(5, "break TIOCSETP %s\n", sys_errlist[errno]);
470 	if (write(fn, "\0\0\0\0\0\0\0\0\0\0\0\0", bnulls) != bnulls) {
471 badbreak:
472 		logent(sys_errlist[errno], "BAD WRITE genbrk");
473 		alarm(0);
474 		longjmp(Sjbuf, 3);
475 	}
476 	ttbuf.sg_ospeed = sospeed;
477 	if (ioctl(fn, TIOCSETP, &ttbuf) < 0)
478 		DEBUG(5, "break ioctl %s\n", sys_errlist[errno]);
479 	if (write(fn, "@", 1) != 1)
480 		goto badbreak;
481 	DEBUG(4, "sent BREAK nulls - %d\n", bnulls);
482 #endif !TIOCSBRK
483 #endif !USG
484 }
485 
486 
487 #ifdef DIALINOUT
488 /* DIALIN/OUT CODE (WLS) */
489 /*
490  * disable and reenable:  allow a single line to be use for dialin/dialout
491  *
492  */
493 
494 char enbdev[16];
495 
496 disable(dev)
497 register char *dev;
498 {
499 	register char *rdev;
500 
501 	/* strip off directory prefixes */
502 	rdev = dev;
503 	while (*rdev)
504 		rdev++;
505 	while (--rdev >= dev && *rdev != '/')
506 		;
507 	rdev++;
508 
509 	if (enbdev[0]) {
510 		if (strcmp(enbdev, rdev) == SAME)
511 			return SUCCESS;	/* already disabled */
512 		delock(enbdev);
513 		reenable();		/* else, reenable the old one */
514 	}
515 	DEBUG(4, "Disable %s\n", rdev);
516 	if (enbcall("disable", rdev) == FAIL)
517 		return FAIL;
518 	strcpy(enbdev, rdev);
519 	return SUCCESS;
520 }
521 
522 reenable()
523 {
524 	if (enbdev[0] == '\0')
525 		return;
526 	DEBUG(4, "Reenable %s\n", enbdev);
527 	(void) enbcall("enable", enbdev);
528 	enbdev[0] = '\0';
529 }
530 
531 enbcall(type, dev)
532 char *type, *dev;
533 {
534 	int pid;
535 	register char *p;
536 	int fildes[2];
537 	int status;
538 	FILE *fil;
539 	char buf[80];
540 
541 	fflush(stderr);
542 	fflush(stdout);
543 	pipe(fildes);
544 	if ((pid = fork()) == 0) {
545 		DEBUG(4, DIALINOUT, CNULL);
546 		DEBUG(4, " %s", type);
547 		DEBUG(4, " %s\n", dev);
548 		close(fildes[0]);
549 		close(0); close(1); close(2);
550 		open(_PATH_DEVNULL,0);
551 		dup(fildes[1]); dup(fildes[1]);
552 		setuid(geteuid());	/* for chown(uid()) in acu program */
553 		execl(DIALINOUT, "acu", type, dev, Rmtname, (char *)0);
554 		exit(-1);
555 	}
556 	if (pid<0)
557 		return FAIL;
558 
559 	close(fildes[1]);
560 	fil = fdopen(fildes[0],"r");
561 	if (fil!=NULL) {
562 #ifdef BSD4_2
563 		setlinebuf(fil);
564 #endif BSD4_2
565 		while (fgets(buf, sizeof buf, fil) != NULL) {
566 			p = buf + strlen(buf) - 1;
567 			if (*p == '\n')
568 				*p = '\0';
569 			DEBUG(4, "ACUCNTRL: %s\n", buf);
570 		}
571 	}
572 	while(wait(&status) != pid)
573 		;
574 	fclose(fil);
575 	return status ? FAIL : SUCCESS;
576 }
577 #endif DIALINOUT
578