1 /*-
2  * Copyright (c) 1985, 1986, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1985, 1986, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)acucntrl.c	8.1 (Berkeley) 06/06/93";
16 #endif /* not lint */
17 
18 /*  acucntrl - turn around tty line between dialin and dialout
19  *
20  * Usage:	acucntrl {enable,disable} /dev/ttydX
21  *
22  * History:
23  *	First written by Allan Wilkes (fisher!allan)
24  *
25  *	Modified June 8,1983 by W.Sebok (astrovax!wls) to poke kernel rather
26  * 	than use kernel hack to turn on/off modem control, using subroutine
27  *	stolen from program written by Tsutomu Shimomura
28  *	{astrovax,escher}!tsutomu
29  *
30  *	Worked over many times by W.Sebok (i.e. hacked to death)
31  *
32  * Operation:
33  *   disable (i.e. setup for dialing out)
34  *	(1) check input arguments
35  *	(2) look in _PATH_UTMP to check that the line is not in use by another
36  *	(3) disable modem control on terminal
37  *	(4) check for carrier on device
38  *	(5) change owner of device to real id
39  *	(6) edit _PATH_TTYS, changing the first character of the appropriate
40  *	    line to 0
41  *	(7) send a hangup to process 1 to poke init to disable getty
42  *	(8) post uid name in capitals in _PATH_UTMP to let world know device
43  *	    has been grabbed
44  *	(9) make sure that DTR is on
45  *
46  *   enable (i.e.) restore for dialin
47  *	(1) check input arguments
48  *	(2) look in _PATH_UTMP to check that the line is not in use by another
49  *	(3) make sure modem control on terminal is disabled
50  *	(4) turn off DTR to make sure line is hung up
51  *	(5) condition line: clear exclusive use and set hangup on close modes
52  *	(6) turn on modem control
53  *	(7) edit _PATH_TTYS, changing the first character of the appropriate
54  *	    line to 1
55  *	(8) send a hangup to process 1 to poke init to enable getty
56  *	(9) clear uid name for _PATH_UTMP
57  */
58 
59 /* #define SENSECARRIER */
60 
61 #include "uucp.h"
62 #ifdef DIALINOUT
63 #include <sys/buf.h>
64 #include <signal.h>
65 #include <sys/conf.h>
66 #ifdef vax
67 #ifdef BSD4_2
68 #include <vaxuba/ubavar.h>
69 #else
70 #include <sys/ubavar.h>
71 #endif
72 #endif /* vax */
73 #include <sys/stat.h>
74 #include <nlist.h>
75 #include <sgtty.h>
76 #include <utmp.h>
77 #include <pwd.h>
78 #include <stdio.h>
79 #include <sys/file.h>
80 #include "pathnames.h"
81 
82 #define NDZLINE	8	/* lines/dz */
83 #define NDHLINE	16	/* lines/dh */
84 #define NDMFLINE 8	/* lines/dmf */
85 
86 #define DZ11	1
87 #define DH11	2
88 #define DMF	3
89 
90 #define NLVALUE(val)	(nl[val].n_value)
91 
92 struct nlist nl[] = {
93 #define CDEVSW	0
94 	{ "_cdevsw" },
95 
96 #define DZOPEN	1
97 	{ "_dzopen" },
98 #define DZINFO	2
99 	{ "_dzinfo" },
100 #define NDZ11	3
101 	{ "_dz_cnt" },
102 #define DZSCAR	4
103 	{ "_dzsoftCAR" },
104 
105 #define DHOPEN	5
106 	{ "_dhopen" },
107 #define DHINFO	6
108 	{ "_dhinfo" },
109 #define NDH11	7
110 	{ "_ndh11" },
111 #define DHSCAR	8
112 	{ "_dhsoftCAR" },
113 
114 #define DMFOPEN	9
115 	{ "_dmfopen" },
116 #define DMFINFO	10
117 	{ "_dmfinfo" },
118 #define NDMF	11
119 	{ "_ndmf" },
120 #define DMFSCAR	12
121 	{ "_dmfsoftCAR" },
122 
123 	{ "\0" }
124 };
125 
126 #define ENABLE	1
127 #define DISABLE	0
128 
129 char Etcttys[] = _PATH_TTYS;
130 #ifdef BSD4_3
131 FILE *ttysfile, *nttysfile;
132 char NEtcttys[] = _PATH_NEWTTYS;
133 extern long ftell();
134 #endif BSD4_3
135 char Devhome[] = _PATH_DEV;
136 
137 char usage[] = "Usage: acucntrl {dis|en}able ttydX\n";
138 
139 struct utmp utmp;
140 char resettty, resetmodem;
141 int etcutmp;
142 off_t utmploc;
143 off_t ttyslnbeg;
144 extern int errno;
145 extern char *sys_errlist[];
146 off_t lseek();
147 
148 #define NAMSIZ	sizeof(utmp.ut_name)
149 #define	LINSIZ	sizeof(utmp.ut_line)
150 
151 main(argc, argv)
152 int argc; char *argv[];
153 {
154 	register char *p;
155 	register int i;
156 	char uname[NAMSIZ], Uname[NAMSIZ];
157 	int enable ;
158 	char *device;
159 	int devfile;
160 	int uid, gid;
161 	struct passwd *getpwuid();
162 	char *rindex();
163 
164 	/* check input arguments */
165 	if (argc!=3 && argc != 4) {
166 		fprintf(stderr, usage);
167 		exit(1);
168 	}
169 
170 	/* interpret command type */
171 	if (prefix(argv[1], "disable")  || strcmp(argv[1], "dialout")==0)
172 		enable = 0;
173 	else if (prefix(argv[1], "enable")  || strcmp(argv[1], "dialin")==0)
174 		enable = 1;
175 	else {
176 		fprintf(stderr, usage);
177 		exit(1);
178 	}
179 
180 	device = rindex(argv[2], '/');
181 	device = (device == NULL) ? argv[2]: device+1;
182 
183 	opnttys(device);
184 
185 #ifdef vax
186 	/* Get nlist info */
187 	nlist(_PATH_UNIX, nl);
188 #endif vax
189 
190 	/* Chdir to /dev */
191 	if(chdir(Devhome) < 0) {
192 		fprintf(stderr, "Cannot chdir to %s: %s\r\n",
193 			Devhome, sys_errlist[errno]);
194 		exit(1);
195 	}
196 
197 	/* Get uid information */
198 	uid = getuid();
199 	gid = getgid();
200 
201 	p = getpwuid(uid)->pw_name;
202 	if (p==NULL) {
203 		fprintf(stderr, "cannot get uid name\n");
204 		exit(1);
205 	}
206 
207 	if (strcmp(p, "uucp") == 0 && argc == 4)
208 		p = argv[3];
209 
210 	/*  to upper case */
211 	i = 0;
212 	do {
213 		uname[i] = *p;
214 		Uname[i++] = (*p>='a' && *p<='z') ? (*p - ('a'-'A')) : *p;
215 	} while (*p++ && i<NAMSIZ);
216 
217 	/* check to see if line is being used */
218 	if( (etcutmp = open(_PATH_UTMP, 2)) < 0) {
219 		fprintf(stderr, "On open %s open: %s\n",
220 			_PATH_UTMP, sys_errlist[errno]);
221 		exit(1);
222 	}
223 
224 	(void)lseek(etcutmp, utmploc, 0);
225 
226 	i = read(etcutmp, (char *)&utmp, sizeof(struct utmp));
227 
228 	if(
229 		i == sizeof(struct utmp) &&
230 		utmp.ut_line[0] != '\0'  &&
231 		utmp.ut_name[0] != '\0'  &&
232 		(
233 			!upcase(utmp.ut_name, NAMSIZ) ||
234 			(
235 				uid != 0 &&
236 				strncmp(utmp.ut_name, Uname, NAMSIZ) != 0
237 			)
238 		)
239 	) {
240 		fprintf(stderr, "%s in use by %s\n", device, utmp.ut_name);
241 		exit(2);
242 	}
243 
244 #ifndef sequent
245 	/* Disable modem control */
246 	if (setmodem(device, DISABLE) < 0) {
247 		fprintf(stderr, "Unable to disable modem control\n");
248 		exit(1);
249 	}
250 #endif !sequent
251 
252 	if (enable) {
253 #ifdef sequent
254 		if (setmodem(device, ENABLE) < 0) {
255 			fprintf(stderr, "Cannot Enable modem control\n");
256 			(void)setmodem(device, i);
257 			exit(1);
258 		}
259 #endif sequent
260 #ifndef sequent
261 		if((devfile = open(device, 1)) < 0) {
262 			fprintf(stderr, "On open of %s: %s\n",
263 				device, sys_errlist[errno]);
264 			(void)setmodem(device, resetmodem);
265 			exit(1);
266 		}
267 		/* Try one last time to hang up */
268 		if (ioctl(devfile, (int)TIOCCDTR, (char *)0) < 0)
269 			fprintf(stderr, "On TIOCCDTR ioctl: %s\n",
270 				sys_errlist[errno]);
271 
272 		if (ioctl(devfile, (int)TIOCNXCL, (char *)0) < 0)
273 			fprintf(stderr,
274 			    "Cannot clear Exclusive Use on %s: %s\n",
275 				device, sys_errlist[errno]);
276 
277 		if (ioctl(devfile, (int)TIOCHPCL, (char *)0) < 0)
278 			fprintf(stderr,
279 			    "Cannot set hangup on close on %s: %s\n",
280 				device, sys_errlist[errno]);
281 
282 #endif !sequent
283 		i = resetmodem;
284 
285 #ifndef sequent
286 		if (setmodem(device, ENABLE) < 0) {
287 			fprintf(stderr, "Cannot Enable modem control\n");
288 			(void)setmodem(device, i);
289 			exit(1);
290 		}
291 #endif sequent
292 		resetmodem=i;
293 
294 		if (settys(ENABLE)) {
295 			fprintf(stderr, "%s already enabled\n", device);
296 		} else {
297 			pokeinit(device, Uname, enable);
298 		}
299 		post(device, "");
300 
301 	} else {
302 #if defined(TIOCMGET) && defined(SENSECARRIER)
303 		if (uid!=0) {
304 			int linestat = 0;
305 
306 			/* check for presence of carrier */
307 			sleep(2); /* need time after modem control turnoff */
308 
309 			if((devfile = open(device, 1)) < 0) {
310 				fprintf(stderr, "On open of %s: %s\n",
311 					device, sys_errlist[errno]);
312 				(void)setmodem(device, resetmodem);
313 				exit(1);
314 			}
315 
316 			(void)ioctl(devfile, TIOCMGET, &linestat);
317 
318 			if (linestat&TIOCM_CAR) {
319 				fprintf(stderr, "%s is in use (Carrier On)\n",
320 					device);
321 				(void)setmodem(device, resetmodem);
322 				exit(2);
323 			}
324 			(void)close(devfile);
325 		}
326 #endif TIOCMGET
327 		/* chown device */
328 		if(chown(device, uid, gid) < 0)
329 			fprintf(stderr, "Cannot chown %s: %s\n",
330 				device, sys_errlist[errno]);
331 
332 
333 		/* poke init */
334 		if(settys(DISABLE)) {
335 			fprintf(stderr, "%s already disabled\n", device);
336 		} else {
337 			pokeinit(device, Uname, enable);
338 		}
339 		post(device, Uname);
340 #ifdef sequent
341 	/* Disable modem control */
342 	if (setmodem(device, DISABLE) < 0) {
343 		fprintf(stderr, "Unable to disable modem control\n");
344 		exit(1);
345 	}
346 #endif sequent
347 		if((devfile = open(device, O_RDWR|O_NDELAY)) < 0) {
348 			fprintf(stderr, "On %s open: %s\n",
349 				device, sys_errlist[errno]);
350 		} else {
351 			if(ioctl(devfile, (int)TIOCSDTR, (char *)0) < 0)
352 				fprintf(stderr,
353 				    "Cannot set DTR on %s: %s\n",
354 					device, sys_errlist[errno]);
355 		}
356 	}
357 
358 	exit(0);
359 }
360 
361 /* return true if no lower case */
362 upcase(str, len)
363 register char *str;
364 register int len;
365 {
366 	for (; *str, --len >= 0 ; str++)
367 		if (*str>='a' && *str<='z')
368 			return(0);
369 	return(1);
370 }
371 
372 /* Post name to public */
373 post(device, name)
374 char *device, *name;
375 {
376 	(void)time((time_t *)&utmp.ut_time);
377 	strncpy(utmp.ut_line, device, LINSIZ);
378 	strncpy(utmp.ut_name, name,  NAMSIZ);
379 	if (lseek(etcutmp, utmploc, 0) < 0)
380 		fprintf(stderr, "on lseek in %s: %s",
381 			_PATH_UTMP, sys_errlist[errno]);
382 	if (write(etcutmp, (char *)&utmp, sizeof(utmp)) < 0)
383 		fprintf(stderr, "on write in %s: %s",
384 			_PATH_UTMP, sys_errlist[errno]);
385 }
386 
387 /* poke process 1 and wait for it to do its thing */
388 pokeinit(device, uname, enable)
389 char *uname, *device; int enable;
390 {
391 	struct utmp utmp;
392 	register int i;
393 
394 	post(device, uname);
395 
396 	/* poke init */
397 	if (kill(1, SIGHUP)) {
398 		fprintf(stderr,
399 		    "Cannot send hangup to init process: %s\n",
400 			sys_errlist[errno]);
401 		(void)settys(resettty);
402 		(void)setmodem(device, resetmodem);
403 		exit(1);
404 	}
405 
406 	if (enable)
407 		return;
408 
409 	/* wait till init has responded, clearing the utmp entry */
410 	i = 100;
411 	do {
412 		sleep(1);
413 		if (lseek(etcutmp, utmploc, 0) < 0)
414 			fprintf(stderr, "On lseek in %s: %s",
415 				_PATH_UTMP, sys_errlist[errno]);
416 		if (read(etcutmp, (char *)&utmp, sizeof utmp) < 0)
417 			fprintf(stderr, "On read from %s: %s",
418 				_PATH_UTMP, sys_errlist[errno]);
419 	} while (utmp.ut_name[0] != '\0' && --i > 0);
420 }
421 
422 #ifdef BSD4_3
423 /* identify terminal line in ttys */
424 opnttys(device)
425 char *device;
426 {
427 	register int  ndevice;
428 	register char *p;
429 	char *index();
430 	char linebuf[BUFSIZ];
431 
432 	ttysfile = NULL;
433 	do {
434 		if (ttysfile != NULL) {
435 			fclose(ttysfile);
436 			sleep(5);
437 		}
438 		ttysfile = fopen(Etcttys, "r");
439 		if(ttysfile == NULL) {
440 			fprintf(stderr, "Cannot open %s: %s\n", Etcttys,
441 				sys_errlist[errno]);
442 			exit(1);
443 		}
444 	} while (flock(fileno(ttysfile), LOCK_NB|LOCK_EX) < 0);
445 	nttysfile = fopen(NEtcttys, "w");
446 	if(nttysfile == NULL) {
447 		fprintf(stderr, "Cannot open %s: %s\n", Etcttys,
448 			sys_errlist[errno]);
449 		exit(1);
450 	}
451 
452 	ndevice = strlen(device);
453 #ifndef BRL4_2
454 	utmploc = sizeof(utmp);
455 #else BRL4_2
456 	utmploc = 0;
457 #endif BRL4_2
458 
459 	while(fgets(linebuf, sizeof(linebuf) - 1, ttysfile) != NULL) {
460 		if(strncmp(device, linebuf, ndevice) == 0)
461 			return;
462 		ttyslnbeg += strlen(linebuf);
463 		if (linebuf[0] != '#' && linebuf[0] != '\0')
464 			utmploc += sizeof(utmp);
465 		if (fputs(linebuf, nttysfile) == NULL) {
466 			fprintf(stderr, "On %s write: %s\n",
467 				Etcttys, sys_errlist[errno]);
468 			exit(1);
469 		}
470 
471 	}
472 	fprintf(stderr, "%s not found in %s\n", device, Etcttys);
473 	exit(1);
474 }
475 
476 /* modify appropriate line in _PATH_TTYS to turn on/off the device */
477 settys(enable)
478 int enable;
479 {
480 	register char *cp, *cp2;
481 	char lbuf[BUFSIZ];
482 	int i;
483 	char c1, c2;
484 
485 	(void) fseek(ttysfile, ttyslnbeg, 0);
486 	if(fgets(lbuf, BUFSIZ, ttysfile) == NULL) {
487 		fprintf(stderr, "On %s read: %s\n",
488 			Etcttys, sys_errlist[errno]);
489 		exit(1);
490 	}
491 	/* format is now */
492 	/* ttyd0 std.100 dialup on secure # comment */
493 	/* except, 2nd item may have embedded spaces inside quotes, Hubert */
494 	cp = lbuf;
495 	for (i=0;*cp && i<3;i++) {
496 		if (*cp == '"') {
497 			cp++;
498 			while (*cp && *cp != '"')
499 				cp++;
500 			if (*cp != '\0')
501 				cp++;
502 		}else {
503 			while (*cp && *cp != ' ' && *cp != '\t')
504 				cp++;
505 		}
506 		while (*cp && (*cp == ' ' || *cp == '\t'))
507 			cp++;
508 	}
509 	if (*cp == '\0') {
510 		fprintf(stderr,"Badly formatted line in %s:\n%s",
511 		    _PATH_TTYS, lbuf);
512 		exit(1);
513 	}
514 	c1 = *--cp;
515 	*cp++ = '\0';
516 	cp2 = cp;
517 	while (*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
518 		cp++;
519 	if (*cp == '\0') {
520 		fprintf(stderr,"Badly formatted line in %s:\n%s",
521 		    _PATH_TTYS, lbuf);
522 		exit(1);
523 	}
524 	c2 = *cp;
525 	*cp++ = '\0';
526 	while (*cp && (*cp == ' ' || *cp == '\t'))
527 		cp++;
528 	resettty = strcmp("on", cp2) != 0;
529 	fprintf(nttysfile,"%s%c%s%c%s", lbuf, c1, enable ? "on" : "off", c2, cp);
530 	if (ferror(nttysfile)) {
531 		fprintf(stderr, "On %s fprintf: %s\n",
532 			NEtcttys, sys_errlist[errno]);
533 		exit(1);
534 	}
535 	while(fgets(lbuf, sizeof(lbuf) - 1, ttysfile) != NULL) {
536 		if (fputs(lbuf, nttysfile) == NULL) {
537 			fprintf(stderr, "On %s write: %s\n",
538 				NEtcttys, sys_errlist[errno]);
539 			exit(1);
540 		}
541 	}
542 
543 	if (enable^resettty)
544 		(void) unlink(NEtcttys);
545 	else {
546 		struct stat statb;
547 		if (stat(Etcttys, &statb) == 0) {
548 			fchmod(fileno(nttysfile) ,statb.st_mode);
549 			fchown(fileno(nttysfile), statb.st_uid, statb.st_gid);
550 		}
551 		(void) rename(NEtcttys, Etcttys);
552 	}
553 	(void) fclose(nttysfile);
554 	(void) fclose(ttysfile);
555 	return enable^resettty;
556 }
557 
558 #else !BSD4_3
559 
560 /* identify terminal line in ttys */
561 opnttys(device)
562 char *device;
563 {
564 	register FILE *ttysfile;
565 	register int  ndevice, lnsiz;
566 	register char *p;
567 	char *index();
568 	char linebuf[BUFSIZ];
569 
570 	ttysfile = fopen(Etcttys, "r");
571 	if(ttysfile == NULL) {
572 		fprintf(stderr, "Cannot open %s: %s\n", Etcttys,
573 			sys_errlist[errno]);
574 		exit(1);
575 	}
576 
577 	ndevice = strlen(device);
578 	ttyslnbeg = 0;
579 	utmploc = 0;
580 
581 	while(fgets(linebuf, sizeof(linebuf) - 1, ttysfile) != NULL) {
582 		lnsiz = strlen(linebuf);
583 		if ((p = index(linebuf, '\n')) != NULL)
584 			*p = '\0';
585 		if(strncmp(device, &linebuf[2], ndevice) == 0) {
586 			(void)fclose(ttysfile);
587 #ifdef sequent
588 			/* Why is the sequent off by one? */
589 			utmploc += sizeof(utmp);
590 #endif sequent
591 			return;
592 		}
593 		ttyslnbeg += lnsiz;
594 		utmploc += sizeof(utmp);
595 	}
596 	fprintf(stderr, "%s not found in %s\n", device, Etcttys);
597 	exit(1);
598 }
599 
600 /* modify appropriate line in _PATH_TTYS to turn on/off the device */
601 settys(enable)
602 int enable;
603 {
604 	int ittysfil;
605 	char out, in;
606 
607 	ittysfil = open(Etcttys, 2);
608 	if(ittysfil < 0) {
609 		fprintf(stderr, "Cannot open %s for output: %s\n",
610 			Etcttys, sys_errlist[errno]);
611 		exit(1);
612 	}
613 	(void)lseek(ittysfil, ttyslnbeg, 0);
614 	if(read(ittysfil, &in, 1)<0) {
615 		fprintf(stderr, "On %s write: %s\n",
616 			Etcttys, sys_errlist[errno]);
617 		exit(1);
618 	}
619 	resettty = (in == '1');
620 	out = enable ? '1' : '0';
621 	(void)lseek(ittysfil, ttyslnbeg, 0);
622 	if(write(ittysfil, &out, 1)<0) {
623 		fprintf(stderr, "On %s write: %s\n",
624 			Etcttys, sys_errlist[errno]);
625 		exit(1);
626 	}
627 	(void)close(ittysfil);
628 	return(in==out);
629 }
630 #endif !BSD4_3
631 
632 #ifdef sequent
633 setmodem(ttyline, enable)
634 char *ttyline; int enable;
635 {
636 	char *sysbuf[BUFSIZ];
637 	sprintf(sysbuf,"/etc/ttyconfig /dev/%s -special %s", ttyline,
638 		enable ? "-carrier" : "-nocarrier");
639 	system(sysbuf);
640 }
641 #endif /* sequent */
642 #ifdef vax
643 /*
644  * Excerpted from (June 8, 1983 W.Sebok)
645  * > ttymodem.c - enable/disable modem control for tty lines.
646  * >
647  * > Knows about DZ11s and DH11/DM11s.
648  * > 23.3.83 - TS
649  * > modified to know about DMF's  (hasn't been tested) Nov 8, 1984 - WLS
650  */
651 
652 
653 setmodem(ttyline, enable)
654 char *ttyline; int enable;
655 {
656 	dev_t dev;
657 	int kmem;
658 	int unit, line, nlines, addr, tflags;
659 	int devtype=0;
660 	char cflags; short sflags;
661 #ifdef BSD4_2
662 	int flags;
663 #else
664 	short flags;
665 #endif
666 	struct uba_device *ubinfo;
667 	struct stat statb;
668 	struct cdevsw cdevsw;
669 
670 	if(nl[CDEVSW].n_type == 0) {
671 		fprintf(stderr, "No namelist.\n");
672 		return(-1);
673 	}
674 
675 	if((kmem = open(_PATH_KMEM, 2)) < 0) {
676 		fprintf(stderr, "%s open: %s\n", _PATH_KMEM,
677 		    sys_errlist[errno]);
678 		return(-1);
679 	}
680 
681 	if(stat(ttyline, &statb) < 0) {
682 		fprintf(stderr, "%s stat: %s\n", ttyline, sys_errlist[errno]);
683 		return(-1);
684 	}
685 
686 	if((statb.st_mode&S_IFMT) != S_IFCHR) {
687 		fprintf(stderr, "%s is not a character device.\n",ttyline);
688 		return(-1);
689 	}
690 
691 	dev = statb.st_rdev;
692 	(void)lseek(kmem,
693 		(off_t) &(((struct cdevsw *)NLVALUE(CDEVSW))[major(dev)]),0);
694 	(void)read(kmem, (char *) &cdevsw, sizeof cdevsw);
695 
696 	if((int)(cdevsw.d_open) == NLVALUE(DZOPEN)) {
697 		devtype = DZ11;
698 		unit = minor(dev) / NDZLINE;
699 		line = minor(dev) % NDZLINE;
700 		addr = (int) &(((int *)NLVALUE(DZINFO))[unit]);
701 		(void)lseek(kmem, (off_t) NLVALUE(NDZ11), 0);
702 	} else if((int)(cdevsw.d_open) == NLVALUE(DHOPEN)) {
703 		devtype = DH11;
704 		unit = minor(dev) / NDHLINE;
705 		line = minor(dev) % NDHLINE;
706 		addr = (int) &(((int *)NLVALUE(DHINFO))[unit]);
707 		(void)lseek(kmem, (off_t) NLVALUE(NDH11), 0);
708 	} else if((int)(cdevsw.d_open) == NLVALUE(DMFOPEN)) {
709 		devtype = DMF;
710 		unit = minor(dev) / NDMFLINE;
711 		line = minor(dev) % NDMFLINE;
712 		addr = (int) &(((int *)NLVALUE(DMFINFO))[unit]);
713 		(void)lseek(kmem, (off_t) NLVALUE(NDMF), 0);
714 	} else {
715 		fprintf(stderr, "Device %s (%d/%d) unknown.\n", ttyline,
716 		    major(dev), minor(dev));
717 		return(-1);
718 	}
719 
720 	(void)read(kmem, (char *) &nlines, sizeof nlines);
721 	if(minor(dev) >= nlines) {
722 		fprintf(stderr, "Sub-device %d does not exist (only %d).\n",
723 		    minor(dev), nlines);
724 		return(-1);
725 	}
726 
727 	(void)lseek(kmem, (off_t)addr, 0);
728 	(void)read(kmem, (char *) &ubinfo, sizeof ubinfo);
729 	(void)lseek(kmem, (off_t) &(ubinfo->ui_flags), 0);
730 	(void)read(kmem, (char *) &flags, sizeof flags);
731 
732 	tflags = 1<<line;
733 	resetmodem = ((flags&tflags) == 0);
734 	flags = enable ? (flags & ~tflags) : (flags | tflags);
735 	(void)lseek(kmem, (off_t) &(ubinfo->ui_flags), 0);
736 	(void)write(kmem, (char *) &flags, sizeof flags);
737 	switch(devtype) {
738 		case DZ11:
739 			if((addr = NLVALUE(DZSCAR)) == 0) {
740 				fprintf(stderr, "No dzsoftCAR.\n");
741 				return(-1);
742 			}
743 			cflags = flags;
744 			(void)lseek(kmem, (off_t) &(((char *)addr)[unit]), 0);
745 			(void)write(kmem, (char *) &cflags, sizeof cflags);
746 			break;
747 		case DH11:
748 			if((addr = NLVALUE(DHSCAR)) == 0) {
749 				fprintf(stderr, "No dhsoftCAR.\n");
750 				return(-1);
751 			}
752 			sflags = flags;
753 			(void)lseek(kmem, (off_t) &(((short *)addr)[unit]), 0);
754 			(void)write(kmem, (char *) &sflags, sizeof sflags);
755 			break;
756 		case DMF:
757 			if((addr = NLVALUE(DMFSCAR)) == 0) {
758 				fprintf(stderr, "No dmfsoftCAR.\n");
759 				return(-1);
760 			}
761 			cflags = flags;
762 			(void)lseek(kmem, (off_t) &(((char *)addr)[unit]), 0);
763 			(void)write(kmem, (char *) &cflags, sizeof cflags);
764 			break;
765 		default:
766 			fprintf(stderr, "Unknown device type\n");
767 			return(-1);
768 	}
769 	return(0);
770 }
771 #endif /* vax */
772 
773 prefix(s1, s2)
774 	register char *s1, *s2;
775 {
776 	register char c;
777 
778 	while ((c = *s1++) == *s2++)
779 		if (c == '\0')
780 			return (1);
781 	return (c == '\0');
782 }
783 #else	/* !DIALINOUT */
784 main()
785 {
786 	fprintf(stderr,"acucntrl is not supported on this system\n");
787 }
788 #endif /* !DIALINOUT */
789