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