Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / appl / telnet / telnetd / sys_term.c
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.89.2.6 2000/12/08 23:34:05 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         wtmp.ut_time = time(NULL);
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  * We only accept the environment variables listed below.
1209  */
1210
1211 static void
1212 scrub_env(void)
1213 {
1214     static const char *reject[] = {
1215         "TERMCAP=/",
1216         NULL
1217     };
1218
1219     static const char *accept[] = {
1220         "XAUTH=", "XAUTHORITY=", "DISPLAY=",
1221         "TERM=",
1222         "EDITOR=",
1223         "PAGER=",
1224         "PRINTER=",
1225         "LOGNAME=",
1226         "POSIXLY_CORRECT=",
1227         "TERMCAP=",
1228         NULL
1229     };
1230
1231     char **cpp, **cpp2;
1232     const char **p;
1233   
1234     for (cpp2 = cpp = environ; *cpp; cpp++) {
1235         int reject_it = 0;
1236
1237         for(p = reject; *p; p++)
1238             if(strncmp(*cpp, *p, strlen(*p)) == 0) {
1239                 reject_it = 1;
1240                 break;
1241             }
1242         if (reject_it)
1243             continue;
1244
1245         for(p = accept; *p; p++)
1246             if(strncmp(*cpp, *p, strlen(*p)) == 0)
1247                 break;
1248         if(*p != NULL)
1249             *cpp2++ = *cpp;
1250     }
1251     *cpp2 = NULL;
1252 }
1253
1254
1255 struct arg_val {
1256     int size;
1257     int argc;
1258     char **argv;
1259 };
1260
1261 static int addarg(struct arg_val*, char*);
1262
1263 /*
1264  * start_login(host)
1265  *
1266  * Assuming that we are now running as a child processes, this
1267  * function will turn us into the login process.
1268  */
1269
1270 void
1271 start_login(char *host, int autologin, char *name)
1272 {
1273     struct arg_val argv;
1274     char *user;
1275
1276 #ifdef HAVE_UTMPX_H
1277     int pid = getpid();
1278     struct utmpx utmpx;
1279     char *clean_tty;
1280
1281     /*
1282      * Create utmp entry for child
1283      */
1284
1285     clean_tty = clean_ttyname(line);
1286     memset(&utmpx, 0, sizeof(utmpx));
1287     strncpy(utmpx.ut_user,  ".telnet", sizeof(utmpx.ut_user));
1288     strncpy(utmpx.ut_line,  clean_tty, sizeof(utmpx.ut_line));
1289 #ifdef HAVE_STRUCT_UTMP_UT_ID
1290     strncpy(utmpx.ut_id, make_id(clean_tty), sizeof(utmpx.ut_id));
1291 #endif
1292     utmpx.ut_pid = pid;
1293         
1294     utmpx.ut_type = LOGIN_PROCESS;
1295
1296     gettimeofday (&utmpx.ut_tv, NULL);
1297     if (pututxline(&utmpx) == NULL)
1298         fatal(net, "pututxline failed");
1299 #endif
1300
1301     scrub_env();
1302         
1303     /*
1304      * -h : pass on name of host.
1305      *          WARNING:  -h is accepted by login if and only if
1306      *                  getuid() == 0.
1307      * -p : don't clobber the environment (so terminal type stays set).
1308      *
1309      * -f : force this login, he has already been authenticated
1310      */
1311
1312     /* init argv structure */ 
1313     argv.size=0;
1314     argv.argc=0;
1315     argv.argv=(char**)malloc(0); /*so we can call realloc later */
1316     addarg(&argv, "login");
1317     addarg(&argv, "-h");
1318     addarg(&argv, host);
1319     addarg(&argv, "-p");
1320     if(name[0])
1321         user = name;
1322     else
1323         user = getenv("USER");
1324 #ifdef AUTHENTICATION
1325     if (auth_level < 0 || autologin != AUTH_VALID) {
1326         if(!no_warn) {
1327             printf("User not authenticated. ");
1328             if (require_otp)
1329                 printf("Using one-time password\r\n");
1330             else
1331                 printf("Using plaintext username and password\r\n");
1332         }
1333         if (require_otp) {
1334             addarg(&argv, "-a");
1335             addarg(&argv, "otp");
1336         }
1337         if(log_unauth) 
1338             syslog(LOG_INFO, "unauthenticated access from %s (%s)", 
1339                    host, user ? user : "unknown user");
1340     }
1341     if (auth_level >= 0 && autologin == AUTH_VALID)
1342         addarg(&argv, "-f");
1343 #endif
1344     if(user){
1345         addarg(&argv, "--");
1346         addarg(&argv, strdup(user));
1347     }
1348     if (getenv("USER")) {
1349         /*
1350          * Assume that login will set the USER variable
1351          * correctly.  For SysV systems, this means that
1352          * USER will no longer be set, just LOGNAME by
1353          * login.  (The problem is that if the auto-login
1354          * fails, and the user then specifies a different
1355          * account name, he can get logged in with both
1356          * LOGNAME and USER in his environment, but the
1357          * USER value will be wrong.
1358          */
1359         unsetenv("USER");
1360     }
1361     closelog();
1362     /*
1363      * This sleep(1) is in here so that telnetd can
1364      * finish up with the tty.  There's a race condition
1365      * the login banner message gets lost...
1366      */
1367     sleep(1);
1368
1369     execv(new_login, argv.argv);
1370
1371     syslog(LOG_ERR, "%s: %m\n", new_login);
1372     fatalperror(net, new_login);
1373     /*NOTREACHED*/
1374 }
1375
1376
1377
1378 static int addarg(struct arg_val *argv, char *val)
1379 {
1380     if(argv->size <= argv->argc+1){
1381         argv->argv = (char**)realloc(argv->argv, sizeof(char*) * (argv->size + 10));
1382         if(argv->argv == NULL)
1383             return 1; /* this should probably be handled better */
1384         argv->size+=10;
1385     }
1386     argv->argv[argv->argc++]=val;
1387     argv->argv[argv->argc]=NULL;
1388     return 0;
1389 }
1390
1391
1392 /*
1393  * rmut()
1394  *
1395  * This is the function called by cleanup() to
1396  * remove the utmp entry for this person.
1397  */
1398
1399 #ifdef HAVE_UTMPX_H
1400 static void
1401 rmut(void)
1402 {
1403     struct utmpx utmpx, *non_save_utxp;
1404     char *clean_tty = clean_ttyname(line);
1405
1406     /*
1407      * This updates the utmpx and utmp entries and make a wtmp/x entry
1408      */
1409
1410     setutxent();
1411     memset(&utmpx, 0, sizeof(utmpx));
1412     strncpy(utmpx.ut_line, clean_tty, sizeof(utmpx.ut_line));
1413     utmpx.ut_type = LOGIN_PROCESS;
1414     non_save_utxp = getutxline(&utmpx);
1415     if (non_save_utxp) {
1416         struct utmpx *utxp;
1417         char user0;
1418
1419         utxp = malloc(sizeof(struct utmpx));
1420         *utxp = *non_save_utxp;
1421         user0 = utxp->ut_user[0];
1422         utxp->ut_user[0] = '\0';
1423         utxp->ut_type = DEAD_PROCESS;
1424 #ifdef HAVE_STRUCT_UTMPX_UT_EXIT
1425 #ifdef _STRUCT___EXIT_STATUS
1426         utxp->ut_exit.__e_termination = 0;
1427         utxp->ut_exit.__e_exit = 0;
1428 #elif defined(__osf__) /* XXX */
1429         utxp->ut_exit.ut_termination = 0;
1430         utxp->ut_exit.ut_exit = 0;
1431 #else   
1432         utxp->ut_exit.e_termination = 0;
1433         utxp->ut_exit.e_exit = 0;
1434 #endif
1435 #endif
1436         gettimeofday(&utxp->ut_tv, NULL);
1437         pututxline(utxp);
1438 #ifdef WTMPX_FILE
1439         utxp->ut_user[0] = user0;
1440         updwtmpx(WTMPX_FILE, utxp);
1441 #elif defined(WTMP_FILE)
1442         /* This is a strange system with a utmpx and a wtmp! */
1443         {
1444           int f = open(wtmpf, O_WRONLY|O_APPEND);
1445           struct utmp wtmp;
1446           if (f >= 0) {
1447             strncpy(wtmp.ut_line,  clean_tty, sizeof(wtmp.ut_line));
1448             strncpy(wtmp.ut_name,  "", sizeof(wtmp.ut_name));
1449 #ifdef HAVE_STRUCT_UTMP_UT_HOST
1450             strncpy(wtmp.ut_host,  "", sizeof(wtmp.ut_host));
1451 #endif
1452             wtmp.ut_time = time(NULL);
1453             write(f, &wtmp, sizeof(wtmp));
1454             close(f);
1455           }
1456         }
1457 #endif
1458         free (utxp);
1459     }
1460     endutxent();
1461 }  /* end of rmut */
1462 #endif
1463
1464 #if !defined(HAVE_UTMPX_H) && !(defined(_CRAY) || defined(__hpux)) && BSD <= 43
1465 static void
1466 rmut(void)
1467 {
1468     int f;
1469     int found = 0;
1470     struct utmp *u, *utmp;
1471     int nutmp;
1472     struct stat statbf;
1473     char *clean_tty = clean_ttyname(line);
1474
1475     f = open(utmpf, O_RDWR);
1476     if (f >= 0) {
1477         fstat(f, &statbf);
1478         utmp = (struct utmp *)malloc((unsigned)statbf.st_size);
1479         if (!utmp)
1480             syslog(LOG_ERR, "utmp malloc failed");
1481         if (statbf.st_size && utmp) {
1482             nutmp = read(f, utmp, (int)statbf.st_size);
1483             nutmp /= sizeof(struct utmp);
1484
1485             for (u = utmp ; u < &utmp[nutmp] ; u++) {
1486                 if (strncmp(u->ut_line,
1487                             clean_tty,
1488                             sizeof(u->ut_line)) ||
1489                     u->ut_name[0]==0)
1490                     continue;
1491                 lseek(f, ((long)u)-((long)utmp), L_SET);
1492                 strncpy(u->ut_name,  "", sizeof(u->ut_name));
1493 #ifdef HAVE_STRUCT_UTMP_UT_HOST
1494                 strncpy(u->ut_host,  "", sizeof(u->ut_host));
1495 #endif
1496                 u->ut_time = time(NULL);
1497                 write(f, u, sizeof(wtmp));
1498                 found++;
1499             }
1500         }
1501         close(f);
1502     }
1503     if (found) {
1504         f = open(wtmpf, O_WRONLY|O_APPEND);
1505         if (f >= 0) {
1506             strncpy(wtmp.ut_line,  clean_tty, sizeof(wtmp.ut_line));
1507             strncpy(wtmp.ut_name,  "", sizeof(wtmp.ut_name));
1508 #ifdef HAVE_STRUCT_UTMP_UT_HOST
1509             strncpy(wtmp.ut_host,  "", sizeof(wtmp.ut_host));
1510 #endif
1511             wtmp.ut_time = time(NULL);
1512             write(f, &wtmp, sizeof(wtmp));
1513             close(f);
1514         }
1515     }
1516     chmod(line, 0666);
1517     chown(line, 0, 0);
1518     line[strlen("/dev/")] = 'p';
1519     chmod(line, 0666);
1520     chown(line, 0, 0);
1521 }  /* end of rmut */
1522 #endif  /* CRAY */
1523
1524 #if defined(__hpux) && !defined(HAVE_UTMPX_H)
1525 static void
1526 rmut (char *line)
1527 {
1528     struct utmp utmp;
1529     struct utmp *utptr;
1530     int fd;                     /* for /etc/wtmp */
1531
1532     utmp.ut_type = USER_PROCESS;
1533     strncpy(utmp.ut_line, clean_ttyname(line), sizeof(utmp.ut_line));
1534     setutent();
1535     utptr = getutline(&utmp);
1536     /* write it out only if it exists */
1537     if (utptr) {
1538         utptr->ut_type = DEAD_PROCESS;
1539         utptr->ut_time = time(NULL);
1540         pututline(utptr);
1541         /* set wtmp entry if wtmp file exists */
1542         if ((fd = open(wtmpf, O_WRONLY | O_APPEND)) >= 0) {
1543             write(fd, utptr, sizeof(utmp));
1544             close(fd);
1545         }
1546     }
1547     endutent();
1548
1549     chmod(line, 0666);
1550     chown(line, 0, 0);
1551     line[14] = line[13];
1552     line[13] = line[12];
1553     line[8] = 'm';
1554     line[9] = '/';
1555     line[10] = 'p';
1556     line[11] = 't';
1557     line[12] = 'y';
1558     chmod(line, 0666);
1559     chown(line, 0, 0);
1560 }
1561 #endif
1562
1563 /*
1564  * cleanup()
1565  *
1566  * This is the routine to call when we are all through, to
1567  * clean up anything that needs to be cleaned up.
1568  */
1569
1570 #ifdef PARENT_DOES_UTMP
1571
1572 void
1573 cleanup(int sig)
1574 {
1575 #ifdef _CRAY
1576     static int incleanup = 0;
1577     int t;
1578     int child_status; /* status of child process as returned by waitpid */
1579     int flags = WNOHANG|WUNTRACED;
1580     
1581     /*
1582      * 1: Pick up the zombie, if we are being called
1583      *    as the signal handler.
1584      * 2: If we are a nested cleanup(), return.
1585      * 3: Try to clean up TMPDIR.
1586      * 4: Fill in utmp with shutdown of process.
1587      * 5: Close down the network and pty connections.
1588      * 6: Finish up the TMPDIR cleanup, if needed.
1589      */
1590     if (sig == SIGCHLD) {
1591         while (waitpid(-1, &child_status, flags) > 0)
1592             ;   /* VOID */
1593         /* Check if the child process was stopped
1594          * rather than exited.  We want cleanup only if
1595          * the child has died.
1596          */
1597         if (WIFSTOPPED(child_status)) {
1598             return;
1599         }
1600     }
1601     t = sigblock(sigmask(SIGCHLD));
1602     if (incleanup) {
1603         sigsetmask(t);
1604         return;
1605     }
1606     incleanup = 1;
1607     sigsetmask(t);
1608     
1609     t = cleantmp(&wtmp);
1610     setutent(); /* just to make sure */
1611 #endif /* CRAY */
1612     rmut(line);
1613     close(ourpty);
1614     shutdown(net, 2);
1615 #ifdef _CRAY
1616     if (t == 0)
1617         cleantmp(&wtmp);
1618 #endif /* CRAY */
1619     exit(1);
1620 }
1621
1622 #else /* PARENT_DOES_UTMP */
1623
1624 void
1625 cleanup(int sig)
1626 {
1627 #if defined(HAVE_UTMPX_H) || !defined(HAVE_LOGWTMP)
1628     rmut();
1629 #ifdef HAVE_VHANGUP
1630 #ifndef __sgi
1631     vhangup(); /* XXX */
1632 #endif
1633 #endif
1634 #else
1635     char *p;
1636     
1637     p = line + sizeof("/dev/") - 1;
1638     if (logout(p))
1639         logwtmp(p, "", "");
1640     chmod(line, 0666);
1641     chown(line, 0, 0);
1642     *p = 'p';
1643     chmod(line, 0666);
1644     chown(line, 0, 0);
1645 #endif
1646     shutdown(net, 2);
1647     exit(1);
1648 }
1649
1650 #endif /* PARENT_DOES_UTMP */
1651
1652 #ifdef PARENT_DOES_UTMP
1653 /*
1654  * _utmp_sig_rcv
1655  * utmp_sig_init
1656  * utmp_sig_wait
1657  *      These three functions are used to coordinate the handling of
1658  *      the utmp file between the server and the soon-to-be-login shell.
1659  *      The server actually creates the utmp structure, the child calls
1660  *      utmp_sig_wait(), until the server calls utmp_sig_notify() and
1661  *      signals the future-login shell to proceed.
1662  */
1663 static int caught=0;            /* NZ when signal intercepted */
1664 static void (*func)();          /* address of previous handler */
1665
1666 void
1667 _utmp_sig_rcv(sig)
1668      int sig;
1669 {
1670     caught = 1;
1671     signal(SIGUSR1, func);
1672 }
1673
1674 void
1675 utmp_sig_init()
1676 {
1677     /*
1678      * register signal handler for UTMP creation
1679      */
1680     if ((int)(func = signal(SIGUSR1, _utmp_sig_rcv)) == -1)
1681         fatalperror(net, "telnetd/signal");
1682 }
1683
1684 void
1685 utmp_sig_reset()
1686 {
1687     signal(SIGUSR1, func);      /* reset handler to default */
1688 }
1689
1690 # ifdef __hpux
1691 # define sigoff() /* do nothing */
1692 # define sigon() /* do nothing */
1693 # endif
1694
1695 void
1696 utmp_sig_wait()
1697 {
1698     /*
1699      * Wait for parent to write our utmp entry.
1700          */
1701     sigoff();
1702     while (caught == 0) {
1703         pause();        /* wait until we get a signal (sigon) */
1704         sigoff();       /* turn off signals while we check caught */
1705     }
1706     sigon();            /* turn on signals again */
1707 }
1708
1709 void
1710 utmp_sig_notify(pid)
1711 {
1712     kill(pid, SIGUSR1);
1713 }
1714
1715 #ifdef _CRAY
1716 static int gotsigjob = 0;
1717
1718         /*ARGSUSED*/
1719 void
1720 sigjob(sig)
1721      int sig;
1722 {
1723     int jid;
1724     struct jobtemp *jp;
1725
1726     while ((jid = waitjob(NULL)) != -1) {
1727         if (jid == 0) {
1728             return;
1729         }
1730         gotsigjob++;
1731         jobend(jid, NULL, NULL);
1732     }
1733 }
1734
1735 /*
1736  *      jid_getutid:
1737  *              called by jobend() before calling cleantmp()
1738  *              to find the correct $TMPDIR to cleanup.
1739  */
1740
1741 struct utmp *
1742 jid_getutid(jid)
1743      int jid;
1744 {
1745     struct utmp *cur = NULL;
1746
1747     setutent(); /* just to make sure */
1748     while (cur = getutent()) {
1749         if ( (cur->ut_type != NULL) && (jid == cur->ut_jid) ) {
1750             return(cur);
1751         }
1752     }
1753
1754     return(0);
1755 }
1756
1757 /*
1758  * Clean up the TMPDIR that login created.
1759  * The first time this is called we pick up the info
1760  * from the utmp.  If the job has already gone away,
1761  * then we'll clean up and be done.  If not, then
1762  * when this is called the second time it will wait
1763  * for the signal that the job is done.
1764  */
1765 int
1766 cleantmp(wtp)
1767      struct utmp *wtp;
1768 {
1769     struct utmp *utp;
1770     static int first = 1;
1771     int mask, omask, ret;
1772     extern struct utmp *getutid (const struct utmp *_Id);
1773
1774
1775     mask = sigmask(WJSIGNAL);
1776
1777     if (first == 0) {
1778         omask = sigblock(mask);
1779         while (gotsigjob == 0)
1780             sigpause(omask);
1781         return(1);
1782     }
1783     first = 0;
1784     setutent(); /* just to make sure */
1785
1786     utp = getutid(wtp);
1787     if (utp == 0) {
1788         syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR");
1789         return(-1);
1790     }
1791     /*
1792      * Nothing to clean up if the user shell was never started.
1793      */
1794     if (utp->ut_type != USER_PROCESS || utp->ut_jid == 0)
1795         return(1);
1796
1797     /*
1798      * Block the WJSIGNAL while we are in jobend().
1799      */
1800     omask = sigblock(mask);
1801     ret = jobend(utp->ut_jid, utp->ut_tpath, utp->ut_user);
1802     sigsetmask(omask);
1803     return(ret);
1804 }
1805
1806 int
1807 jobend(jid, path, user)
1808      int jid;
1809      char *path;
1810      char *user;
1811 {
1812     static int saved_jid = 0;
1813     static int pty_saved_jid = 0;
1814     static char saved_path[sizeof(wtmp.ut_tpath)+1];
1815     static char saved_user[sizeof(wtmp.ut_user)+1];
1816
1817     /*
1818      * this little piece of code comes into play
1819      * only when ptyreconnect is used to reconnect
1820      * to an previous session.
1821      *
1822      * this is the only time when the
1823      * "saved_jid != jid" code is executed.
1824      */
1825
1826     if ( saved_jid && saved_jid != jid ) {
1827         if (!path) {    /* called from signal handler */
1828             pty_saved_jid = jid;
1829         } else {
1830             pty_saved_jid = saved_jid;
1831         }
1832     }
1833
1834     if (path) {
1835         strncpy(saved_path, path, sizeof(wtmp.ut_tpath));
1836         strncpy(saved_user, user, sizeof(wtmp.ut_user));
1837         saved_path[sizeof(saved_path)] = '\0';
1838         saved_user[sizeof(saved_user)] = '\0';
1839     }
1840     if (saved_jid == 0) {
1841         saved_jid = jid;
1842         return(0);
1843     }
1844
1845     /* if the jid has changed, get the correct entry from the utmp file */
1846
1847     if ( saved_jid != jid ) {
1848         struct utmp *utp = NULL;
1849         struct utmp *jid_getutid();
1850
1851         utp = jid_getutid(pty_saved_jid);
1852
1853         if (utp == 0) {
1854             syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR");
1855             return(-1);
1856         }
1857
1858         cleantmpdir(jid, utp->ut_tpath, utp->ut_user);
1859         return(1);
1860     }
1861
1862     cleantmpdir(jid, saved_path, saved_user);
1863     return(1);
1864 }
1865
1866 /*
1867  * Fork a child process to clean up the TMPDIR
1868  */
1869 cleantmpdir(jid, tpath, user)
1870      int jid;
1871      char *tpath;
1872      char *user;
1873 {
1874     switch(fork()) {
1875     case -1:
1876         syslog(LOG_ERR, "TMPDIR cleanup(%s): fork() failed: %m\n",
1877                tpath);
1878         break;
1879     case 0:
1880         execl(CLEANTMPCMD, CLEANTMPCMD, user, tpath, 0);
1881         syslog(LOG_ERR, "TMPDIR cleanup(%s): execl(%s) failed: %m\n",
1882                tpath, CLEANTMPCMD);
1883         exit(1);
1884     default:
1885         /*
1886          * Forget about child.  We will exit, and
1887          * /etc/init will pick it up.
1888          */
1889         break;
1890     }
1891 }
1892 #endif /* CRAY */
1893 #endif  /* defined(PARENT_DOES_UTMP) */