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