BSD 4_3_Net_2 development
[unix-history] / usr / src / contrib / isode / vt / vtd.c
CommitLineData
9319b3c3
C
1/* vtd.c - VT responder */
2
3#ifndef lint
4static char *rcsid = "$Header: /f/osi/vt/RCS/vtd.c,v 7.3 91/02/22 09:48:28 mrose Interim $";
5#endif
6
7/*
8 * $Header: /f/osi/vt/RCS/vtd.c,v 7.3 91/02/22 09:48:28 mrose Interim $
9 *
10 *
11 * $Log: vtd.c,v $
12 * Revision 7.3 91/02/22 09:48:28 mrose
13 * Interim 6.8
14 *
15 * Revision 7.2 90/07/09 14:52:06 mrose
16 * sync
17 *
18 * Revision 7.1 89/11/30 23:51:42 mrose
19 * pa2str
20 *
21 * Revision 7.0 89/11/23 22:31:55 mrose
22 * Release 6.0
23 *
24 */
25
26/*
27 * NOTICE
28 *
29 * Acquisition, use, and distribution of this module and related
30 * materials are subject to the restrictions of a license agreement.
31 * Consult the Preface in the User's Manual for the full terms of
32 * this agreement.
33 *
34 */
35
36
37#undef MAP_BACKSPACE /*Map backspace character to VT ERASE CHAR*/
38
39#include <signal.h>
40#include "vtpm.h"
41#include "sector1.h"
42#include "tailor.h"
43#include <sys/ioctl.h>
44#ifdef BSD44
45#include <sys/termios.h>
46#include <sys/ttydefaults.h>
47#endif
48
49#ifndef _PATH_LOGIN
50#ifndef BSD44
51#define _PATH_LOGIN "/bin/login"
52#else
53#define _PATH_LOGIN "/usr/bin/login"
54#endif
55#endif
56
57#include <arpa/telnet.h>
58
59#include <ctype.h>
60#include <setjmp.h>
61#include <pwd.h>
62#include <varargs.h>
63
64#define BELL '\07'
65#ifndef SUNOS4
66#define BANNER "\r\n\r\n4.2 BSD UNIX (%s)\r\n\r\n\r%s"
67#else
68#define BANNER "\r\n\r\nSunOS UNIX (%s)\r\n\r\n\r%s"
69#endif
70
71int connected = FALSE;
72char command[256];
73
74
75/*
76 * I/O data buffers, pointers, and counters.
77 */
78char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
79char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
80int pcc;
81
82VT_PROFILE vtp_profile;
83int rflag = 0;
84char erase_char;
85char erase_line;
86char intr_char;
87char *crp = "\r";
88char *my_displayobj = "D"; /*Acceptor's Display Object Name*/
89char *my_echo_obj = "NA"; /*Acceptor's Negotiation Control Object Name*/
90char *my_signal_obj = "DI"; /*Acceptor's Signal Control Object*/
91char *his_echo_obj = "NI"; /*Initiator's Negotiation Control Object*/
92char *his_signal_obj = "KB"; /*Initiator/s Signal control Object*/
93int my_right = ACCEPTOR;
94char kb_image; /*KB Control Object image*/
95char di_image; /*DI Control Object Image*/
96char ni_image; /*NI Control Object image*/
97char na_image; /*NA Control Object image*/
98char nego_state; /*Current state of NA affected options*/
99char sync_image; /*SY Control Object image*/
100char ga_image;
101int transparent = 0; /*Flag for Transparent repertoire*/
102int telnet_profile =1;
103int do_break = 1; /*If VT-BREAK agreed to*/
104int showoptions = 0;
105int debug = 0;
106
107#ifdef BSD44
108struct termios oterm;
109#else
110struct tchars otc;
111struct ltchars oltc;
112struct sgttyb ottyb;
113#endif
114char *myhostname;
115char peerhost[BUFSIZ];
116struct PSAPaddr ts_bound;
117struct passwd *pwd;
118
119int pty, net;
120int inter;
121extern char **environ;
122extern int errno;
123char line[] = "/dev/ptyp0";
124char *envinit[] = { "TERM=network", 0 };
125SFD cleanup();
126static int do_cleaning = 0;
127
128char *myname;
129LLog _vt_log = {
130 "vt.log", NULLCP, NULLCP,
131 LLOG_FATAL | LLOG_EXCEPTIONS | LLOG_NOTICE, LLOG_FATAL, -1,
132 LLOGCLS | LLOGCRT | LLOGZER, NOTOK
133
134};
135LLog *vt_log = &_vt_log;
136
137main(argc, argv)
138 int argc;
139 char *argv[];
140{
141 int f = 0;
142 char *cp = line;
143 char *logname = NULLCP;
144 int i, p, t;
145 char j;
146#ifndef BSD44
147 struct sgttyb b;
148#endif
149
150 if (myname = rindex (*argv, '/'))
151 myname++;
152 if (myname == NULL || *myname == NULL)
153 myname = *argv;
154
155 isodetailor (myname, 0);
156 if (debug = isatty (fileno (stderr)))
157 ll_dbinit (vt_log, myname);
158 else
159 ll_hdinit (vt_log, myname);
160
161 for(i=1; i<(argc - 2); i++)
162 {
163 if (!strcmp(argv[i], "-d"))
164 {
165 if (!isdigit(argv[++i][0])
166 || sscanf(argv[i], "%d", &debug) != 1)
167 adios (NULLCP, "usage: %s -d 0-7", myname);
168 advise(LLOG_DEBUG,NULLCP, "setting debug level to %d", debug);
169 if (debug)
170 ll_dbinit (vt_log, myname);
171 }
172 else if(!strcmp(argv[i], "-F"))
173 {
174 if ((logname = argv[++i]) == NULL || *logname == '-')
175 adios (NULLCP, "usage: %s -F logfile", myname);
176 vt_log -> ll_file = logname;
177 (void) ll_close (vt_log);
178 advise(LLOG_DEBUG,NULLCP, "logging to %s",logname);
179 }
180 else
181 adios(NULLCP, "usage: %s [-F logfile] [-d N]",
182 myname);
183 }
184
185 advise (LLOG_NOTICE,NULLCP, "starting");
186
187 acc = &accs;
188 acr = &acrs;
189 aci = &acis;
190 acs = &acss;
191
192 if (ass_ind(argc,argv) == OK) {
193 connected = TRUE;
194 if( !strcmp(vtp_profile.profile_name,"default") )
195 telnet_profile = 0;
196 }
197 else
198 exit(1);
199#ifdef BSD44
200 na_image = 0;
201 nego_state = 0; /*Start off in Local echo*/
202 i = forkpty(&p, line, NULL, NULL);
203 if (i == -1) {
204 perror("vtd -- forkpty");
205 vrelreq();
206 /*NOTREACHED*/
207 }
208 if (i) {
209 vtd(sd, p);
210 /*NOTREACHED*/
211 }
212#else
213/*
214 * Get a pty, scan input lines.
215 */
216 for (j = 'p' ; j <= 't'; j++) {
217 cp[strlen ("/dev/pty")] = j;
218 for (i = 0; i < 16; i++) {
219 cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
220 p = open(cp, 2);
221 if (p >= 0)
222 goto gotpty;
223 }
224 }
225 perror("All network ports in use");
226 vrelreq();
227 /*NOTREACHED*/
228gotpty:
229
230 cp[strlen("/dev/")] = 't';
231 t = open("/dev/tty", 2);
232 if (t >= 0) {
233 if (ioctl(t, TIOCNOTTY, 0) == -1) {
234 perror("ioctl (TIOCNOTTY)");
235 }
236 (void)close(t);
237 }
238 t = open(cp, 2);
239 if (t < 0)
240 fatalperror(f, cp, errno);
241 if (ioctl(t, TIOCGETP, (char*)&b) == -1) {
242 perror("ioctl (TIOCGETP)");
243 adios(NULLCP, "ioctl failed (TIOCGETP)");
244 }
245 b.sg_flags = CRMOD|XTABS|ANYP;
246 if (ioctl(t, TIOCSETP, (char*)&b) == -1) {
247 perror("ioctl (TIOCSETP)");
248 adios(NULLCP, "ioctl failed (TIOCSETP)");
249 }
250 if (ioctl(p, TIOCGETP, (char*)&b) == -1) { /* XXX why is this done on the controller */
251 perror("ioctl (TIOCGETP)");
252 adios(NULLCP, "ioctl failed (TIOCGETP)");
253 }
254 if (telnet_profile)
255 b.sg_flags &= ~ECHO;
256 else
257 b.sg_flags |= ECHO; /*Remote echo for Default*/
258 if (ioctl(p, TIOCSETP, (char*)&b) == -1) {
259 perror("ioctl (TIOCSETP)");
260 adios(NULLCP, "ioctl failed (TIOCSETP)");
261 }
262 na_image = 0;
263 nego_state = 0; /*Start off in Local echo*/
264
265 if ((i = fork()) < 0)
266 fatalperror(f, "fork", errno);
267 if (i)
268 vtd(sd, p);
269 (void)close(sd);
270 (void)close(p);
271 if (dup2(t, 0) == -1) {
272 perror("dup2");
273 adios(NULLCP, "dup2 failed");
274 }
275 if (dup2(t, 1) == -1) {
276 perror("dup2");
277 adios(NULLCP, "dup2 failed");
278 }
279 if (dup2(t, 2) == -1) {
280 perror("dup2");
281 adios(NULLCP, "dup2 failed");
282 }
283 (void)close(t);
284#endif
285 environ = envinit;
286
287 execl(_PATH_LOGIN,"login","-h",peerhost,NULLCP);
288 fatalperror(f, _PATH_LOGIN, errno);
289 /*NOTREACHED*/
290}
291
292fatal(f, msg)
293 int f;
294 char *msg;
295{
296 char buf[BUFSIZ];
297
298 (void) sprintf(buf, "%s: %s.\n", myname, msg);
299 (void) write(f, buf, strlen(buf));
300 adios (NULLCP, msg);
301}
302
303fatalperror(f, msg, errnum)
304 int f;
305 char *msg;
306 int errnum;
307{
308 char buf[BUFSIZ];
309 extern char *sys_errlist[];
310
311 (void) sprintf(buf, "%s: %s", msg, sys_errlist[errnum]);
312 fatal(f, buf);
313}
314
315/*
316 * Main loop. Select from pty and network.
317 */
318
319vtd(f, p)
320{
321 int on = 1;
322 int nfds, result;
323
324 do_cleaning = 1;
325 net = f, pty = p;
326 nfds = (f > p ? f : p) + 1;
327
328 if (ioctl(p, FIONBIO, (char*)&on) == -1) {
329 perror("ioctl");
330 adios(NULLCP, "ioctl failed (FIONBIO)");
331 }
332#ifdef SIGTSTP
333 (void) signal(SIGTSTP, SIG_IGN);
334#endif
335#ifdef SIGCHLD
336 (void) signal(SIGCHLD, cleanup);
337#endif
338 /*
339 * Show banner that getty never gave.
340 */
341 myhostname = PLocalHostName ();
342 (void) sprintf(nfrontp, BANNER, myhostname, "");
343 nfrontp += strlen(nfrontp);
344
345#ifdef BSD44
346 if (tcgetattr(pty, &oterm) == -1) {
347 perror("tcgetattr");
348 adios(NULLCP, "tcgetattr failed");
349 }
350 if (telnet_profile) {
351 oterm.c_lflag &= ~ECHO;
352 if (tcsetattr(pty, TCSADRAIN, &oterm) == -1) {
353 perror("tcgetattr");
354 adios(NULLCP, "tcgetattr failed");
355 }
356 }
357 erase_char = oterm.c_cc[VERASE];
358 erase_line = oterm.c_cc[VKILL];
359 intr_char = oterm.c_cc[VINTR];
360#else
361 if (ioctl(pty,TIOCGETP,(char*)&ottyb) == -1) {
362 perror("ioctl");
363 adios(NULLCP, "ioctl failed (TIOCGETP)");
364 }
365 if (ioctl(pty,TIOCGETC,(char*)&otc) == -1) {
366 perror("ioctl");
367 adios(NULLCP, "ioctl failed (TIOCGETC)");
368 }
369 if (ioctl(pty,TIOCGLTC,(char*)&oltc) == -1) {
370 perror("ioctl");
371 adios(NULLCP, "ioctl failed (TIOCGLTC)");
372 }
373 erase_char = ottyb.sg_erase;
374 erase_line = ottyb.sg_kill;
375 intr_char = otc.t_intrc;
376#endif
377
378
379 for (;;) {
380 fd_set ibits, obits;
381 register int c;
382
383 FD_ZERO (&ibits);
384 FD_ZERO (&obits);
385 /*
386 * Never look for input if there's still
387 * stuff in the corresponding output buffer
388 */
389 if (nfrontp - nbackp) {
390 FD_SET (f, &obits);
391 }
392 else {
393 FD_SET (p, &ibits);
394 }
395 if (pfrontp - pbackp) {
396 FD_SET (p, &obits);
397 }
398 else {
399 FD_SET (f, &ibits);
400 }
401 if (FD_ISSET (f, &ibits) && data_pending()) {
402 FD_CLR (f, &ibits);
403
404 result = xselect(nfds, &ibits, &obits,
405 (fd_set *)NULL, OK);
406
407 if (result < 0)
408 adios("failed", "xselect");
409 FD_SET (f, &ibits);
410 }
411 else {
412 if (xselect(nfds, &ibits, &obits, (fd_set *)NULL,
413 NOTOK) == -1)
414 adios("failed", "xselect");
415 }
416 if (!FD_ISSET (f, &ibits)
417 && !FD_ISSET (p, &ibits)
418 && !FD_ISSET (f, &obits)
419 && !FD_ISSET (p, &obits)) {
420 sleep(5);
421 continue;
422 }
423
424 /*
425 * Something to read from the network...
426 */
427 if (FD_ISSET (f, &ibits)) {
428 while ((c = getch()) > 0)
429 *pfrontp++ = c;
430 }
431 if (c == E_EOF) {
432 break;
433 }
434
435 /*
436 * Something to read from the pty...
437 */
438 if (FD_ISSET (p, &ibits)) {
439 pcc = read(p, nfrontp, (&netobuf[BUFSIZ] - nfrontp));
440
441 if (pcc < 0 && errno == EWOULDBLOCK)
442 pcc = 0;
443 else {
444 if (pcc <= 0) {
445 if (debug)
446 advise(LLOG_EXCEPTIONS,NULLCP,
447 "problem reading from pty");
448 break;
449 }
450 }
451 nfrontp += pcc;
452 }
453
454 if (FD_ISSET (f, &obits) && (nfrontp - nbackp) > 0)
455 netflush();
456
457 if (FD_ISSET (p, &obits) && (pfrontp - pbackp) > 0)
458 ptyflush();
459 }
460 if (debug)
461 advise(LLOG_DEBUG,NULLCP, "finished loop in vtp");
462 cleanup();
463}
464
465/*
466 * Send interrupt to process on other side of pty.
467 * If it is in raw mode, just write NULL;
468 * otherwise, write intr char.
469 */
470interrupt()
471{
472#ifdef BSD44
473 struct termios term;
474
475 ptyflush();
476 if (tcgetattr(pty, &term) == -1) {
477 perror("tcgetattr");
478 return;
479 }
480 if ((term.c_lflag&ISIG) && term.c_cc[VINTR] != _POSIX_VDISABLE)
481 *pfrontp++ = term.c_cc[VINTR];
482 else
483 *pfrontp++ = '\0';
484#else
485 struct sgttyb b;
486 struct tchars tchars;
487
488 ptyflush(); /* half-hearted */
489 if (ioctl(pty, TIOCGETP, (char*)&b) == -1) {
490 perror("ioctl");
491 adios(NULLCP, "ioctl failed");
492 }
493 if (b.sg_flags & RAW) {
494 *pfrontp++ = '\0';
495 return;
496 }
497 *pfrontp++ = ioctl(pty, TIOCGETC, (char*)&tchars) < 0 ?
498 '\177' : tchars.t_intrc;
499#endif
500}
501
502netflush()
503{
504 register char *cp;
505 int n;
506 int i, j;
507 int nl_flag; /*Records if Newline is included in current PDU to
508 decide if Deliver Request should follow it. Should
509 not be required but some implementations may wait
510 for it before delivering NDQ to application*/
511
512 nl_flag = 0;
513 if ((n = nfrontp - nbackp) > 0) {
514
515 if (debug) {
516 (void) ll_log (vt_log, LLOG_DEBUG, NULLCP,
517 ("writing to the net"));
518 (void) ll_printf (vt_log, "<<");
519 for(i=0; i<(nfrontp-nbackp); i++)
520 (void)ll_printf (vt_log, "%02x ",*(nbackp+i));
521 (void)ll_printf (vt_log, ">>\n");
522 (void)ll_sync (vt_log);
523 }
524 if(transparent)
525 {
526 (void)vt_text(nbackp,n);
527 vtsend();
528 cp = nbackp;
529 for(i=0; i<n; i++)
530 {
531 if((*cp == '\r') ||
532 (*cp == '\n'))
533 {
534 vdelreq(FALSE);
535 break;
536 }
537 ++cp;
538 }
539 nbackp += n;
540 }
541 else
542 {
543 cp = nbackp;
544 for(i=0,j=0; i<n; i++)
545 {
546 if(*cp == '\r')
547 {
548 if(rflag) (void)vt_text(crp,1);
549 /*Previous char was CR so put one in NDQ*/
550 if(j)
551 (void)vt_text(nbackp,j);
552 nbackp += (j+1); /*Skip over current CR*/
553 cp = nbackp;
554 j = 0;
555 if(i == (n-1) ) (void)vt_text(crp,1);
556 /*If CR is last char in buffer, send it*/
557 else rflag = 1;
558 /*If not last char in buffer, read next one*/
559 continue;
560 }
561 else if(rflag) /*If previous character was CR*/
562 {
563 if(*cp == '\n') /*Got CR-LF -- map to Next X-Array*/
564 {
565 nbackp += (j+1);
566 cp = nbackp;
567 rflag = 0;
568 vt_newline();
569 ++nl_flag;
570 continue;
571 }
572 else /*Preceeding char was CR but not followed by
573 LF. Put CR in buffer*/
574 (void) vt_text(crp,1);
575 rflag = 0;
576 }
577 if(telnet_profile)
578 {
579 rflag = 0;
580#ifdef MAP_BACKSPACE
581 if(*cp == 0x08) /*If believed to be erase*/
582 {
583 if(j)
584 (void)vt_text(nbackp,j);
585 nbackp += (j+1);
586 cp = nbackp;
587 j = 0;
588 vt_char_erase();
589 continue;
590 }
591#endif
592 if(!vtp_profile.arg_val.tel_arg_list.full_ascii)
593 /*If ASCII GO, dump ctrl chars*/
594 {
595 if((*cp < 0x20) || (*cp > 0x7e))
596 {
597 if(j)
598 (void)vt_text(nbackp,j);
599 nbackp += (j+1);
600 cp = nbackp;
601 j = 0;
602 }
603 else
604 {
605 ++j; ++cp;
606 }
607 }
608 else
609 {
610 ++j;
611 ++cp;
612 }
613 }
614 else /*Else Default Profile*/
615 {
616 if((*cp < 0x20) || (*cp > 0x7e))
617 {
618 if(j)
619 (void)vt_text(nbackp,j);
620 nbackp += (j+1);
621 cp = nbackp;
622 j = 0;
623 }
624 else
625 {
626 ++j;
627 ++cp;
628 }
629 }
630 } /*End for loop*/
631 if(j)
632 (void)vt_text(nbackp,j); /*Load anything left if CR or LF
633 wasn't last char in buffer*/
634 nbackp += j;
635 vtsend(); /*Send the whole NDQ*/
636 if(nl_flag && telnet_profile) vdelreq(FALSE);
637 }
638 }
639 if (n < 0) {
640 if (errno != ENOBUFS && errno != EWOULDBLOCK) {
641 adios("closed", "association");
642 /*NOTREACHED*/
643 }
644 n = 0;
645 }
646 if (nbackp == nfrontp)
647 nbackp = nfrontp = netobuf;
648}
649
650SFD cleanup()
651{
652 sleep(1);
653 while(getch() > 0); /*Clean out unread VT-DATA PDU's still held
654 in network. Kludge to overcome deficiency
655 in Session Release. */
656 rmut();
657#ifndef BSD44
658 vhangup();
659#endif
660 vrelreq();
661 (void)kill(0, SIGKILL);
662 exit(1);
663}
664
665#include <utmp.h>
666
667struct utmp wtmp;
668char wtmpf[] = "/usr/adm/wtmp";
669char utmp[] = "/etc/utmp";
670#define SCPYN(a, b) strncpy(a, b, sizeof (a))
671#define SCMPN(a, b) strncmp(a, b, sizeof (a))
672
673long lseek (), time ();
674
675rmut()
676{
677 register f;
678 int found = 0;
679
680 f = open(utmp, 2);
681 if (f >= 0) {
682 while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
683 if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
684 continue;
685 (void)lseek(f, -(long)sizeof (wtmp), 1);
686 (void)SCPYN(wtmp.ut_name, "");
687#if !defined(SYS5) && !defined(bsd43_ut_host)
688 (void)SCPYN(wtmp.ut_host, "");
689#endif
690 (void)time(&wtmp.ut_time);
691 (void) write(f, (char *)&wtmp, sizeof (wtmp));
692 found++;
693 }
694 (void)close(f);
695 }
696 if (found) {
697 f = open(wtmpf, 1);
698 if (f >= 0) {
699 (void)SCPYN(wtmp.ut_line, line+5);
700 (void)SCPYN(wtmp.ut_name, "");
701#if !defined(SYS5) && !defined(bsd43_ut_host)
702 (void)SCPYN(wtmp.ut_host, "");
703#endif
704 (void)time(&wtmp.ut_time);
705 (void)lseek(f, (long)0, 2);
706 (void) write(f, (char *)&wtmp, sizeof (wtmp));
707 (void)close(f);
708 }
709 }
710 (void)chmod(line, 0666);
711 (void)chown(line, 0, 0);
712 line[strlen("/dev/")] = 'p';
713 (void)chmod(line, 0666);
714 (void)chown(line, 0, 0);
715}
716
717bye()
718{
719 if(do_cleaning) {
720 rmut();
721 (void)kill(0, SIGKILL);
722 }
723 exit(0);
724}
725
726flushbufs()
727{
728 pcc = 0;
729 pfrontp = pbackp = ptyobuf;
730 nfrontp = nbackp = netobuf;
731 while (getch() > 0)
732 continue;
733}
734
735/* \f ERRORS */
736
737void finalbye ()
738{
739 bye ();
740}
741
742
743#ifndef lint
744void adios (va_alist)
745va_dcl
746{
747 va_list ap;
748
749 va_start (ap);
750
751 (void) _ll_log (vt_log, LLOG_FATAL, ap);
752
753 va_end (ap);
754
755 bye ();
756
757 _exit (1);
758}
759#else
760/* VARARGS2 */
761
762void adios (what, fmt)
763char *what,
764 *fmt;
765{
766 adios (what, fmt);
767}
768#endif
769
770
771#ifndef lint
772void advise (va_alist)
773va_dcl
774{
775 int code;
776 va_list ap;
777
778 va_start (ap);
779
780 code = va_arg (ap, int);
781
782 (void) _ll_log (vt_log, code, ap);
783
784 va_end (ap);
785}
786#else
787/* VARARGS3 */
788
789void advise (code, what, fmt)
790int code;
791char *what,
792 *fmt;
793{
794 advise (code, what, fmt);
795}
796#endif
797
798ptyflush()
799{
800 int n;
801
802 if ((n = pfrontp - pbackp) > 0)
803 {
804 n = write(pty, pbackp, n);
805 }
806 if (n < 0)
807 return;
808 pbackp += n;
809 if (pbackp == pfrontp)
810 pbackp = pfrontp = ptyobuf;
811}
812
813#ifdef BSD44
814ptyecho(on)
815{
816 struct termios term;
817
818 ptyflush();
819 if (tcgetattr(pty, &term) == -1) {
820 perror("tcgetattr");
821 return;
822 }
823 if (on)
824 term.c_lflag |= ECHO;
825 else
826 term.c_lflag &= ECHO;
827 if (tcsetattr(pty, TCSAFLUSH, &term) == -1) {
828 perror("tcsetattr");
829 return;
830 }
831}
832#else
833setmode(on, off)
834 int on, off;
835{
836 struct sgttyb b;
837
838 ptyflush();
839 if(ioctl(pty, TIOCGETP, (char*)&b) < 0) {
840 perror("ioctl");
841 adios(NULLCP, "ioctl failed");
842 }
843 b.sg_flags |= on;
844 b.sg_flags &= ~off;
845 if (ioctl(pty, TIOCSETP, (char*)&b) == -1) {
846 perror("ioctl");
847 adios(NULLCP, "ioctl failed");
848 }
849}
850#endif