xref: /freebsd/crypto/heimdal/appl/telnet/telnetd/sys_term.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "telnetd.h"
35 
36 RCSID("$Id: sys_term.c,v 1.90 2000/01/01 11:53:59 assar Exp $");
37 
38 #if defined(_CRAY) || (defined(__hpux) && !defined(HAVE_UTMPX_H))
39 # define PARENT_DOES_UTMP
40 #endif
41 
42 #ifdef HAVE_UTMP_H
43 #include <utmp.h>
44 #endif
45 
46 #ifdef HAVE_UTMPX_H
47 #include <utmpx.h>
48 #endif
49 
50 #ifdef HAVE_UTMPX_H
51 struct	utmpx wtmp;
52 #elif defined(HAVE_UTMP_H)
53 struct	utmp wtmp;
54 #endif /* HAVE_UTMPX_H */
55 
56 #ifdef HAVE_STRUCT_UTMP_UT_HOST
57 int	utmp_len = sizeof(wtmp.ut_host);
58 #else
59 int	utmp_len = MaxHostNameLen;
60 #endif
61 
62 #ifndef UTMP_FILE
63 #ifdef _PATH_UTMP
64 #define UTMP_FILE _PATH_UTMP
65 #else
66 #define UTMP_FILE "/etc/utmp"
67 #endif
68 #endif
69 
70 #if !defined(WTMP_FILE) && defined(_PATH_WTMP)
71 #define WTMP_FILE _PATH_WTMP
72 #endif
73 
74 #ifndef PARENT_DOES_UTMP
75 #ifdef WTMP_FILE
76 char	wtmpf[] = WTMP_FILE;
77 #else
78 char	wtmpf[]	= "/usr/adm/wtmp";
79 #endif
80 char	utmpf[] = UTMP_FILE;
81 #else /* PARENT_DOES_UTMP */
82 #ifdef WTMP_FILE
83 char	wtmpf[] = WTMP_FILE;
84 #else
85 char	wtmpf[]	= "/etc/wtmp";
86 #endif
87 #endif /* PARENT_DOES_UTMP */
88 
89 #ifdef HAVE_TMPDIR_H
90 #include <tmpdir.h>
91 #endif	/* CRAY */
92 
93 #ifdef	STREAMSPTY
94 
95 #ifdef HAVE_SAC_H
96 #include <sac.h>
97 #endif
98 
99 #ifdef HAVE_SYS_STROPTS_H
100 #include <sys/stropts.h>
101 #endif
102 
103 #endif /* STREAMSPTY */
104 
105 #ifdef	HAVE_SYS_STREAM_H
106 #ifdef  HAVE_SYS_UIO_H
107 #include <sys/uio.h>
108 #endif
109 #ifdef __hpux
110 #undef SE
111 #endif
112 #include <sys/stream.h>
113 #endif
114 #if !(defined(__sgi) || defined(__linux) || defined(_AIX)) && defined(HAVE_SYS_TTY)
115 #include <sys/tty.h>
116 #endif
117 #ifdef	t_erase
118 #undef	t_erase
119 #undef	t_kill
120 #undef	t_intrc
121 #undef	t_quitc
122 #undef	t_startc
123 #undef	t_stopc
124 #undef	t_eofc
125 #undef	t_brkc
126 #undef	t_suspc
127 #undef	t_dsuspc
128 #undef	t_rprntc
129 #undef	t_flushc
130 #undef	t_werasc
131 #undef	t_lnextc
132 #endif
133 
134 #ifdef HAVE_TERMIOS_H
135 #include <termios.h>
136 #else
137 #ifdef HAVE_TERMIO_H
138 #include <termio.h>
139 #endif
140 #endif
141 
142 #ifdef HAVE_UTIL_H
143 #include <util.h>
144 #endif
145 
146 # ifndef	TCSANOW
147 #  ifdef TCSETS
148 #   define	TCSANOW		TCSETS
149 #   define	TCSADRAIN	TCSETSW
150 #   define	tcgetattr(f, t)	ioctl(f, TCGETS, (char *)t)
151 #  else
152 #   ifdef TCSETA
153 #    define	TCSANOW		TCSETA
154 #    define	TCSADRAIN	TCSETAW
155 #    define	tcgetattr(f, t)	ioctl(f, TCGETA, (char *)t)
156 #   else
157 #    define	TCSANOW		TIOCSETA
158 #    define	TCSADRAIN	TIOCSETAW
159 #    define	tcgetattr(f, t)	ioctl(f, TIOCGETA, (char *)t)
160 #   endif
161 #  endif
162 #  define	tcsetattr(f, a, t)	ioctl(f, a, t)
163 #  define	cfsetospeed(tp, val)	(tp)->c_cflag &= ~CBAUD; \
164 (tp)->c_cflag |= (val)
165 #  define	cfgetospeed(tp)		((tp)->c_cflag & CBAUD)
166 #  ifdef CIBAUD
167 #   define	cfsetispeed(tp, val)	(tp)->c_cflag &= ~CIBAUD; \
168      (tp)->c_cflag |= ((val)<<IBSHIFT)
169 #   define	cfgetispeed(tp)		(((tp)->c_cflag & CIBAUD)>>IBSHIFT)
170 #  else
171 #   define	cfsetispeed(tp, val)	(tp)->c_cflag &= ~CBAUD; \
172      (tp)->c_cflag |= (val)
173 #   define	cfgetispeed(tp)		((tp)->c_cflag & CBAUD)
174 #  endif
175 # endif /* TCSANOW */
176      struct termios termbuf, termbuf2;	/* pty control structure */
177 # ifdef  STREAMSPTY
178      static int ttyfd = -1;
179      int really_stream = 0;
180 # endif
181 
182      const char *new_login = _PATH_LOGIN;
183 
184 /*
185  * init_termbuf()
186  * copy_termbuf(cp)
187  * set_termbuf()
188  *
189  * These three routines are used to get and set the "termbuf" structure
190  * to and from the kernel.  init_termbuf() gets the current settings.
191  * copy_termbuf() hands in a new "termbuf" to write to the kernel, and
192  * set_termbuf() writes the structure into the kernel.
193  */
194 
195      void
196      init_termbuf(void)
197 {
198 # ifdef  STREAMSPTY
199     if (really_stream)
200 	tcgetattr(ttyfd, &termbuf);
201     else
202 # endif
203 	tcgetattr(ourpty, &termbuf);
204     termbuf2 = termbuf;
205 }
206 
207 void
208 set_termbuf(void)
209 {
210     /*
211      * Only make the necessary changes.
212 	 */
213     if (memcmp(&termbuf, &termbuf2, sizeof(termbuf)))
214 # ifdef  STREAMSPTY
215 	if (really_stream)
216 	    tcsetattr(ttyfd, TCSANOW, &termbuf);
217 	else
218 # endif
219 	    tcsetattr(ourpty, TCSANOW, &termbuf);
220 }
221 
222 
223 /*
224  * spcset(func, valp, valpp)
225  *
226  * This function takes various special characters (func), and
227  * sets *valp to the current value of that character, and
228  * *valpp to point to where in the "termbuf" structure that
229  * value is kept.
230  *
231  * It returns the SLC_ level of support for this function.
232  */
233 
234 
235 int
236 spcset(int func, cc_t *valp, cc_t **valpp)
237 {
238 
239 #define	setval(a, b)	*valp = termbuf.c_cc[a]; \
240     *valpp = &termbuf.c_cc[a]; \
241 				   return(b);
242 #define	defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT);
243 
244     switch(func) {
245     case SLC_EOF:
246 	setval(VEOF, SLC_VARIABLE);
247     case SLC_EC:
248 	setval(VERASE, SLC_VARIABLE);
249     case SLC_EL:
250 	setval(VKILL, SLC_VARIABLE);
251     case SLC_IP:
252 	setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
253     case SLC_ABORT:
254 	setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT);
255     case SLC_XON:
256 #ifdef	VSTART
257 	setval(VSTART, SLC_VARIABLE);
258 #else
259 	defval(0x13);
260 #endif
261     case SLC_XOFF:
262 #ifdef	VSTOP
263 	setval(VSTOP, SLC_VARIABLE);
264 #else
265 	defval(0x11);
266 #endif
267     case SLC_EW:
268 #ifdef	VWERASE
269 	setval(VWERASE, SLC_VARIABLE);
270 #else
271 	defval(0);
272 #endif
273     case SLC_RP:
274 #ifdef	VREPRINT
275 	setval(VREPRINT, SLC_VARIABLE);
276 #else
277 	defval(0);
278 #endif
279     case SLC_LNEXT:
280 #ifdef	VLNEXT
281 	setval(VLNEXT, SLC_VARIABLE);
282 #else
283 	defval(0);
284 #endif
285     case SLC_AO:
286 #if	!defined(VDISCARD) && defined(VFLUSHO)
287 # define VDISCARD VFLUSHO
288 #endif
289 #ifdef	VDISCARD
290 	setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT);
291 #else
292 	defval(0);
293 #endif
294     case SLC_SUSP:
295 #ifdef	VSUSP
296 	setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN);
297 #else
298 	defval(0);
299 #endif
300 #ifdef	VEOL
301     case SLC_FORW1:
302 	setval(VEOL, SLC_VARIABLE);
303 #endif
304 #ifdef	VEOL2
305     case SLC_FORW2:
306 	setval(VEOL2, SLC_VARIABLE);
307 #endif
308     case SLC_AYT:
309 #ifdef	VSTATUS
310 	setval(VSTATUS, SLC_VARIABLE);
311 #else
312 	defval(0);
313 #endif
314 
315     case SLC_BRK:
316     case SLC_SYNCH:
317     case SLC_EOR:
318 	defval(0);
319 
320     default:
321 	*valp = 0;
322 	*valpp = 0;
323 	return(SLC_NOSUPPORT);
324     }
325 }
326 
327 #ifdef _CRAY
328 /*
329  * getnpty()
330  *
331  * Return the number of pty's configured into the system.
332  */
333 int
334 getnpty()
335 {
336 #ifdef _SC_CRAY_NPTY
337     int numptys;
338 
339     if ((numptys = sysconf(_SC_CRAY_NPTY)) != -1)
340 	return numptys;
341     else
342 #endif /* _SC_CRAY_NPTY */
343 	return 128;
344 }
345 #endif /* CRAY */
346 
347 /*
348  * getpty()
349  *
350  * Allocate a pty.  As a side effect, the external character
351  * array "line" contains the name of the slave side.
352  *
353  * Returns the file descriptor of the opened pty.
354  */
355 
356 static char Xline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
357 char *line = Xline;
358 
359 #ifdef	_CRAY
360 char myline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
361 #endif	/* CRAY */
362 
363 #if !defined(HAVE_PTSNAME) && defined(STREAMSPTY)
364 static char *ptsname(int fd)
365 {
366 #ifdef HAVE_TTYNAME
367     return ttyname(fd);
368 #else
369     return NULL;
370 #endif
371 }
372 #endif
373 
374 int getpty(int *ptynum)
375 {
376 #ifdef __osf__ /* XXX */
377     int master;
378     int slave;
379     if(openpty(&master, &slave, line, 0, 0) == 0){
380 	close(slave);
381 	return master;
382     }
383     return -1;
384 #else
385 #ifdef HAVE__GETPTY
386     int master, slave;
387     char *p;
388     p = _getpty(&master, O_RDWR, 0600, 1);
389     if(p == NULL)
390 	return -1;
391     strlcpy(line, p, sizeof(Xline));
392     return master;
393 #else
394 
395     int p;
396     char *cp, *p1, *p2;
397     int i;
398 #if SunOS == 40
399     int dummy;
400 #endif
401 #if 0 /* && defined(HAVE_OPENPTY) */
402     int master;
403     int slave;
404     if(openpty(&master, &slave, line, 0, 0) == 0){
405 	close(slave);
406 	return master;
407     }
408 #else
409 #ifdef	STREAMSPTY
410     char *clone[] = { "/dev/ptc", "/dev/ptmx", "/dev/ptm",
411 		      "/dev/ptym/clone", 0 };
412 
413     char **q;
414     for(q=clone; *q; q++){
415 	p=open(*q, O_RDWR);
416 	if(p >= 0){
417 #ifdef HAVE_GRANTPT
418 	    grantpt(p);
419 #endif
420 #ifdef HAVE_UNLOCKPT
421 	    unlockpt(p);
422 #endif
423 	    strlcpy(line, ptsname(p), sizeof(Xline));
424 	    really_stream = 1;
425 	    return p;
426 	}
427     }
428 #endif /* STREAMSPTY */
429 #ifndef _CRAY
430 
431 #ifndef	__hpux
432     snprintf(line, sizeof(Xline), "/dev/ptyXX");
433     p1 = &line[8];
434     p2 = &line[9];
435 #else
436     snprintf(line, sizeof(Xline), "/dev/ptym/ptyXX");
437     p1 = &line[13];
438     p2 = &line[14];
439 #endif
440 
441 
442     for (cp = "pqrstuvwxyzPQRST"; *cp; cp++) {
443 	struct stat stb;
444 
445 	*p1 = *cp;
446 	*p2 = '0';
447 	/*
448 	 * This stat() check is just to keep us from
449 	 * looping through all 256 combinations if there
450 	 * aren't that many ptys available.
451 	 */
452 	if (stat(line, &stb) < 0)
453 	    break;
454 	for (i = 0; i < 16; i++) {
455 	    *p2 = "0123456789abcdef"[i];
456 	    p = open(line, O_RDWR);
457 	    if (p > 0) {
458 #ifndef	__hpux
459 		line[5] = 't';
460 #else
461 		for (p1 = &line[8]; *p1; p1++)
462 		    *p1 = *(p1+1);
463 		line[9] = 't';
464 #endif
465 		chown(line, 0, 0);
466 		chmod(line, 0600);
467 #if SunOS == 40
468 		if (ioctl(p, TIOCGPGRP, &dummy) == 0
469 		    || errno != EIO) {
470 		    chmod(line, 0666);
471 		    close(p);
472 		    line[5] = 'p';
473 		} else
474 #endif /* SunOS == 40 */
475 		    return(p);
476 	    }
477 	}
478     }
479 #else	/* CRAY */
480     extern lowpty, highpty;
481     struct stat sb;
482 
483     for (*ptynum = lowpty; *ptynum <= highpty; (*ptynum)++) {
484 	snprintf(myline, sizeof(myline), "/dev/pty/%03d", *ptynum);
485 	p = open(myline, 2);
486 	if (p < 0)
487 	    continue;
488 	snprintf(line, sizeof(Xline), "/dev/ttyp%03d", *ptynum);
489 	/*
490 	 * Here are some shenanigans to make sure that there
491 	 * are no listeners lurking on the line.
492 	 */
493 	if(stat(line, &sb) < 0) {
494 	    close(p);
495 	    continue;
496 	}
497 	if(sb.st_uid || sb.st_gid || sb.st_mode != 0600) {
498 	    chown(line, 0, 0);
499 	    chmod(line, 0600);
500 	    close(p);
501 	    p = open(myline, 2);
502 	    if (p < 0)
503 		continue;
504 	}
505 	/*
506 	 * Now it should be safe...check for accessability.
507 	 */
508 	if (access(line, 6) == 0)
509 	    return(p);
510 	else {
511 	    /* no tty side to pty so skip it */
512 	    close(p);
513 	}
514     }
515 #endif	/* CRAY */
516 #endif	/* STREAMSPTY */
517 #endif /* OPENPTY */
518     return(-1);
519 #endif
520 }
521 
522 
523 int
524 tty_isecho(void)
525 {
526     return (termbuf.c_lflag & ECHO);
527 }
528 
529 int
530 tty_flowmode(void)
531 {
532     return((termbuf.c_iflag & IXON) ? 1 : 0);
533 }
534 
535 int
536 tty_restartany(void)
537 {
538     return((termbuf.c_iflag & IXANY) ? 1 : 0);
539 }
540 
541 void
542 tty_setecho(int on)
543 {
544     if (on)
545 	termbuf.c_lflag |= ECHO;
546     else
547 	termbuf.c_lflag &= ~ECHO;
548 }
549 
550 int
551 tty_israw(void)
552 {
553     return(!(termbuf.c_lflag & ICANON));
554 }
555 
556 void
557 tty_binaryin(int on)
558 {
559     if (on) {
560 	termbuf.c_iflag &= ~ISTRIP;
561     } else {
562 	termbuf.c_iflag |= ISTRIP;
563     }
564 }
565 
566 void
567 tty_binaryout(int on)
568 {
569     if (on) {
570 	termbuf.c_cflag &= ~(CSIZE|PARENB);
571 	termbuf.c_cflag |= CS8;
572 	termbuf.c_oflag &= ~OPOST;
573     } else {
574 	termbuf.c_cflag &= ~CSIZE;
575 	termbuf.c_cflag |= CS7|PARENB;
576 	termbuf.c_oflag |= OPOST;
577     }
578 }
579 
580 int
581 tty_isbinaryin(void)
582 {
583     return(!(termbuf.c_iflag & ISTRIP));
584 }
585 
586 int
587 tty_isbinaryout(void)
588 {
589     return(!(termbuf.c_oflag&OPOST));
590 }
591 
592 
593 int
594 tty_issofttab(void)
595 {
596 # ifdef	OXTABS
597     return (termbuf.c_oflag & OXTABS);
598 # endif
599 # ifdef	TABDLY
600     return ((termbuf.c_oflag & TABDLY) == TAB3);
601 # endif
602 }
603 
604 void
605 tty_setsofttab(int on)
606 {
607     if (on) {
608 # ifdef	OXTABS
609 	termbuf.c_oflag |= OXTABS;
610 # endif
611 # ifdef	TABDLY
612 	termbuf.c_oflag &= ~TABDLY;
613 	termbuf.c_oflag |= TAB3;
614 # endif
615     } else {
616 # ifdef	OXTABS
617 	termbuf.c_oflag &= ~OXTABS;
618 # endif
619 # ifdef	TABDLY
620 	termbuf.c_oflag &= ~TABDLY;
621 	termbuf.c_oflag |= TAB0;
622 # endif
623     }
624 }
625 
626 int
627 tty_islitecho(void)
628 {
629 # ifdef	ECHOCTL
630     return (!(termbuf.c_lflag & ECHOCTL));
631 # endif
632 # ifdef	TCTLECH
633     return (!(termbuf.c_lflag & TCTLECH));
634 # endif
635 # if	!defined(ECHOCTL) && !defined(TCTLECH)
636     return (0);	/* assumes ctl chars are echoed '^x' */
637 # endif
638 }
639 
640 void
641 tty_setlitecho(int on)
642 {
643 # ifdef	ECHOCTL
644     if (on)
645 	termbuf.c_lflag &= ~ECHOCTL;
646     else
647 	termbuf.c_lflag |= ECHOCTL;
648 # endif
649 # ifdef	TCTLECH
650     if (on)
651 	termbuf.c_lflag &= ~TCTLECH;
652     else
653 	termbuf.c_lflag |= TCTLECH;
654 # endif
655 }
656 
657 int
658 tty_iscrnl(void)
659 {
660     return (termbuf.c_iflag & ICRNL);
661 }
662 
663 /*
664  * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
665  */
666 #if B4800 != 4800
667 #define	DECODE_BAUD
668 #endif
669 
670 #ifdef	DECODE_BAUD
671 
672 /*
673  * A table of available terminal speeds
674  */
675 struct termspeeds {
676     int	speed;
677     int	value;
678 } termspeeds[] = {
679     { 0,      B0 },      { 50,    B50 },    { 75,     B75 },
680     { 110,    B110 },    { 134,   B134 },   { 150,    B150 },
681     { 200,    B200 },    { 300,   B300 },   { 600,    B600 },
682     { 1200,   B1200 },   { 1800,  B1800 },  { 2400,   B2400 },
683     { 4800,   B4800 },
684 #ifdef	B7200
685     { 7200,  B7200 },
686 #endif
687     { 9600,   B9600 },
688 #ifdef	B14400
689     { 14400,  B14400 },
690 #endif
691 #ifdef	B19200
692     { 19200,  B19200 },
693 #endif
694 #ifdef	B28800
695     { 28800,  B28800 },
696 #endif
697 #ifdef	B38400
698     { 38400,  B38400 },
699 #endif
700 #ifdef	B57600
701     { 57600,  B57600 },
702 #endif
703 #ifdef	B115200
704     { 115200, B115200 },
705 #endif
706 #ifdef	B230400
707     { 230400, B230400 },
708 #endif
709     { -1,     0 }
710 };
711 #endif	/* DECODE_BUAD */
712 
713 void
714 tty_tspeed(int val)
715 {
716 #ifdef	DECODE_BAUD
717     struct termspeeds *tp;
718 
719     for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
720 	;
721     if (tp->speed == -1)	/* back up to last valid value */
722 	--tp;
723     cfsetospeed(&termbuf, tp->value);
724 #else	/* DECODE_BUAD */
725     cfsetospeed(&termbuf, val);
726 #endif	/* DECODE_BUAD */
727 }
728 
729 void
730 tty_rspeed(int val)
731 {
732 #ifdef	DECODE_BAUD
733     struct termspeeds *tp;
734 
735     for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
736 	;
737     if (tp->speed == -1)	/* back up to last valid value */
738 	--tp;
739     cfsetispeed(&termbuf, tp->value);
740 #else	/* DECODE_BAUD */
741     cfsetispeed(&termbuf, val);
742 #endif	/* DECODE_BAUD */
743 }
744 
745 #ifdef PARENT_DOES_UTMP
746 extern	struct utmp wtmp;
747 extern char wtmpf[];
748 
749 extern void utmp_sig_init (void);
750 extern void utmp_sig_reset (void);
751 extern void utmp_sig_wait (void);
752 extern void utmp_sig_notify (int);
753 # endif /* PARENT_DOES_UTMP */
754 
755 #ifdef STREAMSPTY
756 
757 /* I_FIND seems to live a life of its own */
758 static int my_find(int fd, char *module)
759 {
760 #if defined(I_FIND) && defined(I_LIST)
761     static int flag;
762     static struct str_list sl;
763     int n;
764     int i;
765 
766     if(!flag){
767 	n = ioctl(fd, I_LIST, 0);
768 	if(n < 0){
769 	    perror("ioctl(fd, I_LIST, 0)");
770 	    return -1;
771 	}
772 	sl.sl_modlist=(struct str_mlist*)malloc(n * sizeof(struct str_mlist));
773 	sl.sl_nmods = n;
774 	n = ioctl(fd, I_LIST, &sl);
775 	if(n < 0){
776 	    perror("ioctl(fd, I_LIST, n)");
777 	    return -1;
778 	}
779 	flag = 1;
780     }
781 
782     for(i=0; i<sl.sl_nmods; i++)
783 	if(!strcmp(sl.sl_modlist[i].l_name, module))
784 	    return 1;
785 #endif
786     return 0;
787 }
788 
789 static void maybe_push_modules(int fd, char **modules)
790 {
791     char **p;
792     int err;
793 
794     for(p=modules; *p; p++){
795 	err = my_find(fd, *p);
796 	if(err == 1)
797 	    break;
798 	if(err < 0 && errno != EINVAL)
799 	    fatalperror(net, "my_find()");
800 	/* module not pushed or does not exist */
801     }
802     /* p points to null or to an already pushed module, now push all
803        modules before this one */
804 
805     for(p--; p >= modules; p--){
806 	err = ioctl(fd, I_PUSH, *p);
807 	if(err < 0 && errno != EINVAL)
808 	    fatalperror(net, "I_PUSH");
809     }
810 }
811 #endif
812 
813 /*
814  * getptyslave()
815  *
816  * Open the slave side of the pty, and do any initialization
817  * that is necessary.  The return value is a file descriptor
818  * for the slave side.
819  */
820 void getptyslave(void)
821 {
822     int t = -1;
823 
824     struct winsize ws;
825     extern int def_row, def_col;
826     extern int def_tspeed, def_rspeed;
827     /*
828      * Opening the slave side may cause initilization of the
829      * kernel tty structure.  We need remember the state of
830      * 	if linemode was turned on
831      *	terminal window size
832      *	terminal speed
833      * so that we can re-set them if we need to.
834      */
835 
836 
837     /*
838      * Make sure that we don't have a controlling tty, and
839      * that we are the session (process group) leader.
840      */
841 
842 #ifdef HAVE_SETSID
843     if(setsid()<0)
844 	fatalperror(net, "setsid()");
845 #else
846 # ifdef	TIOCNOTTY
847     t = open(_PATH_TTY, O_RDWR);
848     if (t >= 0) {
849 	ioctl(t, TIOCNOTTY, (char *)0);
850 	close(t);
851     }
852 # endif
853 #endif
854 
855 # ifdef PARENT_DOES_UTMP
856     /*
857      * Wait for our parent to get the utmp stuff to get done.
858      */
859     utmp_sig_wait();
860 # endif
861 
862     t = cleanopen(line);
863     if (t < 0)
864 	fatalperror(net, line);
865 
866 #ifdef  STREAMSPTY
867     ttyfd = t;
868 
869 
870     /*
871      * Not all systems have (or need) modules ttcompat and pckt so
872      * don't flag it as a fatal error if they don't exist.
873      */
874 
875     if (really_stream)
876 	{
877 	    /* these are the streams modules that we want pushed. note
878 	       that they are in reverse order, ptem will be pushed
879 	       first. maybe_push_modules() will try to push all modules
880 	       before the first one that isn't already pushed. i.e if
881 	       ldterm is pushed, only ttcompat will be attempted.
882 
883 	       all this is because we don't know which modules are
884 	       available, and we don't know which modules are already
885 	       pushed (via autopush, for instance).
886 
887 	       */
888 
889 	    char *ttymodules[] = { "ttcompat", "ldterm", "ptem", NULL };
890 	    char *ptymodules[] = { "pckt", NULL };
891 
892 	    maybe_push_modules(t, ttymodules);
893 	    maybe_push_modules(ourpty, ptymodules);
894 	}
895 #endif
896     /*
897      * set up the tty modes as we like them to be.
898      */
899     init_termbuf();
900 # ifdef	TIOCSWINSZ
901     if (def_row || def_col) {
902 	memset(&ws, 0, sizeof(ws));
903 	ws.ws_col = def_col;
904 	ws.ws_row = def_row;
905 	ioctl(t, TIOCSWINSZ, (char *)&ws);
906     }
907 # endif
908 
909     /*
910      * Settings for sgtty based systems
911      */
912 
913     /*
914      * Settings for UNICOS (and HPUX)
915      */
916 # if defined(_CRAY) || defined(__hpux)
917     termbuf.c_oflag = OPOST|ONLCR|TAB3;
918     termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
919     termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
920     termbuf.c_cflag = EXTB|HUPCL|CS8;
921 # endif
922 
923     /*
924      * Settings for all other termios/termio based
925      * systems, other than 4.4BSD.  In 4.4BSD the
926      * kernel does the initial terminal setup.
927      */
928 # if !(defined(_CRAY) || defined(__hpux)) && (BSD <= 43)
929 #  ifndef	OXTABS
930 #   define OXTABS	0
931 #  endif
932     termbuf.c_lflag |= ECHO;
933     termbuf.c_oflag |= ONLCR|OXTABS;
934     termbuf.c_iflag |= ICRNL;
935     termbuf.c_iflag &= ~IXOFF;
936 # endif
937     tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600);
938     tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600);
939 
940     /*
941      * Set the tty modes, and make this our controlling tty.
942      */
943     set_termbuf();
944     if (login_tty(t) == -1)
945 	fatalperror(net, "login_tty");
946     if (net > 2)
947 	close(net);
948     if (ourpty > 2) {
949 	close(ourpty);
950 	ourpty = -1;
951     }
952 }
953 
954 #ifndef	O_NOCTTY
955 #define	O_NOCTTY	0
956 #endif
957 /*
958  * Open the specified slave side of the pty,
959  * making sure that we have a clean tty.
960  */
961 
962 int cleanopen(char *line)
963 {
964     int t;
965 
966 #ifdef STREAMSPTY
967     if (!really_stream)
968 #endif
969 	{
970 	    /*
971 	     * Make sure that other people can't open the
972 	     * slave side of the connection.
973 	     */
974 	    chown(line, 0, 0);
975 	    chmod(line, 0600);
976 	}
977 
978 #ifdef HAVE_REVOKE
979     revoke(line);
980 #endif
981 
982     t = open(line, O_RDWR|O_NOCTTY);
983 
984     if (t < 0)
985 	return(-1);
986 
987     /*
988      * Hangup anybody else using this ttyp, then reopen it for
989      * ourselves.
990      */
991 # if !(defined(_CRAY) || defined(__hpux)) && (BSD <= 43) && !defined(STREAMSPTY)
992     signal(SIGHUP, SIG_IGN);
993 #ifdef HAVE_VHANGUP
994     vhangup();
995 #else
996 #endif
997     signal(SIGHUP, SIG_DFL);
998     t = open(line, O_RDWR|O_NOCTTY);
999     if (t < 0)
1000 	return(-1);
1001 # endif
1002 # if	defined(_CRAY) && defined(TCVHUP)
1003     {
1004 	int i;
1005 	signal(SIGHUP, SIG_IGN);
1006 	ioctl(t, TCVHUP, (char *)0);
1007 	signal(SIGHUP, SIG_DFL);
1008 
1009 	i = open(line, O_RDWR);
1010 
1011 	if (i < 0)
1012 	    return(-1);
1013 	close(t);
1014 	t = i;
1015     }
1016 # endif	/* defined(CRAY) && defined(TCVHUP) */
1017     return(t);
1018 }
1019 
1020 #if !defined(BSD4_4)
1021 
1022 int login_tty(int t)
1023 {
1024 # if defined(TIOCSCTTY) && !defined(__hpux)
1025     if (ioctl(t, TIOCSCTTY, (char *)0) < 0)
1026 	fatalperror(net, "ioctl(sctty)");
1027 #  ifdef _CRAY
1028     /*
1029      * Close the hard fd to /dev/ttypXXX, and re-open through
1030      * the indirect /dev/tty interface.
1031      */
1032     close(t);
1033     if ((t = open("/dev/tty", O_RDWR)) < 0)
1034 	fatalperror(net, "open(/dev/tty)");
1035 #  endif
1036 # else
1037     /*
1038      * We get our controlling tty assigned as a side-effect
1039      * of opening up a tty device.  But on BSD based systems,
1040      * this only happens if our process group is zero.  The
1041      * setsid() call above may have set our pgrp, so clear
1042      * it out before opening the tty...
1043      */
1044 #ifdef HAVE_SETPGID
1045     setpgid(0, 0);
1046 #else
1047     setpgrp(0, 0); /* if setpgid isn't available, setpgrp
1048 		      probably takes arguments */
1049 #endif
1050     close(open(line, O_RDWR));
1051 # endif
1052     if (t != 0)
1053 	dup2(t, 0);
1054     if (t != 1)
1055 	dup2(t, 1);
1056     if (t != 2)
1057 	dup2(t, 2);
1058     if (t > 2)
1059 	close(t);
1060     return(0);
1061 }
1062 #endif	/* BSD <= 43 */
1063 
1064 /*
1065  * This comes from ../../bsd/tty.c and should not really be here.
1066  */
1067 
1068 /*
1069  * Clean the tty name.  Return a pointer to the cleaned version.
1070  */
1071 
1072 static char *
1073 clean_ttyname (char *tty)
1074 {
1075   char *res = tty;
1076 
1077   if (strncmp (res, _PATH_DEV, strlen(_PATH_DEV)) == 0)
1078     res += strlen(_PATH_DEV);
1079   if (strncmp (res, "pty/", 4) == 0)
1080     res += 4;
1081   if (strncmp (res, "ptym/", 5) == 0)
1082     res += 5;
1083   return res;
1084 }
1085 
1086 /*
1087  * Generate a name usable as an `ut_id', typically without `tty'.
1088  */
1089 
1090 #ifdef HAVE_STRUCT_UTMP_UT_ID
1091 static char *
1092 make_id (char *tty)
1093 {
1094   char *res = tty;
1095 
1096   if (strncmp (res, "pts/", 4) == 0)
1097     res += 4;
1098   if (strncmp (res, "tty", 3) == 0)
1099     res += 3;
1100   return res;
1101 }
1102 #endif
1103 
1104 /*
1105  * startslave(host)
1106  *
1107  * Given a hostname, do whatever
1108  * is necessary to startup the login process on the slave side of the pty.
1109  */
1110 
1111 /* ARGSUSED */
1112 void
1113 startslave(char *host, int autologin, char *autoname)
1114 {
1115     int i;
1116 
1117 #ifdef AUTHENTICATION
1118     if (!autoname || !autoname[0])
1119 	autologin = 0;
1120 
1121     if (autologin < auth_level) {
1122 	fatal(net, "Authorization failed");
1123 	exit(1);
1124     }
1125 #endif
1126 
1127     {
1128 	char *tbuf =
1129 	    "\r\n*** Connection not encrypted! "
1130 	    "Communication may be eavesdropped. ***\r\n";
1131 #ifdef ENCRYPTION
1132 	if (!no_warn && (encrypt_output == 0 || decrypt_input == 0))
1133 #endif
1134 	    writenet((unsigned char*)tbuf, strlen(tbuf));
1135     }
1136 # ifdef	PARENT_DOES_UTMP
1137     utmp_sig_init();
1138 # endif	/* PARENT_DOES_UTMP */
1139 
1140     if ((i = fork()) < 0)
1141 	fatalperror(net, "fork");
1142     if (i) {
1143 # ifdef PARENT_DOES_UTMP
1144 	/*
1145 	 * Cray parent will create utmp entry for child and send
1146 	 * signal to child to tell when done.  Child waits for signal
1147 	 * before doing anything important.
1148 	 */
1149 	int pid = i;
1150 	void sigjob (int);
1151 
1152 	setpgrp();
1153 	utmp_sig_reset();		/* reset handler to default */
1154 	/*
1155 	 * Create utmp entry for child
1156 	 */
1157 	time(&wtmp.ut_time);
1158 	wtmp.ut_type = LOGIN_PROCESS;
1159 	wtmp.ut_pid = pid;
1160 	strncpy(wtmp.ut_user,  "LOGIN", sizeof(wtmp.ut_user));
1161 	strncpy(wtmp.ut_host,  host, sizeof(wtmp.ut_host));
1162 	strncpy(wtmp.ut_line,  clean_ttyname(line), sizeof(wtmp.ut_line));
1163 #ifdef HAVE_STRUCT_UTMP_UT_ID
1164 	strncpy(wtmp.ut_id, wtmp.ut_line + 3, sizeof(wtmp.ut_id));
1165 #endif
1166 
1167 	pututline(&wtmp);
1168 	endutent();
1169 	if ((i = open(wtmpf, O_WRONLY|O_APPEND)) >= 0) {
1170 	    write(i, &wtmp, sizeof(struct utmp));
1171 	    close(i);
1172 	}
1173 #ifdef	_CRAY
1174 	signal(WJSIGNAL, sigjob);
1175 #endif
1176 	utmp_sig_notify(pid);
1177 # endif	/* PARENT_DOES_UTMP */
1178     } else {
1179 	getptyslave();
1180 	start_login(host, autologin, autoname);
1181 	/*NOTREACHED*/
1182     }
1183 }
1184 
1185 char	*envinit[3];
1186 extern char **environ;
1187 
1188 void
1189 init_env(void)
1190 {
1191     extern char *getenv(const char *);
1192     char **envp;
1193 
1194     envp = envinit;
1195     if ((*envp = getenv("TZ")))
1196 	*envp++ -= 3;
1197 #if defined(_CRAY) || defined(__hpux)
1198     else
1199 	*envp++ = "TZ=GMT0";
1200 #endif
1201     *envp = 0;
1202     environ = envinit;
1203 }
1204 
1205 /*
1206  * scrub_env()
1207  *
1208  * Remove variables from the environment that might cause login to
1209  * behave in a bad manner. To avoid this, login should be staticly
1210  * linked.
1211  */
1212 
1213 static void scrub_env(void)
1214 {
1215     static char *remove[] = { "LD_", "_RLD_", "LIBPATH=", "IFS=", NULL };
1216 
1217     char **cpp, **cpp2;
1218     char **p;
1219 
1220     for (cpp2 = cpp = environ; *cpp; cpp++) {
1221 	for(p = remove; *p; p++)
1222 	    if(strncmp(*cpp, *p, strlen(*p)) == 0)
1223 		break;
1224 	if(*p == NULL)
1225 	    *cpp2++ = *cpp;
1226     }
1227     *cpp2 = 0;
1228 }
1229 
1230 
1231 struct arg_val {
1232     int size;
1233     int argc;
1234     char **argv;
1235 };
1236 
1237 static void addarg(struct arg_val*, char*);
1238 
1239 /*
1240  * start_login(host)
1241  *
1242  * Assuming that we are now running as a child processes, this
1243  * function will turn us into the login process.
1244  */
1245 
1246 void
1247 start_login(char *host, int autologin, char *name)
1248 {
1249     struct arg_val argv;
1250     char *user;
1251 
1252 #ifdef HAVE_UTMPX_H
1253     int pid = getpid();
1254     struct utmpx utmpx;
1255     char *clean_tty;
1256 
1257     /*
1258      * Create utmp entry for child
1259      */
1260 
1261     clean_tty = clean_ttyname(line);
1262     memset(&utmpx, 0, sizeof(utmpx));
1263     strncpy(utmpx.ut_user,  ".telnet", sizeof(utmpx.ut_user));
1264     strncpy(utmpx.ut_line,  clean_tty, sizeof(utmpx.ut_line));
1265 #ifdef HAVE_STRUCT_UTMP_UT_ID
1266     strncpy(utmpx.ut_id, make_id(clean_tty), sizeof(utmpx.ut_id));
1267 #endif
1268     utmpx.ut_pid = pid;
1269 
1270     utmpx.ut_type = LOGIN_PROCESS;
1271 
1272     gettimeofday (&utmpx.ut_tv, NULL);
1273     if (pututxline(&utmpx) == NULL)
1274 	fatal(net, "pututxline failed");
1275 #endif
1276 
1277     scrub_env();
1278 
1279     /*
1280      * -h : pass on name of host.
1281      *		WARNING:  -h is accepted by login if and only if
1282      *			getuid() == 0.
1283      * -p : don't clobber the environment (so terminal type stays set).
1284      *
1285      * -f : force this login, he has already been authenticated
1286      */
1287 
1288     /* init argv structure */
1289     argv.size=0;
1290     argv.argc=0;
1291     argv.argv=(char**)malloc(0); /*so we can call realloc later */
1292     addarg(&argv, "login");
1293     addarg(&argv, "-h");
1294     addarg(&argv, host);
1295     addarg(&argv, "-p");
1296     if(name[0])
1297 	user = name;
1298     else
1299 	user = getenv("USER");
1300 #ifdef AUTHENTICATION
1301     if (auth_level < 0 || autologin != AUTH_VALID) {
1302 	if(!no_warn) {
1303 	    printf("User not authenticated. ");
1304 	    if (require_otp)
1305 		printf("Using one-time password\r\n");
1306 	    else
1307 		printf("Using plaintext username and password\r\n");
1308 	}
1309 	if (require_otp) {
1310 	    addarg(&argv, "-a");
1311 	    addarg(&argv, "otp");
1312 	}
1313 	if(log_unauth)
1314 	    syslog(LOG_INFO, "unauthenticated access from %s (%s)",
1315 		   host, user ? user : "unknown user");
1316     }
1317     if (auth_level >= 0 && autologin == AUTH_VALID)
1318 	addarg(&argv, "-f");
1319 #endif
1320     if(user){
1321 	addarg(&argv, "--");
1322 	addarg(&argv, strdup(user));
1323     }
1324     if (getenv("USER")) {
1325 	/*
1326 	 * Assume that login will set the USER variable
1327 	 * correctly.  For SysV systems, this means that
1328 	 * USER will no longer be set, just LOGNAME by
1329 	 * login.  (The problem is that if the auto-login
1330 	 * fails, and the user then specifies a different
1331 	 * account name, he can get logged in with both
1332 	 * LOGNAME and USER in his environment, but the
1333 	 * USER value will be wrong.
1334 	 */
1335 	unsetenv("USER");
1336     }
1337     closelog();
1338     /*
1339      * This sleep(1) is in here so that telnetd can
1340      * finish up with the tty.  There's a race condition
1341      * the login banner message gets lost...
1342      */
1343     sleep(1);
1344 
1345     execv(new_login, argv.argv);
1346 
1347     syslog(LOG_ERR, "%s: %m\n", new_login);
1348     fatalperror(net, new_login);
1349     /*NOTREACHED*/
1350 }
1351 
1352 static void
1353 addarg(struct arg_val *argv, char *val)
1354 {
1355     if(argv->size <= argv->argc+1) {
1356 	argv->argv = realloc(argv->argv, sizeof(char*) * (argv->size + 10));
1357 	if (argv->argv == NULL)
1358 	    fatal (net, "realloc: out of memory");
1359 	argv->size+=10;
1360     }
1361     argv->argv[argv->argc++] = val;
1362     argv->argv[argv->argc]   = NULL;
1363 }
1364 
1365 
1366 /*
1367  * rmut()
1368  *
1369  * This is the function called by cleanup() to
1370  * remove the utmp entry for this person.
1371  */
1372 
1373 #ifdef HAVE_UTMPX_H
1374 static void
1375 rmut(void)
1376 {
1377     struct utmpx utmpx, *non_save_utxp;
1378     char *clean_tty = clean_ttyname(line);
1379 
1380     /*
1381      * This updates the utmpx and utmp entries and make a wtmp/x entry
1382      */
1383 
1384     setutxent();
1385     memset(&utmpx, 0, sizeof(utmpx));
1386     strncpy(utmpx.ut_line, clean_tty, sizeof(utmpx.ut_line));
1387     utmpx.ut_type = LOGIN_PROCESS;
1388     non_save_utxp = getutxline(&utmpx);
1389     if (non_save_utxp) {
1390 	struct utmpx *utxp;
1391 	char user0;
1392 
1393 	utxp = malloc(sizeof(struct utmpx));
1394 	*utxp = *non_save_utxp;
1395 	user0 = utxp->ut_user[0];
1396 	utxp->ut_user[0] = '\0';
1397 	utxp->ut_type = DEAD_PROCESS;
1398 #ifdef HAVE_STRUCT_UTMPX_UT_EXIT
1399 #ifdef _STRUCT___EXIT_STATUS
1400 	utxp->ut_exit.__e_termination = 0;
1401 	utxp->ut_exit.__e_exit = 0;
1402 #elif defined(__osf__) /* XXX */
1403 	utxp->ut_exit.ut_termination = 0;
1404 	utxp->ut_exit.ut_exit = 0;
1405 #else
1406 	utxp->ut_exit.e_termination = 0;
1407 	utxp->ut_exit.e_exit = 0;
1408 #endif
1409 #endif
1410 	gettimeofday(&utxp->ut_tv, NULL);
1411 	pututxline(utxp);
1412 #ifdef WTMPX_FILE
1413 	utxp->ut_user[0] = user0;
1414 	updwtmpx(WTMPX_FILE, utxp);
1415 #elif defined(WTMP_FILE)
1416 	/* This is a strange system with a utmpx and a wtmp! */
1417 	{
1418 	  int f = open(wtmpf, O_WRONLY|O_APPEND);
1419 	  struct utmp wtmp;
1420 	  if (f >= 0) {
1421 	    strncpy(wtmp.ut_line,  clean_tty, sizeof(wtmp.ut_line));
1422 	    strncpy(wtmp.ut_name,  "", sizeof(wtmp.ut_name));
1423 #ifdef HAVE_STRUCT_UTMP_UT_HOST
1424 	    strncpy(wtmp.ut_host,  "", sizeof(wtmp.ut_host));
1425 #endif
1426 	    time(&wtmp.ut_time);
1427 	    write(f, &wtmp, sizeof(wtmp));
1428 	    close(f);
1429 	  }
1430 	}
1431 #endif
1432 	free (utxp);
1433     }
1434     endutxent();
1435 }  /* end of rmut */
1436 #endif
1437 
1438 #if !defined(HAVE_UTMPX_H) && !(defined(_CRAY) || defined(__hpux)) && BSD <= 43
1439 static void
1440 rmut(void)
1441 {
1442     int f;
1443     int found = 0;
1444     struct utmp *u, *utmp;
1445     int nutmp;
1446     struct stat statbf;
1447     char *clean_tty = clean_ttyname(line);
1448 
1449     f = open(utmpf, O_RDWR);
1450     if (f >= 0) {
1451 	fstat(f, &statbf);
1452 	utmp = (struct utmp *)malloc((unsigned)statbf.st_size);
1453 	if (!utmp)
1454 	    syslog(LOG_ERR, "utmp malloc failed");
1455 	if (statbf.st_size && utmp) {
1456 	    nutmp = read(f, utmp, (int)statbf.st_size);
1457 	    nutmp /= sizeof(struct utmp);
1458 
1459 	    for (u = utmp ; u < &utmp[nutmp] ; u++) {
1460 		if (strncmp(u->ut_line,
1461 			    clean_tty,
1462 			    sizeof(u->ut_line)) ||
1463 		    u->ut_name[0]==0)
1464 		    continue;
1465 		lseek(f, ((long)u)-((long)utmp), L_SET);
1466 		strncpy(u->ut_name,  "", sizeof(u->ut_name));
1467 #ifdef HAVE_STRUCT_UTMP_UT_HOST
1468 		strncpy(u->ut_host,  "", sizeof(u->ut_host));
1469 #endif
1470 		time(&u->ut_time);
1471 		write(f, u, sizeof(wtmp));
1472 		found++;
1473 	    }
1474 	}
1475 	close(f);
1476     }
1477     if (found) {
1478 	f = open(wtmpf, O_WRONLY|O_APPEND);
1479 	if (f >= 0) {
1480 	    strncpy(wtmp.ut_line,  clean_tty, sizeof(wtmp.ut_line));
1481 	    strncpy(wtmp.ut_name,  "", sizeof(wtmp.ut_name));
1482 #ifdef HAVE_STRUCT_UTMP_UT_HOST
1483 	    strncpy(wtmp.ut_host,  "", sizeof(wtmp.ut_host));
1484 #endif
1485 	    time(&wtmp.ut_time);
1486 	    write(f, &wtmp, sizeof(wtmp));
1487 	    close(f);
1488 	}
1489     }
1490     chmod(line, 0666);
1491     chown(line, 0, 0);
1492     line[strlen("/dev/")] = 'p';
1493     chmod(line, 0666);
1494     chown(line, 0, 0);
1495 }  /* end of rmut */
1496 #endif	/* CRAY */
1497 
1498 #if defined(__hpux) && !defined(HAVE_UTMPX_H)
1499 static void
1500 rmut (char *line)
1501 {
1502     struct utmp utmp;
1503     struct utmp *utptr;
1504     int fd;			/* for /etc/wtmp */
1505 
1506     utmp.ut_type = USER_PROCESS;
1507     strncpy(utmp.ut_line, clean_ttyname(line), sizeof(utmp.ut_line));
1508     setutent();
1509     utptr = getutline(&utmp);
1510     /* write it out only if it exists */
1511     if (utptr) {
1512 	utptr->ut_type = DEAD_PROCESS;
1513 	utptr->ut_time = time(NULL);
1514 	pututline(utptr);
1515 	/* set wtmp entry if wtmp file exists */
1516 	if ((fd = open(wtmpf, O_WRONLY | O_APPEND)) >= 0) {
1517 	    write(fd, utptr, sizeof(utmp));
1518 	    close(fd);
1519 	}
1520     }
1521     endutent();
1522 
1523     chmod(line, 0666);
1524     chown(line, 0, 0);
1525     line[14] = line[13];
1526     line[13] = line[12];
1527     line[8] = 'm';
1528     line[9] = '/';
1529     line[10] = 'p';
1530     line[11] = 't';
1531     line[12] = 'y';
1532     chmod(line, 0666);
1533     chown(line, 0, 0);
1534 }
1535 #endif
1536 
1537 /*
1538  * cleanup()
1539  *
1540  * This is the routine to call when we are all through, to
1541  * clean up anything that needs to be cleaned up.
1542  */
1543 
1544 #ifdef PARENT_DOES_UTMP
1545 
1546 void
1547 cleanup(int sig)
1548 {
1549 #ifdef _CRAY
1550     static int incleanup = 0;
1551     int t;
1552     int child_status; /* status of child process as returned by waitpid */
1553     int flags = WNOHANG|WUNTRACED;
1554 
1555     /*
1556      * 1: Pick up the zombie, if we are being called
1557      *    as the signal handler.
1558      * 2: If we are a nested cleanup(), return.
1559      * 3: Try to clean up TMPDIR.
1560      * 4: Fill in utmp with shutdown of process.
1561      * 5: Close down the network and pty connections.
1562      * 6: Finish up the TMPDIR cleanup, if needed.
1563      */
1564     if (sig == SIGCHLD) {
1565 	while (waitpid(-1, &child_status, flags) > 0)
1566 	    ;	/* VOID */
1567 	/* Check if the child process was stopped
1568 	 * rather than exited.  We want cleanup only if
1569 	 * the child has died.
1570 	 */
1571 	if (WIFSTOPPED(child_status)) {
1572 	    return;
1573 	}
1574     }
1575     t = sigblock(sigmask(SIGCHLD));
1576     if (incleanup) {
1577 	sigsetmask(t);
1578 	return;
1579     }
1580     incleanup = 1;
1581     sigsetmask(t);
1582 
1583     t = cleantmp(&wtmp);
1584     setutent();	/* just to make sure */
1585 #endif /* CRAY */
1586     rmut(line);
1587     close(ourpty);
1588     shutdown(net, 2);
1589 #ifdef _CRAY
1590     if (t == 0)
1591 	cleantmp(&wtmp);
1592 #endif /* CRAY */
1593     exit(1);
1594 }
1595 
1596 #else /* PARENT_DOES_UTMP */
1597 
1598 void
1599 cleanup(int sig)
1600 {
1601 #if defined(HAVE_UTMPX_H) || !defined(HAVE_LOGWTMP)
1602     rmut();
1603 #ifdef HAVE_VHANGUP
1604 #ifndef __sgi
1605     vhangup(); /* XXX */
1606 #endif
1607 #endif
1608 #else
1609     char *p;
1610 
1611     p = line + sizeof("/dev/") - 1;
1612     if (logout(p))
1613 	logwtmp(p, "", "");
1614     chmod(line, 0666);
1615     chown(line, 0, 0);
1616     *p = 'p';
1617     chmod(line, 0666);
1618     chown(line, 0, 0);
1619 #endif
1620     shutdown(net, 2);
1621     exit(1);
1622 }
1623 
1624 #endif /* PARENT_DOES_UTMP */
1625 
1626 #ifdef PARENT_DOES_UTMP
1627 /*
1628  * _utmp_sig_rcv
1629  * utmp_sig_init
1630  * utmp_sig_wait
1631  *	These three functions are used to coordinate the handling of
1632  *	the utmp file between the server and the soon-to-be-login shell.
1633  *	The server actually creates the utmp structure, the child calls
1634  *	utmp_sig_wait(), until the server calls utmp_sig_notify() and
1635  *	signals the future-login shell to proceed.
1636  */
1637 static int caught=0;		/* NZ when signal intercepted */
1638 static void (*func)();		/* address of previous handler */
1639 
1640 void
1641 _utmp_sig_rcv(sig)
1642      int sig;
1643 {
1644     caught = 1;
1645     signal(SIGUSR1, func);
1646 }
1647 
1648 void
1649 utmp_sig_init()
1650 {
1651     /*
1652      * register signal handler for UTMP creation
1653      */
1654     if ((int)(func = signal(SIGUSR1, _utmp_sig_rcv)) == -1)
1655 	fatalperror(net, "telnetd/signal");
1656 }
1657 
1658 void
1659 utmp_sig_reset()
1660 {
1661     signal(SIGUSR1, func);	/* reset handler to default */
1662 }
1663 
1664 # ifdef __hpux
1665 # define sigoff() /* do nothing */
1666 # define sigon() /* do nothing */
1667 # endif
1668 
1669 void
1670 utmp_sig_wait()
1671 {
1672     /*
1673      * Wait for parent to write our utmp entry.
1674 	 */
1675     sigoff();
1676     while (caught == 0) {
1677 	pause();	/* wait until we get a signal (sigon) */
1678 	sigoff();	/* turn off signals while we check caught */
1679     }
1680     sigon();		/* turn on signals again */
1681 }
1682 
1683 void
1684 utmp_sig_notify(pid)
1685 {
1686     kill(pid, SIGUSR1);
1687 }
1688 
1689 #ifdef _CRAY
1690 static int gotsigjob = 0;
1691 
1692 	/*ARGSUSED*/
1693 void
1694 sigjob(sig)
1695      int sig;
1696 {
1697     int jid;
1698     struct jobtemp *jp;
1699 
1700     while ((jid = waitjob(NULL)) != -1) {
1701 	if (jid == 0) {
1702 	    return;
1703 	}
1704 	gotsigjob++;
1705 	jobend(jid, NULL, NULL);
1706     }
1707 }
1708 
1709 /*
1710  *	jid_getutid:
1711  *		called by jobend() before calling cleantmp()
1712  *		to find the correct $TMPDIR to cleanup.
1713  */
1714 
1715 struct utmp *
1716 jid_getutid(jid)
1717      int jid;
1718 {
1719     struct utmp *cur = NULL;
1720 
1721     setutent();	/* just to make sure */
1722     while (cur = getutent()) {
1723 	if ( (cur->ut_type != NULL) && (jid == cur->ut_jid) ) {
1724 	    return(cur);
1725 	}
1726     }
1727 
1728     return(0);
1729 }
1730 
1731 /*
1732  * Clean up the TMPDIR that login created.
1733  * The first time this is called we pick up the info
1734  * from the utmp.  If the job has already gone away,
1735  * then we'll clean up and be done.  If not, then
1736  * when this is called the second time it will wait
1737  * for the signal that the job is done.
1738  */
1739 int
1740 cleantmp(wtp)
1741      struct utmp *wtp;
1742 {
1743     struct utmp *utp;
1744     static int first = 1;
1745     int mask, omask, ret;
1746     extern struct utmp *getutid (const struct utmp *_Id);
1747 
1748 
1749     mask = sigmask(WJSIGNAL);
1750 
1751     if (first == 0) {
1752 	omask = sigblock(mask);
1753 	while (gotsigjob == 0)
1754 	    sigpause(omask);
1755 	return(1);
1756     }
1757     first = 0;
1758     setutent();	/* just to make sure */
1759 
1760     utp = getutid(wtp);
1761     if (utp == 0) {
1762 	syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR");
1763 	return(-1);
1764     }
1765     /*
1766      * Nothing to clean up if the user shell was never started.
1767      */
1768     if (utp->ut_type != USER_PROCESS || utp->ut_jid == 0)
1769 	return(1);
1770 
1771     /*
1772      * Block the WJSIGNAL while we are in jobend().
1773      */
1774     omask = sigblock(mask);
1775     ret = jobend(utp->ut_jid, utp->ut_tpath, utp->ut_user);
1776     sigsetmask(omask);
1777     return(ret);
1778 }
1779 
1780 int
1781 jobend(jid, path, user)
1782      int jid;
1783      char *path;
1784      char *user;
1785 {
1786     static int saved_jid = 0;
1787     static int pty_saved_jid = 0;
1788     static char saved_path[sizeof(wtmp.ut_tpath)+1];
1789     static char saved_user[sizeof(wtmp.ut_user)+1];
1790 
1791     /*
1792      * this little piece of code comes into play
1793      * only when ptyreconnect is used to reconnect
1794      * to an previous session.
1795      *
1796      * this is the only time when the
1797      * "saved_jid != jid" code is executed.
1798      */
1799 
1800     if ( saved_jid && saved_jid != jid ) {
1801 	if (!path) {	/* called from signal handler */
1802 	    pty_saved_jid = jid;
1803 	} else {
1804 	    pty_saved_jid = saved_jid;
1805 	}
1806     }
1807 
1808     if (path) {
1809 	strncpy(saved_path, path, sizeof(wtmp.ut_tpath));
1810 	strncpy(saved_user, user, sizeof(wtmp.ut_user));
1811 	saved_path[sizeof(saved_path)] = '\0';
1812 	saved_user[sizeof(saved_user)] = '\0';
1813     }
1814     if (saved_jid == 0) {
1815 	saved_jid = jid;
1816 	return(0);
1817     }
1818 
1819     /* if the jid has changed, get the correct entry from the utmp file */
1820 
1821     if ( saved_jid != jid ) {
1822 	struct utmp *utp = NULL;
1823 	struct utmp *jid_getutid();
1824 
1825 	utp = jid_getutid(pty_saved_jid);
1826 
1827 	if (utp == 0) {
1828 	    syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR");
1829 	    return(-1);
1830 	}
1831 
1832 	cleantmpdir(jid, utp->ut_tpath, utp->ut_user);
1833 	return(1);
1834     }
1835 
1836     cleantmpdir(jid, saved_path, saved_user);
1837     return(1);
1838 }
1839 
1840 /*
1841  * Fork a child process to clean up the TMPDIR
1842  */
1843 cleantmpdir(jid, tpath, user)
1844      int jid;
1845      char *tpath;
1846      char *user;
1847 {
1848     switch(fork()) {
1849     case -1:
1850 	syslog(LOG_ERR, "TMPDIR cleanup(%s): fork() failed: %m\n",
1851 	       tpath);
1852 	break;
1853     case 0:
1854 	execl(CLEANTMPCMD, CLEANTMPCMD, user, tpath, 0);
1855 	syslog(LOG_ERR, "TMPDIR cleanup(%s): execl(%s) failed: %m\n",
1856 	       tpath, CLEANTMPCMD);
1857 	exit(1);
1858     default:
1859 	/*
1860 	 * Forget about child.  We will exit, and
1861 	 * /etc/init will pick it up.
1862 	 */
1863 	break;
1864     }
1865 }
1866 #endif /* CRAY */
1867 #endif	/* defined(PARENT_DOES_UTMP) */
1868