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