xref: /netbsd-src/libexec/telnetd/sys_term.c (revision a93ea220fcb3e34cdfdcd4d7a5d391e0b2b4f2ba)
1 /*	$NetBSD: sys_term.c,v 1.38 2003/07/14 16:23:40 itojun Exp $	*/
2 
3 /*
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)sys_term.c	8.4+1 (Berkeley) 5/30/95";
40 #else
41 __RCSID("$NetBSD: sys_term.c,v 1.38 2003/07/14 16:23:40 itojun Exp $");
42 #endif
43 #endif /* not lint */
44 
45 #include "telnetd.h"
46 #include "pathnames.h"
47 
48 #include <util.h>
49 
50 #include <sys/cdefs.h>
51 
52 #include <utmp.h>
53 struct	utmp wtmp;
54 
55 #define SCPYN(a, b)	(void) strncpy(a, b, sizeof(a))
56 #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
57 
58 struct termios termbuf, termbuf2;	/* pty control structure */
59 
60 void getptyslave __P((void));
61 int cleanopen __P((char *));
62 char **addarg __P((char **, char *));
63 void scrub_env __P((void));
64 int getent __P((char *, char *));
65 char *getstr __P((const char *, char **));
66 #ifdef KRB5
67 extern void kerberos5_cleanup __P((void));
68 #endif
69 
70 /*
71  * init_termbuf()
72  * copy_termbuf(cp)
73  * set_termbuf()
74  *
75  * These three routines are used to get and set the "termbuf" structure
76  * to and from the kernel.  init_termbuf() gets the current settings.
77  * copy_termbuf() hands in a new "termbuf" to write to the kernel, and
78  * set_termbuf() writes the structure into the kernel.
79  */
80 
81 void
82 init_termbuf()
83 {
84 	(void) tcgetattr(pty, &termbuf);
85 	termbuf2 = termbuf;
86 }
87 
88 #if	defined(LINEMODE) && defined(TIOCPKT_IOCTL)
89 void
90 copy_termbuf(cp, len)
91 	char *cp;
92 	int len;
93 {
94 	if (len > sizeof(termbuf))
95 		len = sizeof(termbuf);
96 	memmove((char *)&termbuf, cp, len);
97 	termbuf2 = termbuf;
98 }
99 #endif	/* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */
100 
101 void
102 set_termbuf()
103 {
104 	/*
105 	 * Only make the necessary changes.
106 	 */
107 	if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf)))
108 		(void) tcsetattr(pty, TCSANOW, &termbuf);
109 }
110 
111 
112 /*
113  * spcset(func, valp, valpp)
114  *
115  * This function takes various special characters (func), and
116  * sets *valp to the current value of that character, and
117  * *valpp to point to where in the "termbuf" structure that
118  * value is kept.
119  *
120  * It returns the SLC_ level of support for this function.
121  */
122 
123 
124 int
125 spcset(func, valp, valpp)
126 	int func;
127 	cc_t *valp;
128 	cc_t **valpp;
129 {
130 
131 #define	setval(a, b)	*valp = termbuf.c_cc[a]; \
132 			*valpp = &termbuf.c_cc[a]; \
133 			return(b);
134 #define	defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT);
135 
136 	switch(func) {
137 	case SLC_EOF:
138 		setval(VEOF, SLC_VARIABLE);
139 	case SLC_EC:
140 		setval(VERASE, SLC_VARIABLE);
141 	case SLC_EL:
142 		setval(VKILL, SLC_VARIABLE);
143 	case SLC_IP:
144 		setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
145 	case SLC_ABORT:
146 		setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
147 	case SLC_XON:
148 		setval(VSTART, SLC_VARIABLE);
149 	case SLC_XOFF:
150 		setval(VSTOP, SLC_VARIABLE);
151 	case SLC_EW:
152 		setval(VWERASE, SLC_VARIABLE);
153 	case SLC_RP:
154 		setval(VREPRINT, SLC_VARIABLE);
155 	case SLC_LNEXT:
156 		setval(VLNEXT, SLC_VARIABLE);
157 	case SLC_AO:
158 		setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT);
159 	case SLC_SUSP:
160 		setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN);
161 	case SLC_FORW1:
162 		setval(VEOL, SLC_VARIABLE);
163 	case SLC_FORW2:
164 		setval(VEOL2, SLC_VARIABLE);
165 	case SLC_AYT:
166 		setval(VSTATUS, SLC_VARIABLE);
167 
168 	case SLC_BRK:
169 	case SLC_SYNCH:
170 	case SLC_EOR:
171 		defval(0);
172 
173 	default:
174 		*valp = 0;
175 		*valpp = 0;
176 		return(SLC_NOSUPPORT);
177 	}
178 }
179 
180 
181 /*
182  * getpty()
183  *
184  * Allocate a pty.  As a side effect, the external character
185  * array "line" contains the name of the slave side.
186  *
187  * Returns the file descriptor of the opened pty.
188  */
189 #ifndef	__GNUC__
190 char *line = NULL16STR;
191 #else
192 static char Xline[] = NULL16STR;
193 char *line = Xline;
194 #endif
195 
196 
197 static int ptyslavefd; /* for cleanopen() */
198 
199 int
200 getpty(ptynum)
201 int *ptynum;
202 {
203 	int ptyfd;
204 
205 	ptyfd = openpty(ptynum, &ptyslavefd, line, NULL, NULL);
206 	if (ptyfd == 0)
207 		return *ptynum;
208 	ptyslavefd = -1;
209 	return (-1);
210 }
211 
212 #ifdef	LINEMODE
213 /*
214  * tty_flowmode()	Find out if flow control is enabled or disabled.
215  * tty_linemode()	Find out if linemode (external processing) is enabled.
216  * tty_setlinemod(on)	Turn on/off linemode.
217  * tty_isecho()		Find out if echoing is turned on.
218  * tty_setecho(on)	Enable/disable character echoing.
219  * tty_israw()		Find out if terminal is in RAW mode.
220  * tty_binaryin(on)	Turn on/off BINARY on input.
221  * tty_binaryout(on)	Turn on/off BINARY on output.
222  * tty_isediting()	Find out if line editing is enabled.
223  * tty_istrapsig()	Find out if signal trapping is enabled.
224  * tty_setedit(on)	Turn on/off line editing.
225  * tty_setsig(on)	Turn on/off signal trapping.
226  * tty_issofttab()	Find out if tab expansion is enabled.
227  * tty_setsofttab(on)	Turn on/off soft tab expansion.
228  * tty_islitecho()	Find out if typed control chars are echoed literally
229  * tty_setlitecho()	Turn on/off literal echo of control chars
230  * tty_tspeed(val)	Set transmit speed to val.
231  * tty_rspeed(val)	Set receive speed to val.
232  */
233 
234 
235 int
236 tty_linemode()
237 {
238 	return(termbuf.c_lflag & EXTPROC);
239 }
240 
241 void
242 tty_setlinemode(on)
243 	int on;
244 {
245 	set_termbuf();
246 	(void) ioctl(pty, TIOCEXT, (char *)&on);
247 	init_termbuf();
248 }
249 #endif	/* LINEMODE */
250 
251 int
252 tty_isecho()
253 {
254 	return (termbuf.c_lflag & ECHO);
255 }
256 
257 int
258 tty_flowmode()
259 {
260 	return((termbuf.c_iflag & IXON) ? 1 : 0);
261 }
262 
263 int
264 tty_restartany()
265 {
266 	return((termbuf.c_iflag & IXANY) ? 1 : 0);
267 }
268 
269 void
270 tty_setecho(on)
271 	int on;
272 {
273 	if (on)
274 		termbuf.c_lflag |= ECHO;
275 	else
276 		termbuf.c_lflag &= ~ECHO;
277 }
278 
279 int
280 tty_israw()
281 {
282 	return(!(termbuf.c_lflag & ICANON));
283 }
284 
285 void
286 tty_binaryin(on)
287 	int on;
288 {
289 	if (on) {
290 		termbuf.c_iflag &= ~ISTRIP;
291 	} else {
292 		termbuf.c_iflag |= ISTRIP;
293 	}
294 }
295 
296 void
297 tty_binaryout(on)
298 	int on;
299 {
300 	if (on) {
301 		termbuf.c_cflag &= ~(CSIZE|PARENB);
302 		termbuf.c_cflag |= CS8;
303 		termbuf.c_oflag &= ~OPOST;
304 	} else {
305 		termbuf.c_cflag &= ~CSIZE;
306 		termbuf.c_cflag |= CS7|PARENB;
307 		termbuf.c_oflag |= OPOST;
308 	}
309 }
310 
311 int
312 tty_isbinaryin()
313 {
314 	return(!(termbuf.c_iflag & ISTRIP));
315 }
316 
317 int
318 tty_isbinaryout()
319 {
320 	return(!(termbuf.c_oflag&OPOST));
321 }
322 
323 #ifdef	LINEMODE
324 int
325 tty_isediting()
326 {
327 	return(termbuf.c_lflag & ICANON);
328 }
329 
330 int
331 tty_istrapsig()
332 {
333 	return(termbuf.c_lflag & ISIG);
334 }
335 
336 void
337 tty_setedit(on)
338 	int on;
339 {
340 	if (on)
341 		termbuf.c_lflag |= ICANON;
342 	else
343 		termbuf.c_lflag &= ~ICANON;
344 }
345 
346 void
347 tty_setsig(on)
348 	int on;
349 {
350 	if (on)
351 		termbuf.c_lflag |= ISIG;
352 	else
353 		termbuf.c_lflag &= ~ISIG;
354 }
355 #endif	/* LINEMODE */
356 
357 int
358 tty_issofttab()
359 {
360 # ifdef	OXTABS
361 	return (termbuf.c_oflag & OXTABS);
362 # endif
363 # ifdef	TABDLY
364 	return ((termbuf.c_oflag & TABDLY) == TAB3);
365 # endif
366 }
367 
368 void
369 tty_setsofttab(on)
370 	int on;
371 {
372 	if (on) {
373 # ifdef	OXTABS
374 		termbuf.c_oflag |= OXTABS;
375 # endif
376 # ifdef	TABDLY
377 		termbuf.c_oflag &= ~TABDLY;
378 		termbuf.c_oflag |= TAB3;
379 # endif
380 	} else {
381 # ifdef	OXTABS
382 		termbuf.c_oflag &= ~OXTABS;
383 # endif
384 # ifdef	TABDLY
385 		termbuf.c_oflag &= ~TABDLY;
386 		termbuf.c_oflag |= TAB0;
387 # endif
388 	}
389 }
390 
391 int
392 tty_islitecho()
393 {
394 # ifdef	ECHOCTL
395 	return (!(termbuf.c_lflag & ECHOCTL));
396 # endif
397 # ifdef	TCTLECH
398 	return (!(termbuf.c_lflag & TCTLECH));
399 # endif
400 # if	!defined(ECHOCTL) && !defined(TCTLECH)
401 	return (0);	/* assumes ctl chars are echoed '^x' */
402 # endif
403 }
404 
405 void
406 tty_setlitecho(on)
407 	int on;
408 {
409 # ifdef	ECHOCTL
410 	if (on)
411 		termbuf.c_lflag &= ~ECHOCTL;
412 	else
413 		termbuf.c_lflag |= ECHOCTL;
414 # endif
415 # ifdef	TCTLECH
416 	if (on)
417 		termbuf.c_lflag &= ~TCTLECH;
418 	else
419 		termbuf.c_lflag |= TCTLECH;
420 # endif
421 }
422 
423 int
424 tty_iscrnl()
425 {
426 	return (termbuf.c_iflag & ICRNL);
427 }
428 
429 void
430 tty_tspeed(val)
431 	int val;
432 {
433 	cfsetospeed(&termbuf, val);
434 }
435 
436 void
437 tty_rspeed(val)
438 	int val;
439 {
440 	cfsetispeed(&termbuf, val);
441 }
442 
443 
444 
445 
446 /*
447  * getptyslave()
448  *
449  * Open the slave side of the pty, and do any initialization
450  * that is necessary.  The return value is a file descriptor
451  * for the slave side.
452  */
453 extern int def_tspeed, def_rspeed;
454 	extern int def_row, def_col;
455 
456     void
457 getptyslave()
458 {
459 	register int t = -1;
460 
461 #ifdef	LINEMODE
462 	int waslm;
463 #endif
464 	struct winsize ws;
465 	/*
466 	 * Opening the slave side may cause initilization of the
467 	 * kernel tty structure.  We need remember the state of
468 	 * 	if linemode was turned on
469 	 *	terminal window size
470 	 *	terminal speed
471 	 * so that we can re-set them if we need to.
472 	 */
473 #ifdef	LINEMODE
474 	waslm = tty_linemode();
475 #endif
476 
477 	/*
478 	 * Make sure that we don't have a controlling tty, and
479 	 * that we are the session (process group) leader.
480 	 */
481 	t = open(_PATH_TTY, O_RDWR);
482 	if (t >= 0) {
483 		(void) ioctl(t, TIOCNOTTY, (char *)0);
484 		(void) close(t);
485 	}
486 
487 
488 
489 	t = cleanopen(line);
490 	if (t < 0)
491 		fatalperror(net, line);
492 
493 
494 	/*
495 	 * set up the tty modes as we like them to be.
496 	 */
497 	init_termbuf();
498 	if (def_row || def_col) {
499 		memset((char *)&ws, 0, sizeof(ws));
500 		ws.ws_col = def_col;
501 		ws.ws_row = def_row;
502 		(void)ioctl(t, TIOCSWINSZ, (char *)&ws);
503 	}
504 
505 	/*
506 	 * Settings for sgtty based systems
507 	 */
508 
509 	/*
510 	 * Settings for all other termios/termio based
511 	 * systems, other than 4.4BSD.  In 4.4BSD the
512 	 * kernel does the initial terminal setup.
513 	 */
514 	tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600);
515 	tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600);
516 #ifdef	LINEMODE
517 	if (waslm)
518 		tty_setlinemode(1);
519 #endif	/* LINEMODE */
520 
521 	/*
522 	 * Set the tty modes, and make this our controlling tty.
523 	 */
524 	set_termbuf();
525 	if (login_tty(t) == -1)
526 		fatalperror(net, "login_tty");
527 	if (net > 2)
528 		(void) close(net);
529 	if (pty > 2) {
530 		(void) close(pty);
531 		pty = -1;
532 	}
533 }
534 
535 /*
536  * Open the specified slave side of the pty,
537  * making sure that we have a clean tty.
538  */
539 int
540 cleanopen(ttyline)
541 	char *ttyline;
542 {
543 	return ptyslavefd;
544 }
545 
546 /*
547  * startslave(host)
548  *
549  * Given a hostname, do whatever
550  * is necessary to startup the login process on the slave side of the pty.
551  */
552 
553 /* ARGSUSED */
554 void
555 startslave(host, autologin, autoname)
556 	char *host;
557 	int autologin;
558 	char *autoname;
559 {
560 	register int i;
561 
562 #ifdef AUTHENTICATION
563 	if (!autoname || !autoname[0])
564 		autologin = 0;
565 
566 	if (autologin < auth_level) {
567 		fatal(net, "Authorization failed");
568 		exit(1);
569 	}
570 #endif
571 
572 
573 	if ((i = fork()) < 0)
574 		fatalperror(net, "fork");
575 	if (i) {
576 	} else {
577 		getptyslave();
578 		start_login(host, autologin, autoname);
579 		/*NOTREACHED*/
580 	}
581 }
582 
583 char	*envinit[3];
584 
585 void
586 init_env()
587 {
588 	char **envp;
589 
590 	envp = envinit;
591 	if ((*envp = getenv("TZ")))
592 		*envp++ -= 3;
593 	*envp = 0;
594 	environ = envinit;
595 }
596 
597 
598 /*
599  * start_login(host)
600  *
601  * Assuming that we are now running as a child processes, this
602  * function will turn us into the login process.
603  */
604 extern char *gettyname;
605 
606 void
607 start_login(host, autologin, name)
608 	char *host;
609 	int autologin;
610 	char *name;
611 {
612 	register char **argv;
613 #define	TABBUFSIZ	512
614 	char	defent[TABBUFSIZ];
615 	char	defstrs[TABBUFSIZ];
616 #undef	TABBUFSIZ
617 	const char *loginprog = NULL;
618 
619 
620 	scrub_env();
621 
622 	/*
623 	 * -h : pass on name of host.
624 	 *		WARNING:  -h is accepted by login if and only if
625 	 *			getuid() == 0.
626 	 * -p : don't clobber the environment (so terminal type stays set).
627 	 *
628 	 * -f : force this login, he has already been authenticated
629 	 */
630 	argv = addarg(0, "login");
631 
632 	{
633 		argv = addarg(argv, "-h");
634 		argv = addarg(argv, host);
635 	}
636 	argv = addarg(argv, "-p");
637 #ifdef	LINEMODE
638 	/*
639 	 * Set the environment variable "LINEMODE" to either
640 	 * "real" or "kludge" if we are operating in either
641 	 * real or kludge linemode.
642 	 */
643 	if (lmodetype == REAL_LINEMODE)
644 		setenv("LINEMODE", "real", 1);
645 # ifdef KLUDGELINEMODE
646 	else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK)
647 		setenv("LINEMODE", "kludge", 1);
648 # endif
649 #endif
650 #ifdef SECURELOGIN
651 	/*
652 	 * don't worry about the -f that might get sent.
653 	 * A -s is supposed to override it anyhow.
654 	 */
655 	if (require_secure_login)
656 		argv = addarg(argv, "-s");
657 #endif
658 #ifdef AUTHENTICATION
659 	if (auth_level >= 0 && autologin == AUTH_VALID) {
660 		argv = addarg(argv, "-f");
661 		argv = addarg(argv, "--");
662 		argv = addarg(argv, name);
663 	} else
664 #endif
665 	if (getenv("USER")) {
666 		argv = addarg(argv, "--");
667 		argv = addarg(argv, getenv("USER"));
668 		/*
669 		 * Assume that login will set the USER variable
670 		 * correctly.  For SysV systems, this means that
671 		 * USER will no longer be set, just LOGNAME by
672 		 * login.  (The problem is that if the auto-login
673 		 * fails, and the user then specifies a different
674 		 * account name, he can get logged in with both
675 		 * LOGNAME and USER in his environment, but the
676 		 * USER value will be wrong.
677 		 */
678 		unsetenv("USER");
679 	}
680         if (getent(defent, gettyname) == 1) {
681                 char *cp = defstrs;
682 
683                 loginprog = getstr("lo", &cp);
684         }
685         if (loginprog == NULL)
686                 loginprog = _PATH_LOGIN;
687 	closelog();
688 	/*
689 	 * This sleep(1) is in here so that telnetd can
690 	 * finish up with the tty.  There's a race condition
691 	 * the login banner message gets lost...
692 	 */
693 	sleep(1);
694         execv(loginprog, argv);
695 
696         syslog(LOG_ERR, "%s: %m", loginprog);
697         fatalperror(net, loginprog);
698 	/*NOTREACHED*/
699 }
700 
701 	char **
702 addarg(argv, val)
703 	register char **argv;
704 	register char *val;
705 {
706 	register char **cpp;
707 
708 	if (argv == NULL) {
709 		/*
710 		 * 10 entries, a leading length, and a null
711 		 */
712 		argv = (char **)malloc(sizeof(*argv) * 12);
713 		if (argv == NULL)
714 			return(NULL);
715 		*argv++ = (char *)10;
716 		*argv = (char *)0;
717 	}
718 	for (cpp = argv; *cpp; cpp++)
719 		;
720 	if (cpp == &argv[(long)argv[-1]]) {
721 		--argv;
722 		*argv = (char *)((long)(*argv) + 10);
723 		argv = (char **)realloc(argv, sizeof(*argv) * ((long)(*argv) + 2));
724 		if (argv == NULL) {
725 			fatal(net, "not enough memory");
726 			/*NOTREACHED*/
727 		}
728 		argv++;
729 		cpp = &argv[(long)argv[-1] - 10];
730 	}
731 	*cpp++ = val;
732 	*cpp = 0;
733 	return(argv);
734 }
735 
736 /*
737  * scrub_env()
738  *
739  * We only accept the environment variables listed below.
740  */
741 
742 void
743 scrub_env()
744 {
745 	static const char *reject[] = {
746 		"TERMCAP=/",
747 		NULL
748 	};
749 
750 	static const char *acceptstr[] = {
751 		"XAUTH=", "XAUTHORITY=", "DISPLAY=",
752 		"TERM=",
753 		"EDITOR=",
754 		"PAGER=",
755 		"LOGNAME=",
756 		"POSIXLY_CORRECT=",
757 		"TERMCAP=",
758 		"PRINTER=",
759 		NULL
760 	};
761 
762 	char **cpp, **cpp2;
763 	const char **p;
764 
765 	for (cpp2 = cpp = environ; *cpp; cpp++) {
766 		int reject_it = 0;
767 
768 		for(p = reject; *p; p++)
769 			if(strncmp(*cpp, *p, strlen(*p)) == 0) {
770 				reject_it = 1;
771 				break;
772 			}
773 		if (reject_it)
774 			continue;
775 
776 		for(p = acceptstr; *p; p++)
777 			if(strncmp(*cpp, *p, strlen(*p)) == 0)
778 				break;
779 		if(*p != NULL)
780 			*cpp2++ = *cpp;
781 	}
782 	*cpp2 = NULL;
783 }
784 
785 /*
786  * cleanup()
787  *
788  * This is the routine to call when we are all through, to
789  * clean up anything that needs to be cleaned up.
790  */
791 /* ARGSUSED */
792 void
793 cleanup(sig)
794 	int sig;
795 {
796 	char *p, c;
797 
798 	p = line + sizeof("/dev/") - 1;
799 #ifdef SUPPORT_UTMP
800 	if (logout(p))
801 		logwtmp(p, "", "");
802 #endif
803 #ifdef SUPPORT_UTMPX
804 	if (logoutx(p, 0, DEAD_PROCESS))
805 		logwtmpx(p, "", "", 0, DEAD_PROCESS);
806 #endif
807 	(void)chmod(line, 0666);
808 	(void)chown(line, 0, 0);
809 	c = *p; *p = 'p';
810 	(void)chmod(line, 0666);
811 	(void)chown(line, 0, 0);
812 	*p = c;
813 	if (ttyaction(line, "telnetd", "root"))
814 		syslog(LOG_ERR, "%s: ttyaction failed", line);
815 	(void) shutdown(net, 2);
816 	exit(1);
817 }
818