* Copyright (c) 1989 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
static char sccsid
[] = "@(#)sys_term.c 5.1 (Berkeley) %G%";
char wtmpf
[] = "/usr/adm/wtmp";
char utmpf
[] = "/etc/utmp";
char wtmpf
[] = "/etc/wtmp";
#define SCPYN(a, b) (void) strncpy(a, b, sizeof(a))
#define SCMPN(a, b) strncmp(a, b, sizeof(a))
struct termios termbuf
, termbuf2
; /* pty control structure */
* These three routines are used to get and set the "termbuf" structure
* to and from the kernel. init_termbuf() gets the current settings.
* copy_termbuf() hands in a new "termbuf" to write to the kernel, and
* set_termbuf() writes the structure into the kernel.
(void) ioctl(pty
, TIOCGETP
, (char *)&termbuf
.sg
);
(void) ioctl(pty
, TIOCGETC
, (char *)&termbuf
.tc
);
(void) ioctl(pty
, TIOCGLTC
, (char *)&termbuf
.ltc
);
(void) ioctl(pty
, TIOCGSTATE
, (char *)&termbuf
.state
);
(void) ioctl(pty
, TCGETA
, (char *)&termbuf
);
#if defined(LINEMODE) && defined(TIOCPKT_IOCTL)
if (len
> sizeof(termbuf
))
bcopy(cp
, (char *)&termbuf
, len
);
#endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */
* Only make the necessary changes.
if (bcmp((char *)&termbuf
.sg
, (char *)&termbuf2
.sg
, sizeof(termbuf
.sg
)))
(void) ioctl(pty
, TIOCSETP
, (char *)&termbuf
.sg
);
if (bcmp((char *)&termbuf
.tc
, (char *)&termbuf2
.tc
, sizeof(termbuf
.tc
)))
(void) ioctl(pty
, TIOCSETC
, (char *)&termbuf
.tc
);
if (bcmp((char *)&termbuf
.ltc
, (char *)&termbuf2
.ltc
,
(void) ioctl(pty
, TIOCSLTC
, (char *)&termbuf
.ltc
);
if (termbuf
.lflags
!= termbuf2
.lflags
)
(void) ioctl(pty
, TIOCLSET
, (char *)&termbuf
.lflags
);
if (bcmp((char *)&termbuf
, (char *)&termbuf2
, sizeof(termbuf
)))
(void) ioctl(pty
, TCSETA
, (char *)&termbuf
);
* spcset(func, valp, valpp)
* This function takes various special characters (func), and
* sets *valp to the current value of that character, and
* *valpp to point to where in the "termbuf" structure that
* It returns the SLC_ level of support for this function.
spcset(func
, valp
, valpp
)
*valp
= termbuf
.tc
.t_eofc
;
*valpp
= (unsigned char *)&termbuf
.tc
.t_eofc
;
*valp
= termbuf
.sg
.sg_erase
;
*valpp
= (unsigned char *)&termbuf
.sg
.sg_erase
;
*valp
= termbuf
.sg
.sg_kill
;
*valpp
= (unsigned char *)&termbuf
.sg
.sg_kill
;
*valp
= termbuf
.tc
.t_intrc
;
*valpp
= (unsigned char *)&termbuf
.tc
.t_intrc
;
return(SLC_VARIABLE
|SLC_FLUSHIN
|SLC_FLUSHOUT
);
*valp
= termbuf
.tc
.t_quitc
;
*valpp
= (unsigned char *)&termbuf
.tc
.t_quitc
;
return(SLC_VARIABLE
|SLC_FLUSHIN
|SLC_FLUSHOUT
);
*valp
= termbuf
.tc
.t_startc
;
*valpp
= (unsigned char *)&termbuf
.tc
.t_startc
;
*valp
= termbuf
.tc
.t_stopc
;
*valpp
= (unsigned char *)&termbuf
.tc
.t_stopc
;
*valp
= termbuf
.ltc
.t_flushc
;
*valpp
= (unsigned char *)&termbuf
.ltc
.t_flushc
;
*valp
= termbuf
.ltc
.t_suspc
;
*valpp
= (unsigned char *)&termbuf
.ltc
.t_suspc
;
*valp
= termbuf
.ltc
.t_werasc
;
*valpp
= (unsigned char *)&termbuf
.ltc
.t_werasc
;
*valp
= termbuf
.ltc
.t_rprntc
;
*valpp
= (unsigned char *)&termbuf
.ltc
.t_rprntc
;
*valp
= termbuf
.ltc
.t_lnextc
;
*valpp
= (unsigned char *)&termbuf
.ltc
.t_lnextc
;
spcset(func
, valp
, valpp
)
*valp
= termbuf
.c_cc
[VEOF
];
*valpp
= &termbuf
.c_cc
[VEOF
];
*valp
= termbuf
.c_cc
[VERASE
];
*valpp
= &termbuf
.c_cc
[VERASE
];
*valp
= termbuf
.c_cc
[VKILL
];
*valpp
= &termbuf
.c_cc
[VKILL
];
*valp
= termbuf
.c_cc
[VINTR
];
*valpp
= &termbuf
.c_cc
[VINTR
];
return(SLC_VARIABLE
|SLC_FLUSHIN
|SLC_FLUSHOUT
);
*valp
= termbuf
.c_cc
[VQUIT
];
*valpp
= &termbuf
.c_cc
[VQUIT
];
return(SLC_VARIABLE
|SLC_FLUSHIN
|SLC_FLUSHOUT
);
* Allocate a pty. As a side effect, the external character
* array "line" contains the name of the slave side.
* Returns the file descriptor of the opened pty.
char *line
= "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
register char c
, *p1
, *p2
;
(void) sprintf(line
, "/dev/ptyXX");
for (c
= 'p'; c
<= 's'; c
++) {
if (stat(line
, &stb
) < 0)
for (i
= 0; i
< 16; i
++) {
*p2
= "0123456789abcdef"[i
];
for (npty
= lowpty
; npty
<= highpty
; npty
++) {
(void) sprintf(line
, "/dev/pty/%03d", npty
);
(void) sprintf(line
, "/dev/ttyp%03d", npty
);
if (access(line
, 6) == 0)
/* no tty side to pty so skip it */
* tty_flowmode() Find out if flow control is enabled or disabled.
* tty_linemode() Find out if linemode (external processing) is enabled.
* tty_setlinemod(on) Turn on/off linemode.
* tty_isecho() Find out if echoing is turned on.
* tty_setecho(on) Enable/disable character echoing.
* tty_israw() Find out if terminal is in RAW mode.
* tty_binaryin(on) Turn on/off BINARY on input.
* tty_binaryout(on) Turn on/off BINARY on output.
* tty_isediting() Find out if line editing is enabled.
* tty_istrapsig() Find out if signal trapping is enabled.
* tty_setedit(on) Turn on/off line editing.
* tty_setsig(on) Turn on/off signal trapping.
* tty_tspeed(val) Set transmit speed to val.
* tty_rspeed(val) Set receive speed to val.
return((termbuf
.tc
.t_startc
) > 0 && (termbuf
.tc
.t_stopc
) > 0);
return(termbuf
.c_iflag
& IXON
? 1 : 0);
return(termbuf
.state
& TS_EXTPROC
);
return(termbuf
.c_lflag
& EXTPROC
);
(void) ioctl(pty
, TIOCEXT
, (char *)&on
);
termbuf
.c_lflag
|= EXTPROC
;
termbuf
.c_lflag
&= ~EXTPROC
;
return (termbuf
.sg
.sg_flags
& ECHO
);
return (termbuf
.c_lflag
& ECHO
);
termbuf
.sg
.sg_flags
|= ECHO
|CRMOD
;
termbuf
.sg
.sg_flags
&= ~(ECHO
|CRMOD
);
termbuf
.c_lflag
&= ~ECHO
;
#if defined(LINEMODE) && defined(KLUDGELINEMODE)
return(termbuf
.sg
.sg_flags
& RAW
);
return(!(termbuf
.c_lflag
& ICANON
));
#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
termbuf
.lflags
|= LPASS8
;
termbuf
.lflags
&= ~LPASS8
;
termbuf
.c_lflag
&= ~ISTRIP
;
termbuf
.c_lflag
|= ISTRIP
;
termbuf
.lflags
|= LLITOUT
;
termbuf
.lflags
&= ~LLITOUT
;
termbuf
.c_cflag
&= ~(CSIZE
|PARENB
);
termbuf
.c_oflag
&= ~OPOST
;
termbuf
.c_cflag
&= ~CSIZE
;
termbuf
.c_cflag
|= CS7
|PARENB
;
termbuf
.c_oflag
|= OPOST
;
return(termbuf
.lflags
& LPASS8
);
return(!(termbuf
.c_lflag
& ISTRIP
));
return(termbuf
.lflags
& LLITOUT
);
return(mywants
[TELOPT_BINARY
] == OPT_YES
);
return(!(termbuf
.sg
.sg_flags
& (CBREAK
|RAW
)));
return(termbuf
.c_lflag
& ICANON
);
return(!(termbuf
.sg
.sg_flags
&RAW
));
return(termbuf
.c_lflag
& ISIG
);
termbuf
.sg
.sg_flags
&= ~CBREAK
;
termbuf
.sg
.sg_flags
|= CBREAK
;
termbuf
.c_lflag
|= ICANON
;
termbuf
.c_lflag
&= ~ICANON
;
termbuf
.c_lflag
&= ~ISIG
;
* A table of available terminal speeds
{ 0, B0
}, { 50, B50
}, { 75, B75
},
{ 110, B110
}, { 134, B134
}, { 150, B150
},
{ 200, B200
}, { 300, B300
}, { 600, B600
},
{ 1200, B1200
}, { 1800, B1800
}, { 2400, B2400
},
{ 4800, B4800
}, { 9600, B9600
}, { 19200, B9600
},
{ 38400, B9600
}, { -1, B9600
}
register struct termspeeds
*tp
;
for (tp
= termspeeds
; (tp
->speed
!= -1) && (val
> tp
->speed
); tp
++)
termbuf
.sg
.sg_ospeed
= tp
->value
;
termbuf
.c_cflag
&= ~CBAUD
;
termbuf
.c_cflag
|= tp
->value
;
termbuf
.c_ospeed
= tp
->value
;
register struct termspeeds
*tp
;
for (tp
= termspeeds
; (tp
->speed
!= -1) && (val
> tp
->speed
); tp
++)
termbuf
.sg
.sg_ispeed
= tp
->value
;
termbuf
.c_cflag
&= ~CBAUD
;
termbuf
.c_cflag
|= tp
->value
;
termbuf
.c_ispeed
= tp
->value
;
return((termbuf
.c_oflag
& OPOST
) && (termbuf
.c_oflag
& ONLCR
) &&
!(termbuf
.c_oflag
& ONLRET
));
* Open the slave side of the pty, and do any initialization
* that is necessary. The return value is a file descriptor
* Disassociate self from control terminal and open ttyp side.
* Set important flags on ttyp and ptyp.
t
= open(_PATH_TTY
, O_RDWR
);
(void) ioctl(t
, TIOCNOTTY
, (char *)0);
(void) signal(SIGHUP
, SIG_IGN
);
(void) signal(SIGHUP
, SIG_DFL
);
termbuf
.sg
.sg_flags
|= CRMOD
|ANYP
|ECHO
;
termbuf
.sg
.sg_ospeed
= termbuf
.sg
.sg_ispeed
= B9600
;
termbuf
.c_oflag
|= ONLCR
|OXTABS
;
termbuf
.c_iflag
|= ICRNL
;
termbuf
.c_iflag
&= ~IXOFF
;
termbuf
.sg
.sg_ospeed
= termbuf
.sg
.sg_ispeed
= B9600
;
termbuf
.c_ospeed
= termbuf
.c_ispeed
= B9600
;
(void) chown(line
, 0, 0);
(void) chmod(line
, 0600);
* Given a file descriptor (t) for a tty, and a hostname, do whatever
* is necessary to startup the login process on the slave side of the pty.
fatalperror(net
, "fork");
* Cray parent will create utmp entry for child and send
* signal to child to tell when done. Child waits for signal
* before doing anything important.
(void) signal(SIGUSR1
, func
); /* reset handler to default */
* Create utmp entry for child
(void) time(&wtmp
.ut_time
);
wtmp
.ut_type
= LOGIN_PROCESS
;
SCPYN(wtmp
.ut_user
, "LOGIN");
SCPYN(wtmp
.ut_host
, host
);
SCPYN(wtmp
.ut_line
, line
+ sizeof("/dev/") - 1);
SCPYN(wtmp
.ut_id
, wtmp
.ut_line
+3);
if ((i
= open(wtmpf
, O_WRONLY
|O_APPEND
)) >= 0) {
(void) write(i
, (char *)&wtmp
, sizeof(struct utmp
));
struct init_request request
;
* Init will start up login process if we ask nicely. We only wait
* for it to start up and begin normal telnet operation.
if ((i
= open(INIT_FIFO
, O_WRONLY
)) < 0) {
(void) sprintf(tbuf
, "Can't open %s\n", INIT_FIFO
);
memset((char *)&request
, 0, sizeof(request
));
request
.magic
= INIT_MAGIC
;
SCPYN(request
.gen_id
, gen_id
);
SCPYN(request
.tty_id
, &line
[8]);
SCPYN(request
.host
, host
);
SCPYN(request
.term_type
, &terminaltype
[5]);
if (write(i
, (char *)&request
, sizeof(request
)) < 0) {
(void) sprintf(tbuf
, "Can't write to %s\n", INIT_FIFO
);
(void) signal(SIGALRM
, nologinproc
);
n
= read(pty
, ptyip
, BUFSIZ
);
if (i
== 3 || n
>= 0 || !gotalarm
)
(void) write(net
, "telnetd: waiting for /etc/init to start login process.\r\n", 56);
fatal(net
, "/etc/init didn't start login process");
(void) signal(SIGALRM
, SIG_DFL
);
* Set tab expansion the way we like, in case init did something
termbuf
.c_oflag
&= ~TABDLY
;
* Assuming that we are now running as a child processes, this
* function will turn us into the login process.
t
= open(line
, 2); /* open ttyp */
* Hangup anybody else using this ttyp, then reopen it for
(void) chown(line
, 0, 0);
(void) chmod(line
, 0600);
(void) signal(SIGHUP
, SIG_IGN
);
(void) ioctl(t
, TCVHUP
, (char *)0);
(void) signal(SIGHUP
, SIG_DFL
);
* set ttyp modes as we like them to be
termbuf
.c_oflag
= OPOST
|ONLCR
;
termbuf
.c_iflag
= IGNPAR
|ISTRIP
|ICRNL
|IXON
;
termbuf
.c_lflag
= ISIG
|ICANON
|ECHO
|ECHOE
|ECHOK
;
termbuf
.c_cflag
= EXTB
|HUPCL
|CS8
;
* set up standard paths before forking to login
fatalperror(net
, "setsid");
if (ioctl(t
, TIOCSCTTY
, (char *)0) < 0)
fatalperror(net
, "ioctl(sctty)");
if (*envp
= getenv("TZ"))
* -h : pass on name of host.
* WARNING: -h is accepted by login if and only if
* -p : don't clobber the environment (so terminal type stays set).
execl(_PATH_LOGIN
, "login", "-h", host
,
syslog(LOG_ERR
, "%s: %m\n", _PATH_LOGIN
);
fatalperror(net
, _PATH_LOGIN
);
* This is the routine to call when we are all through, to
* clean up anything that needs to be cleaned up.
p
= line
+ sizeof("/dev/") - 1;
#if defined(CRAY) && !defined(NEWINIT)
* These three functions are used to coordinate the handling of
* the utmp file between the server and the soon-to-be-login shell.
* The server actually creates the utmp structure, the child calls
* utmp_sig_wait(), until the server calls utmp_sig_notify() and
* signals the future-login shell to proceed.
static int caught
=0; /* NZ when signal intercepted */
static void (*func
)(); /* address of previous handler */
(void) signal(SIGUSR1
, func
);
* register signal handler for UTMP creation
if ((int)(func
= signal(SIGUSR1
, _utmp_sig_rcv
)) == -1)
fatalperror(net
, "telnetd/signal");
* Wait for parent to write our utmp entry.
pause(); /* wait until we get a signal (sigon) */
sigoff(); /* turn off signals while we check caught */
sigon(); /* turn on signals again */
#endif /* defined(CRAY) && !defined(NEWINIT) */
* This is the function called by cleanup() to
* remove the utmp entry for this person.
#if !defined(CRAY) && BSD <= 43
(void) fstat(f
, &statbf
);
utmp
= (struct utmp
*)malloc((unsigned)statbf
.st_size
);
syslog(LOG_ERR
, "utmp malloc failed");
if (statbf
.st_size
&& utmp
) {
nutmp
= read(f
, (char *)utmp
, (int)statbf
.st_size
);
nutmp
/= sizeof(struct utmp
);
for (u
= utmp
; u
< &utmp
[nutmp
] ; u
++) {
if (SCMPN(u
->ut_line
, line
+5) ||
(void) lseek(f
, ((long)u
)-((long)utmp
), L_SET
);
(void) time(&u
->ut_time
);
(void) write(f
, (char *)u
, sizeof(wtmp
));
f
= open(wtmpf
, O_WRONLY
|O_APPEND
);
SCPYN(wtmp
.ut_line
, line
+5);
(void) time(&wtmp
.ut_time
);
(void) write(f
, (char *)&wtmp
, sizeof(wtmp
));
(void) chmod(line
, 0666);
(void) chown(line
, 0, 0);
line
[strlen("/dev/")] = 'p';
(void) chmod(line
, 0666);
(void) chown(line
, 0, 0);