sys_term.c revision 78527
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
36RCSID("$Id: sys_term.c,v 1.100 2001/04/24 23:11:43 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
51struct	utmpx wtmp;
52#elif defined(HAVE_UTMP_H)
53struct	utmp wtmp;
54#endif /* HAVE_UTMPX_H */
55
56#ifdef HAVE_STRUCT_UTMP_UT_HOST
57int	utmp_len = sizeof(wtmp.ut_host);
58#else
59int	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
76char	wtmpf[] = WTMP_FILE;
77#else
78char	wtmpf[]	= "/usr/adm/wtmp";
79#endif
80char	utmpf[] = UTMP_FILE;
81#else /* PARENT_DOES_UTMP */
82#ifdef WTMP_FILE
83char	wtmpf[] = WTMP_FILE;
84#else
85char	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
207void
208set_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
235int
236spcset(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 */
333int
334getnpty()
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
356static char Xline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
357char *line = Xline;
358
359#ifdef	_CRAY
360char 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)
364static char *ptsname(int fd)
365{
366#ifdef HAVE_TTYNAME
367    return ttyname(fd);
368#else
369    return NULL;
370#endif
371}
372#endif
373
374int 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
523int
524tty_isecho(void)
525{
526    return (termbuf.c_lflag & ECHO);
527}
528
529int
530tty_flowmode(void)
531{
532    return((termbuf.c_iflag & IXON) ? 1 : 0);
533}
534
535int
536tty_restartany(void)
537{
538    return((termbuf.c_iflag & IXANY) ? 1 : 0);
539}
540
541void
542tty_setecho(int on)
543{
544    if (on)
545	termbuf.c_lflag |= ECHO;
546    else
547	termbuf.c_lflag &= ~ECHO;
548}
549
550int
551tty_israw(void)
552{
553    return(!(termbuf.c_lflag & ICANON));
554}
555
556void
557tty_binaryin(int on)
558{
559    if (on) {
560	termbuf.c_iflag &= ~ISTRIP;
561    } else {
562	termbuf.c_iflag |= ISTRIP;
563    }
564}
565
566void
567tty_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
580int
581tty_isbinaryin(void)
582{
583    return(!(termbuf.c_iflag & ISTRIP));
584}
585
586int
587tty_isbinaryout(void)
588{
589    return(!(termbuf.c_oflag&OPOST));
590}
591
592
593int
594tty_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
604void
605tty_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
626int
627tty_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
640void
641tty_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
657int
658tty_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 */
675struct 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
713void
714tty_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
729void
730tty_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
746extern	struct utmp wtmp;
747extern char wtmpf[];
748
749extern void utmp_sig_init (void);
750extern void utmp_sig_reset (void);
751extern void utmp_sig_wait (void);
752extern 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 */
758static 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
789static 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 */
820void 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
962int 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
1022int 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
1072static char *
1073clean_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
1091static char *
1092make_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 */
1112void
1113startslave(const char *host, const char *utmp_host,
1114	   int autologin, char *autoname)
1115{
1116    int i;
1117
1118#ifdef AUTHENTICATION
1119    if (!autoname || !autoname[0])
1120	autologin = 0;
1121
1122    if (autologin < auth_level) {
1123	fatal(net, "Authorization failed");
1124	exit(1);
1125    }
1126#endif
1127
1128    {
1129	char *tbuf =
1130	    "\r\n*** Connection not encrypted! "
1131	    "Communication may be eavesdropped. ***\r\n";
1132#ifdef ENCRYPTION
1133	if (!no_warn && (encrypt_output == 0 || decrypt_input == 0))
1134#endif
1135	    writenet((unsigned char*)tbuf, strlen(tbuf));
1136    }
1137# ifdef	PARENT_DOES_UTMP
1138    utmp_sig_init();
1139# endif	/* PARENT_DOES_UTMP */
1140
1141    if ((i = fork()) < 0)
1142	fatalperror(net, "fork");
1143    if (i) {
1144# ifdef PARENT_DOES_UTMP
1145	/*
1146	 * Cray parent will create utmp entry for child and send
1147	 * signal to child to tell when done.  Child waits for signal
1148	 * before doing anything important.
1149	 */
1150	int pid = i;
1151	void sigjob (int);
1152
1153	setpgrp();
1154	utmp_sig_reset();		/* reset handler to default */
1155	/*
1156	 * Create utmp entry for child
1157	 */
1158	wtmp.ut_time = time(NULL);
1159	wtmp.ut_type = LOGIN_PROCESS;
1160	wtmp.ut_pid = pid;
1161	strncpy(wtmp.ut_user,  "LOGIN", sizeof(wtmp.ut_user));
1162	strncpy(wtmp.ut_host,  utmp_host, sizeof(wtmp.ut_host));
1163	strncpy(wtmp.ut_line,  clean_ttyname(line), sizeof(wtmp.ut_line));
1164#ifdef HAVE_STRUCT_UTMP_UT_ID
1165	strncpy(wtmp.ut_id, wtmp.ut_line + 3, sizeof(wtmp.ut_id));
1166#endif
1167
1168	pututline(&wtmp);
1169	endutent();
1170	if ((i = open(wtmpf, O_WRONLY|O_APPEND)) >= 0) {
1171	    write(i, &wtmp, sizeof(struct utmp));
1172	    close(i);
1173	}
1174#ifdef	_CRAY
1175	signal(WJSIGNAL, sigjob);
1176#endif
1177	utmp_sig_notify(pid);
1178# endif	/* PARENT_DOES_UTMP */
1179    } else {
1180	getptyslave();
1181#if defined(DCE)
1182	/* if we authenticated via K5, try and join the PAG */
1183	kerberos5_dfspag();
1184#endif
1185	start_login(host, autologin, autoname);
1186	/*NOTREACHED*/
1187    }
1188}
1189
1190char	*envinit[3];
1191extern char **environ;
1192
1193void
1194init_env(void)
1195{
1196    char **envp;
1197
1198    envp = envinit;
1199    if ((*envp = getenv("TZ")))
1200	*envp++ -= 3;
1201#if defined(_CRAY) || defined(__hpux)
1202    else
1203	*envp++ = "TZ=GMT0";
1204#endif
1205    *envp = 0;
1206    environ = envinit;
1207}
1208
1209/*
1210 * scrub_env()
1211 *
1212 * We only accept the environment variables listed below.
1213 */
1214
1215static void
1216scrub_env(void)
1217{
1218    static const char *reject[] = {
1219	"TERMCAP=/",
1220	NULL
1221    };
1222
1223    static const char *accept[] = {
1224	"XAUTH=", "XAUTHORITY=", "DISPLAY=",
1225	"TERM=",
1226	"EDITOR=",
1227	"PAGER=",
1228	"PRINTER=",
1229	"LOGNAME=",
1230	"POSIXLY_CORRECT=",
1231	"TERMCAP=",
1232	NULL
1233    };
1234
1235    char **cpp, **cpp2;
1236    const char **p;
1237
1238    for (cpp2 = cpp = environ; *cpp; cpp++) {
1239	int reject_it = 0;
1240
1241	for(p = reject; *p; p++)
1242	    if(strncmp(*cpp, *p, strlen(*p)) == 0) {
1243		reject_it = 1;
1244		break;
1245	    }
1246	if (reject_it)
1247	    continue;
1248
1249	for(p = accept; *p; p++)
1250	    if(strncmp(*cpp, *p, strlen(*p)) == 0)
1251		break;
1252	if(*p != NULL)
1253	    *cpp2++ = *cpp;
1254    }
1255    *cpp2 = NULL;
1256}
1257
1258
1259struct arg_val {
1260    int size;
1261    int argc;
1262    const char **argv;
1263};
1264
1265static void addarg(struct arg_val*, const char*);
1266
1267/*
1268 * start_login(host)
1269 *
1270 * Assuming that we are now running as a child processes, this
1271 * function will turn us into the login process.
1272 */
1273
1274void
1275start_login(const char *host, int autologin, char *name)
1276{
1277    struct arg_val argv;
1278    char *user;
1279    int save_errno;
1280
1281#ifdef HAVE_UTMPX_H
1282    int pid = getpid();
1283    struct utmpx utmpx;
1284    char *clean_tty;
1285
1286    /*
1287     * Create utmp entry for child
1288     */
1289
1290    clean_tty = clean_ttyname(line);
1291    memset(&utmpx, 0, sizeof(utmpx));
1292    strncpy(utmpx.ut_user,  ".telnet", sizeof(utmpx.ut_user));
1293    strncpy(utmpx.ut_line,  clean_tty, sizeof(utmpx.ut_line));
1294#ifdef HAVE_STRUCT_UTMP_UT_ID
1295    strncpy(utmpx.ut_id, make_id(clean_tty), sizeof(utmpx.ut_id));
1296#endif
1297    utmpx.ut_pid = pid;
1298
1299    utmpx.ut_type = LOGIN_PROCESS;
1300
1301    gettimeofday (&utmpx.ut_tv, NULL);
1302    if (pututxline(&utmpx) == NULL)
1303	fatal(net, "pututxline failed");
1304#endif
1305
1306    scrub_env();
1307
1308    /*
1309     * -h : pass on name of host.
1310     *		WARNING:  -h is accepted by login if and only if
1311     *			getuid() == 0.
1312     * -p : don't clobber the environment (so terminal type stays set).
1313     *
1314     * -f : force this login, he has already been authenticated
1315     */
1316
1317    /* init argv structure */
1318    argv.size=0;
1319    argv.argc=0;
1320    argv.argv=malloc(0); /*so we can call realloc later */
1321    addarg(&argv, "login");
1322    addarg(&argv, "-h");
1323    addarg(&argv, host);
1324    addarg(&argv, "-p");
1325    if(name[0])
1326	user = name;
1327    else
1328	user = getenv("USER");
1329#ifdef AUTHENTICATION
1330    if (auth_level < 0 || autologin != AUTH_VALID) {
1331	if(!no_warn) {
1332	    printf("User not authenticated. ");
1333	    if (require_otp)
1334		printf("Using one-time password\r\n");
1335	    else
1336		printf("Using plaintext username and password\r\n");
1337	}
1338	if (require_otp) {
1339	    addarg(&argv, "-a");
1340	    addarg(&argv, "otp");
1341	}
1342	if(log_unauth)
1343	    syslog(LOG_INFO, "unauthenticated access from %s (%s)",
1344		   host, user ? user : "unknown user");
1345    }
1346    if (auth_level >= 0 && autologin == AUTH_VALID)
1347	addarg(&argv, "-f");
1348#endif
1349    if(user){
1350	addarg(&argv, "--");
1351	addarg(&argv, strdup(user));
1352    }
1353    if (getenv("USER")) {
1354	/*
1355	 * Assume that login will set the USER variable
1356	 * correctly.  For SysV systems, this means that
1357	 * USER will no longer be set, just LOGNAME by
1358	 * login.  (The problem is that if the auto-login
1359	 * fails, and the user then specifies a different
1360	 * account name, he can get logged in with both
1361	 * LOGNAME and USER in his environment, but the
1362	 * USER value will be wrong.
1363	 */
1364	unsetenv("USER");
1365    }
1366    closelog();
1367    /*
1368     * This sleep(1) is in here so that telnetd can
1369     * finish up with the tty.  There's a race condition
1370     * the login banner message gets lost...
1371     */
1372    sleep(1);
1373
1374    execv(new_login, argv.argv);
1375    save_errno = errno;
1376    syslog(LOG_ERR, "%s: %m\n", new_login);
1377    fatalperror_errno(net, new_login, save_errno);
1378    /*NOTREACHED*/
1379}
1380
1381static void
1382addarg(struct arg_val *argv, const char *val)
1383{
1384    if(argv->size <= argv->argc+1) {
1385	argv->argv = realloc(argv->argv, sizeof(char*) * (argv->size + 10));
1386	if (argv->argv == NULL)
1387	    fatal (net, "realloc: out of memory");
1388	argv->size+=10;
1389    }
1390    argv->argv[argv->argc++] = val;
1391    argv->argv[argv->argc]   = NULL;
1392}
1393
1394
1395/*
1396 * rmut()
1397 *
1398 * This is the function called by cleanup() to
1399 * remove the utmp entry for this person.
1400 */
1401
1402#ifdef HAVE_UTMPX_H
1403static void
1404rmut(void)
1405{
1406    struct utmpx utmpx, *non_save_utxp;
1407    char *clean_tty = clean_ttyname(line);
1408
1409    /*
1410     * This updates the utmpx and utmp entries and make a wtmp/x entry
1411     */
1412
1413    setutxent();
1414    memset(&utmpx, 0, sizeof(utmpx));
1415    strncpy(utmpx.ut_line, clean_tty, sizeof(utmpx.ut_line));
1416    utmpx.ut_type = LOGIN_PROCESS;
1417    non_save_utxp = getutxline(&utmpx);
1418    if (non_save_utxp) {
1419	struct utmpx *utxp;
1420	char user0;
1421
1422	utxp = malloc(sizeof(struct utmpx));
1423	*utxp = *non_save_utxp;
1424	user0 = utxp->ut_user[0];
1425	utxp->ut_user[0] = '\0';
1426	utxp->ut_type = DEAD_PROCESS;
1427#ifdef HAVE_STRUCT_UTMPX_UT_EXIT
1428#ifdef _STRUCT___EXIT_STATUS
1429	utxp->ut_exit.__e_termination = 0;
1430	utxp->ut_exit.__e_exit = 0;
1431#elif defined(__osf__) /* XXX */
1432	utxp->ut_exit.ut_termination = 0;
1433	utxp->ut_exit.ut_exit = 0;
1434#else
1435	utxp->ut_exit.e_termination = 0;
1436	utxp->ut_exit.e_exit = 0;
1437#endif
1438#endif
1439	gettimeofday(&utxp->ut_tv, NULL);
1440	pututxline(utxp);
1441#ifdef WTMPX_FILE
1442	utxp->ut_user[0] = user0;
1443	updwtmpx(WTMPX_FILE, utxp);
1444#elif defined(WTMP_FILE)
1445	/* This is a strange system with a utmpx and a wtmp! */
1446	{
1447	  int f = open(wtmpf, O_WRONLY|O_APPEND);
1448	  struct utmp wtmp;
1449	  if (f >= 0) {
1450	    strncpy(wtmp.ut_line,  clean_tty, sizeof(wtmp.ut_line));
1451	    strncpy(wtmp.ut_name,  "", sizeof(wtmp.ut_name));
1452#ifdef HAVE_STRUCT_UTMP_UT_HOST
1453	    strncpy(wtmp.ut_host,  "", sizeof(wtmp.ut_host));
1454#endif
1455	    wtmp.ut_time = time(NULL);
1456	    write(f, &wtmp, sizeof(wtmp));
1457	    close(f);
1458	  }
1459	}
1460#endif
1461	free (utxp);
1462    }
1463    endutxent();
1464}  /* end of rmut */
1465#endif
1466
1467#if !defined(HAVE_UTMPX_H) && !(defined(_CRAY) || defined(__hpux)) && BSD <= 43
1468static void
1469rmut(void)
1470{
1471    int f;
1472    int found = 0;
1473    struct utmp *u, *utmp;
1474    int nutmp;
1475    struct stat statbf;
1476    char *clean_tty = clean_ttyname(line);
1477
1478    f = open(utmpf, O_RDWR);
1479    if (f >= 0) {
1480	fstat(f, &statbf);
1481	utmp = (struct utmp *)malloc((unsigned)statbf.st_size);
1482	if (!utmp)
1483	    syslog(LOG_ERR, "utmp malloc failed");
1484	if (statbf.st_size && utmp) {
1485	    nutmp = read(f, utmp, (int)statbf.st_size);
1486	    nutmp /= sizeof(struct utmp);
1487
1488	    for (u = utmp ; u < &utmp[nutmp] ; u++) {
1489		if (strncmp(u->ut_line,
1490			    clean_tty,
1491			    sizeof(u->ut_line)) ||
1492		    u->ut_name[0]==0)
1493		    continue;
1494		lseek(f, ((long)u)-((long)utmp), L_SET);
1495		strncpy(u->ut_name,  "", sizeof(u->ut_name));
1496#ifdef HAVE_STRUCT_UTMP_UT_HOST
1497		strncpy(u->ut_host,  "", sizeof(u->ut_host));
1498#endif
1499		u->ut_time = time(NULL);
1500		write(f, u, sizeof(wtmp));
1501		found++;
1502	    }
1503	}
1504	close(f);
1505    }
1506    if (found) {
1507	f = open(wtmpf, O_WRONLY|O_APPEND);
1508	if (f >= 0) {
1509	    strncpy(wtmp.ut_line,  clean_tty, sizeof(wtmp.ut_line));
1510	    strncpy(wtmp.ut_name,  "", sizeof(wtmp.ut_name));
1511#ifdef HAVE_STRUCT_UTMP_UT_HOST
1512	    strncpy(wtmp.ut_host,  "", sizeof(wtmp.ut_host));
1513#endif
1514	    wtmp.ut_time = time(NULL);
1515	    write(f, &wtmp, sizeof(wtmp));
1516	    close(f);
1517	}
1518    }
1519    chmod(line, 0666);
1520    chown(line, 0, 0);
1521    line[strlen("/dev/")] = 'p';
1522    chmod(line, 0666);
1523    chown(line, 0, 0);
1524}  /* end of rmut */
1525#endif	/* CRAY */
1526
1527#if defined(__hpux) && !defined(HAVE_UTMPX_H)
1528static void
1529rmut (char *line)
1530{
1531    struct utmp utmp;
1532    struct utmp *utptr;
1533    int fd;			/* for /etc/wtmp */
1534
1535    utmp.ut_type = USER_PROCESS;
1536    strncpy(utmp.ut_line, clean_ttyname(line), sizeof(utmp.ut_line));
1537    setutent();
1538    utptr = getutline(&utmp);
1539    /* write it out only if it exists */
1540    if (utptr) {
1541	utptr->ut_type = DEAD_PROCESS;
1542	utptr->ut_time = time(NULL);
1543	pututline(utptr);
1544	/* set wtmp entry if wtmp file exists */
1545	if ((fd = open(wtmpf, O_WRONLY | O_APPEND)) >= 0) {
1546	    write(fd, utptr, sizeof(utmp));
1547	    close(fd);
1548	}
1549    }
1550    endutent();
1551
1552    chmod(line, 0666);
1553    chown(line, 0, 0);
1554    line[14] = line[13];
1555    line[13] = line[12];
1556    line[8] = 'm';
1557    line[9] = '/';
1558    line[10] = 'p';
1559    line[11] = 't';
1560    line[12] = 'y';
1561    chmod(line, 0666);
1562    chown(line, 0, 0);
1563}
1564#endif
1565
1566/*
1567 * cleanup()
1568 *
1569 * This is the routine to call when we are all through, to
1570 * clean up anything that needs to be cleaned up.
1571 */
1572
1573#ifdef PARENT_DOES_UTMP
1574
1575void
1576cleanup(int sig)
1577{
1578#ifdef _CRAY
1579    static int incleanup = 0;
1580    int t;
1581    int child_status; /* status of child process as returned by waitpid */
1582    int flags = WNOHANG|WUNTRACED;
1583
1584    /*
1585     * 1: Pick up the zombie, if we are being called
1586     *    as the signal handler.
1587     * 2: If we are a nested cleanup(), return.
1588     * 3: Try to clean up TMPDIR.
1589     * 4: Fill in utmp with shutdown of process.
1590     * 5: Close down the network and pty connections.
1591     * 6: Finish up the TMPDIR cleanup, if needed.
1592     */
1593    if (sig == SIGCHLD) {
1594	while (waitpid(-1, &child_status, flags) > 0)
1595	    ;	/* VOID */
1596	/* Check if the child process was stopped
1597	 * rather than exited.  We want cleanup only if
1598	 * the child has died.
1599	 */
1600	if (WIFSTOPPED(child_status)) {
1601	    return;
1602	}
1603    }
1604    t = sigblock(sigmask(SIGCHLD));
1605    if (incleanup) {
1606	sigsetmask(t);
1607	return;
1608    }
1609    incleanup = 1;
1610    sigsetmask(t);
1611
1612    t = cleantmp(&wtmp);
1613    setutent();	/* just to make sure */
1614#endif /* CRAY */
1615    rmut(line);
1616    close(ourpty);
1617    shutdown(net, 2);
1618#ifdef _CRAY
1619    if (t == 0)
1620	cleantmp(&wtmp);
1621#endif /* CRAY */
1622    exit(1);
1623}
1624
1625#else /* PARENT_DOES_UTMP */
1626
1627void
1628cleanup(int sig)
1629{
1630#if defined(HAVE_UTMPX_H) || !defined(HAVE_LOGWTMP)
1631    rmut();
1632#ifdef HAVE_VHANGUP
1633#ifndef __sgi
1634    vhangup(); /* XXX */
1635#endif
1636#endif
1637#else
1638    char *p;
1639
1640    p = line + sizeof("/dev/") - 1;
1641    if (logout(p))
1642	logwtmp(p, "", "");
1643    chmod(line, 0666);
1644    chown(line, 0, 0);
1645    *p = 'p';
1646    chmod(line, 0666);
1647    chown(line, 0, 0);
1648#endif
1649    shutdown(net, 2);
1650    exit(1);
1651}
1652
1653#endif /* PARENT_DOES_UTMP */
1654
1655#ifdef PARENT_DOES_UTMP
1656/*
1657 * _utmp_sig_rcv
1658 * utmp_sig_init
1659 * utmp_sig_wait
1660 *	These three functions are used to coordinate the handling of
1661 *	the utmp file between the server and the soon-to-be-login shell.
1662 *	The server actually creates the utmp structure, the child calls
1663 *	utmp_sig_wait(), until the server calls utmp_sig_notify() and
1664 *	signals the future-login shell to proceed.
1665 */
1666static int caught=0;		/* NZ when signal intercepted */
1667static void (*func)();		/* address of previous handler */
1668
1669void
1670_utmp_sig_rcv(sig)
1671     int sig;
1672{
1673    caught = 1;
1674    signal(SIGUSR1, func);
1675}
1676
1677void
1678utmp_sig_init()
1679{
1680    /*
1681     * register signal handler for UTMP creation
1682     */
1683    if ((int)(func = signal(SIGUSR1, _utmp_sig_rcv)) == -1)
1684	fatalperror(net, "telnetd/signal");
1685}
1686
1687void
1688utmp_sig_reset()
1689{
1690    signal(SIGUSR1, func);	/* reset handler to default */
1691}
1692
1693# ifdef __hpux
1694# define sigoff() /* do nothing */
1695# define sigon() /* do nothing */
1696# endif
1697
1698void
1699utmp_sig_wait()
1700{
1701    /*
1702     * Wait for parent to write our utmp entry.
1703	 */
1704    sigoff();
1705    while (caught == 0) {
1706	pause();	/* wait until we get a signal (sigon) */
1707	sigoff();	/* turn off signals while we check caught */
1708    }
1709    sigon();		/* turn on signals again */
1710}
1711
1712void
1713utmp_sig_notify(pid)
1714{
1715    kill(pid, SIGUSR1);
1716}
1717
1718#ifdef _CRAY
1719static int gotsigjob = 0;
1720
1721	/*ARGSUSED*/
1722void
1723sigjob(sig)
1724     int sig;
1725{
1726    int jid;
1727    struct jobtemp *jp;
1728
1729    while ((jid = waitjob(NULL)) != -1) {
1730	if (jid == 0) {
1731	    return;
1732	}
1733	gotsigjob++;
1734	jobend(jid, NULL, NULL);
1735    }
1736}
1737
1738/*
1739 *	jid_getutid:
1740 *		called by jobend() before calling cleantmp()
1741 *		to find the correct $TMPDIR to cleanup.
1742 */
1743
1744struct utmp *
1745jid_getutid(jid)
1746     int jid;
1747{
1748    struct utmp *cur = NULL;
1749
1750    setutent();	/* just to make sure */
1751    while (cur = getutent()) {
1752	if ( (cur->ut_type != NULL) && (jid == cur->ut_jid) ) {
1753	    return(cur);
1754	}
1755    }
1756
1757    return(0);
1758}
1759
1760/*
1761 * Clean up the TMPDIR that login created.
1762 * The first time this is called we pick up the info
1763 * from the utmp.  If the job has already gone away,
1764 * then we'll clean up and be done.  If not, then
1765 * when this is called the second time it will wait
1766 * for the signal that the job is done.
1767 */
1768int
1769cleantmp(wtp)
1770     struct utmp *wtp;
1771{
1772    struct utmp *utp;
1773    static int first = 1;
1774    int mask, omask, ret;
1775    extern struct utmp *getutid (const struct utmp *_Id);
1776
1777
1778    mask = sigmask(WJSIGNAL);
1779
1780    if (first == 0) {
1781	omask = sigblock(mask);
1782	while (gotsigjob == 0)
1783	    sigpause(omask);
1784	return(1);
1785    }
1786    first = 0;
1787    setutent();	/* just to make sure */
1788
1789    utp = getutid(wtp);
1790    if (utp == 0) {
1791	syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR");
1792	return(-1);
1793    }
1794    /*
1795     * Nothing to clean up if the user shell was never started.
1796     */
1797    if (utp->ut_type != USER_PROCESS || utp->ut_jid == 0)
1798	return(1);
1799
1800    /*
1801     * Block the WJSIGNAL while we are in jobend().
1802     */
1803    omask = sigblock(mask);
1804    ret = jobend(utp->ut_jid, utp->ut_tpath, utp->ut_user);
1805    sigsetmask(omask);
1806    return(ret);
1807}
1808
1809int
1810jobend(jid, path, user)
1811     int jid;
1812     char *path;
1813     char *user;
1814{
1815    static int saved_jid = 0;
1816    static int pty_saved_jid = 0;
1817    static char saved_path[sizeof(wtmp.ut_tpath)+1];
1818    static char saved_user[sizeof(wtmp.ut_user)+1];
1819
1820    /*
1821     * this little piece of code comes into play
1822     * only when ptyreconnect is used to reconnect
1823     * to an previous session.
1824     *
1825     * this is the only time when the
1826     * "saved_jid != jid" code is executed.
1827     */
1828
1829    if ( saved_jid && saved_jid != jid ) {
1830	if (!path) {	/* called from signal handler */
1831	    pty_saved_jid = jid;
1832	} else {
1833	    pty_saved_jid = saved_jid;
1834	}
1835    }
1836
1837    if (path) {
1838	strncpy(saved_path, path, sizeof(wtmp.ut_tpath));
1839	strncpy(saved_user, user, sizeof(wtmp.ut_user));
1840	saved_path[sizeof(saved_path)] = '\0';
1841	saved_user[sizeof(saved_user)] = '\0';
1842    }
1843    if (saved_jid == 0) {
1844	saved_jid = jid;
1845	return(0);
1846    }
1847
1848    /* if the jid has changed, get the correct entry from the utmp file */
1849
1850    if ( saved_jid != jid ) {
1851	struct utmp *utp = NULL;
1852	struct utmp *jid_getutid();
1853
1854	utp = jid_getutid(pty_saved_jid);
1855
1856	if (utp == 0) {
1857	    syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR");
1858	    return(-1);
1859	}
1860
1861	cleantmpdir(jid, utp->ut_tpath, utp->ut_user);
1862	return(1);
1863    }
1864
1865    cleantmpdir(jid, saved_path, saved_user);
1866    return(1);
1867}
1868
1869/*
1870 * Fork a child process to clean up the TMPDIR
1871 */
1872cleantmpdir(jid, tpath, user)
1873     int jid;
1874     char *tpath;
1875     char *user;
1876{
1877    switch(fork()) {
1878    case -1:
1879	syslog(LOG_ERR, "TMPDIR cleanup(%s): fork() failed: %m\n",
1880	       tpath);
1881	break;
1882    case 0:
1883	execl(CLEANTMPCMD, CLEANTMPCMD, user, tpath, 0);
1884	syslog(LOG_ERR, "TMPDIR cleanup(%s): execl(%s) failed: %m\n",
1885	       tpath, CLEANTMPCMD);
1886	exit(1);
1887    default:
1888	/*
1889	 * Forget about child.  We will exit, and
1890	 * /etc/init will pick it up.
1891	 */
1892	break;
1893    }
1894}
1895#endif /* CRAY */
1896#endif	/* defined(PARENT_DOES_UTMP) */
1897