X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/1c15e88899094343f75aeba04122cd96a96b428e..ad7871609881e73855d0b04da49b486cd93efca7:/usr/src/libexec/telnetd/sys_term.c diff --git a/usr/src/libexec/telnetd/sys_term.c b/usr/src/libexec/telnetd/sys_term.c index 9aa99d5299..55dcd56632 100644 --- a/usr/src/libexec/telnetd/sys_term.c +++ b/usr/src/libexec/telnetd/sys_term.c @@ -1,49 +1,106 @@ /* - * Copyright (c) 1989 Regents of the University of California. - * All rights reserved. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. * - * Redistribution and use in source and binary forms are permitted provided - * that: (1) source distributions retain this entire copyright notice and - * comment, and (2) distributions including binaries display the following - * acknowledgement: ``This product includes software developed by the - * University of California, Berkeley and its contributors'' in the - * documentation or other materials provided with the distribution and in - * all advertising materials mentioning features or use of this software. - * Neither the name of the University nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)sys_term.c 5.10 (Berkeley) 6/30/90"; +static char sccsid[] = "@(#)sys_term.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint */ #include "telnetd.h" #include "pathnames.h" +#if defined(AUTHENTICATION) +#include +#endif + +#if defined(CRAY) || defined(__hpux) +# define PARENT_DOES_UTMP +#endif + #ifdef NEWINIT #include +int utmp_len = MAXHOSTNAMELEN; /* sizeof(init_request.host) */ #else /* NEWINIT*/ -#include +# ifdef UTMPX +# include +# else +# include +# endif /* UTMPX */ struct utmp wtmp; -# ifndef CRAY +int utmp_len = sizeof(wtmp.ut_host); +# ifndef PARENT_DOES_UTMP char wtmpf[] = "/usr/adm/wtmp"; char utmpf[] = "/etc/utmp"; -# else /* CRAY */ +# else /* PARENT_DOES_UTMP */ char wtmpf[] = "/etc/wtmp"; +# endif /* PARENT_DOES_UTMP */ + +# ifdef CRAY +#include +#include +# if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY) + /* + * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can + * use it to tell us to turn off all the socket security code, + * since that is only used in UNICOS 7.0 and later. + */ +# undef _SC_CRAY_SECURE_SYS +# endif + +# if defined(_SC_CRAY_SECURE_SYS) +#include +#include +extern int secflag; +extern struct sysv sysv; +# endif /* _SC_CRAY_SECURE_SYS */ # endif /* CRAY */ #endif /* NEWINIT */ +#ifdef STREAMSPTY +#include +#include +#endif + #define SCPYN(a, b) (void) strncpy(a, b, sizeof(a)) #define SCMPN(a, b) strncmp(a, b, sizeof(a)) #ifdef STREAMS #include #endif +#ifdef __hpux +#include +#include +#endif #include #ifdef t_erase #undef t_erase @@ -74,20 +131,48 @@ struct termbuf { int state; int lflags; } termbuf, termbuf2; +# define cfsetospeed(tp, val) (tp)->sg.sg_ospeed = (val) +# define cfsetispeed(tp, val) (tp)->sg.sg_ispeed = (val) +# define cfgetospeed(tp) (tp)->sg.sg_ospeed +# define cfgetispeed(tp) (tp)->sg.sg_ispeed #else /* USE_TERMIO */ # ifdef SYSV_TERMIO # define termios termio # endif -# ifndef TCSETA +# ifndef TCSANOW # ifdef TCSETS -# define TCSETA TCSETS -# define TCGETA TCGETS +# define TCSANOW TCSETS +# define TCSADRAIN TCSETSW +# define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t) +# else +# ifdef TCSETA +# define TCSANOW TCSETA +# define TCSADRAIN TCSETAW +# define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t) +# else +# define TCSANOW TIOCSETA +# define TCSADRAIN TIOCSETAW +# define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t) +# endif +# endif +# define tcsetattr(f, a, t) ioctl(f, a, t) +# define cfsetospeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ + (tp)->c_cflag |= (val) +# define cfgetospeed(tp) ((tp)->c_cflag & CBAUD) +# ifdef CIBAUD +# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CIBAUD; \ + (tp)->c_cflag |= ((val)<c_cflag & CIBAUD)>>IBSHIFT) # else -# define TCSETA TIOCSETAW -# define TCGETA TIOCGETA +# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ + (tp)->c_cflag |= (val) +# define cfgetispeed(tp) ((tp)->c_cflag & CBAUD) # endif -# endif /* 4.4BSD */ +# endif /* TCSANOW */ struct termios termbuf, termbuf2; /* pty control structure */ +# ifdef STREAMSPTY +int ttyfd = -1; +# endif #endif /* USE_TERMIO */ /* @@ -101,6 +186,7 @@ struct termios termbuf, termbuf2; /* pty control structure */ * set_termbuf() writes the structure into the kernel. */ + void init_termbuf() { #ifndef USE_TERMIO @@ -111,15 +197,20 @@ init_termbuf() (void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state); # endif #else - (void) ioctl(pty, TCGETA, (char *)&termbuf); +# ifdef STREAMSPTY + (void) tcgetattr(ttyfd, &termbuf); +# else + (void) tcgetattr(pty, &termbuf); +# endif #endif termbuf2 = termbuf; } #if defined(LINEMODE) && defined(TIOCPKT_IOCTL) + void copy_termbuf(cp, len) -char *cp; -int len; + char *cp; + int len; { if (len > sizeof(termbuf)) len = sizeof(termbuf); @@ -128,6 +219,7 @@ int len; } #endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */ + void set_termbuf() { /* @@ -135,7 +227,7 @@ set_termbuf() */ #ifndef USE_TERMIO if (bcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg))) - (void) ioctl(pty, TIOCSETP, (char *)&termbuf.sg); + (void) ioctl(pty, TIOCSETN, (char *)&termbuf.sg); if (bcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc))) (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc); if (bcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc, @@ -145,8 +237,12 @@ set_termbuf() (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags); #else /* USE_TERMIO */ if (bcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) - (void) ioctl(pty, TCSETA, (char *)&termbuf); -# if defined(CRAY2) && defined(UNCIOS5) +# ifdef STREAMSPTY + (void) tcsetattr(ttyfd, TCSANOW, &termbuf); +# else + (void) tcsetattr(pty, TCSANOW, &termbuf); +# endif +# if defined(CRAY2) && defined(UNICOS5) needtermstat = 1; # endif #endif /* USE_TERMIO */ @@ -165,10 +261,11 @@ set_termbuf() */ #ifndef USE_TERMIO + int spcset(func, valp, valpp) -int func; -cc_t *valp; -cc_t **valpp; + int func; + cc_t *valp; + cc_t **valpp; { switch(func) { case SLC_EOF: @@ -239,10 +336,11 @@ cc_t **valpp; #else /* USE_TERMIO */ + int spcset(func, valp, valpp) -int func; -cc_t *valp; -cc_t **valpp; + int func; + cc_t *valp; + cc_t **valpp; { #define setval(a, b) *valp = termbuf.c_cc[a]; \ @@ -292,8 +390,11 @@ cc_t **valpp; defval(0); #endif case SLC_AO: -#ifdef VFLUSHO - setval(VFLUSHO, SLC_VARIABLE|SLC_FLUSHOUT); +#if !defined(VDISCARD) && defined(VFLUSHO) +# define VDISCARD VFLUSHO +#endif +#ifdef VDISCARD + setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT); #else defval(0); #endif @@ -311,10 +412,15 @@ cc_t **valpp; case SLC_FORW2: setval(VEOL2, SLC_VARIABLE); #endif + case SLC_AYT: +#ifdef VSTATUS + setval(VSTATUS, SLC_VARIABLE); +#else + defval(0); +#endif case SLC_BRK: case SLC_SYNCH: - case SLC_AYT: case SLC_EOR: defval(0); @@ -332,16 +438,21 @@ cc_t **valpp; * * Return the number of pty's configured into the system. */ + int getnpty() { #ifdef _SC_CRAY_NPTY - return sysconf(_SC_CRAY_NPTY); -#else - return 128; + int numptys; + + if ((numptys = sysconf(_SC_CRAY_NPTY)) != -1) + return numptys; + else #endif /* _SC_CRAY_NPTY */ + return 128; } #endif /* CRAY */ +#ifndef convex /* * getpty() * @@ -350,45 +461,117 @@ getnpty() * * Returns the file descriptor of the opened pty. */ +#ifndef __GNUC__ char *line = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +#else +static char Xline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +char *line = Xline; +#endif +#ifdef CRAY +char *myline = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +#endif /* CRAY */ -getpty() + int +getpty(ptynum) +int *ptynum; { register int p; +#ifdef STREAMSPTY + int t; + char *ptsname(); + + p = open("/dev/ptmx", 2); + if (p > 0) { + grantpt(p); + unlockpt(p); + strcpy(line, ptsname(p)); + return(p); + } + +#else /* ! STREAMSPTY */ #ifndef CRAY - register char c, *p1, *p2; + register char *cp, *p1, *p2; register int i; +#if defined(sun) && defined(TIOCGPGRP) && BSD < 199207 + int dummy; +#endif +#ifndef __hpux (void) sprintf(line, "/dev/ptyXX"); p1 = &line[8]; p2 = &line[9]; +#else + (void) sprintf(line, "/dev/ptym/ptyXX"); + p1 = &line[13]; + p2 = &line[14]; +#endif - for (c = 'p'; c <= 's'; c++) { + for (cp = "pqrstuvwxyzPQRST"; *cp; cp++) { struct stat stb; - *p1 = c; + *p1 = *cp; *p2 = '0'; + /* + * This stat() check is just to keep us from + * looping through all 256 combinations if there + * aren't that many ptys available. + */ if (stat(line, &stb) < 0) break; for (i = 0; i < 16; i++) { *p2 = "0123456789abcdef"[i]; p = open(line, 2); if (p > 0) { +#ifndef __hpux line[5] = 't'; - return(p); +#else + for (p1 = &line[8]; *p1; p1++) + *p1 = *(p1+1); + line[9] = 't'; +#endif + chown(line, 0, 0); + chmod(line, 0600); +#if defined(sun) && defined(TIOCGPGRP) && BSD < 199207 + if (ioctl(p, TIOCGPGRP, &dummy) == 0 + || errno != EIO) { + chmod(line, 0666); + close(p); + line[5] = 'p'; + } else +#endif /* defined(sun) && defined(TIOCGPGRP) && BSD < 199207 */ + return(p); } } } #else /* CRAY */ - register int npty; extern lowpty, highpty; + struct stat sb; - for (npty = lowpty; npty <= highpty; npty++) { - (void) sprintf(line, "/dev/pty/%03d", npty); - p = open(line, 2); + for (*ptynum = lowpty; *ptynum <= highpty; (*ptynum)++) { + (void) sprintf(myline, "/dev/pty/%03d", *ptynum); + p = open(myline, 2); if (p < 0) continue; - (void) sprintf(line, "/dev/ttyp%03d", npty); + (void) sprintf(line, "/dev/ttyp%03d", *ptynum); + /* + * Here are some shenanigans to make sure that there + * are no listeners lurking on the line. + */ + if(stat(line, &sb) < 0) { + (void) close(p); + continue; + } + if(sb.st_uid || sb.st_gid || sb.st_mode != 0600) { + chown(line, 0, 0); + chmod(line, 0600); + (void)close(p); + p = open(myline, 2); + if (p < 0) + continue; + } + /* + * Now it should be safe...check for accessability. + */ if (access(line, 6) == 0) return(p); else { @@ -397,8 +580,10 @@ getpty() } } #endif /* CRAY */ +#endif /* STREAMSPTY */ return(-1); } +#endif /* convex */ #ifdef LINEMODE /* @@ -422,40 +607,49 @@ getpty() * tty_rspeed(val) Set receive speed to val. */ -tty_flowmode() -{ -#ifndef USE_TERMIO - return((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0); -#else - return(termbuf.c_iflag & IXON ? 1 : 0); +#ifdef convex +static int linestate; #endif -} + int tty_linemode() { +#ifndef convex #ifndef USE_TERMIO return(termbuf.state & TS_EXTPROC); #else return(termbuf.c_lflag & EXTPROC); #endif +#else + return(linestate); +#endif } + void tty_setlinemode(on) -int on; + int on; { #ifdef TIOCEXT +# ifndef convex + set_termbuf(); +# else + linestate = on; +# endif (void) ioctl(pty, TIOCEXT, (char *)&on); +# ifndef convex + init_termbuf(); +# endif #else /* !TIOCEXT */ -#ifdef EXTPROC +# ifdef EXTPROC if (on) termbuf.c_lflag |= EXTPROC; else termbuf.c_lflag &= ~EXTPROC; -#endif - set_termbuf(); +# endif #endif /* TIOCEXT */ } + int tty_isecho() { #ifndef USE_TERMIO @@ -466,7 +660,33 @@ tty_isecho() } #endif /* LINEMODE */ + int +tty_flowmode() +{ +#ifndef USE_TERMIO + return(((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0) ? 1 : 0); +#else + return((termbuf.c_iflag & IXON) ? 1 : 0); +#endif +} + + int +tty_restartany() +{ +#ifndef USE_TERMIO +# ifdef DECCTQ + return((termbuf.lflags & DECCTQ) ? 0 : 1); +# else + return(-1); +# endif +#else + return((termbuf.c_iflag & IXANY) ? 1 : 0); +#endif +} + + void tty_setecho(on) + int on; { #ifndef USE_TERMIO if (on) @@ -482,6 +702,7 @@ tty_setecho(on) } #if defined(LINEMODE) && defined(KLUDGELINEMODE) + int tty_israw() { #ifndef USE_TERMIO @@ -492,7 +713,9 @@ tty_israw() } #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + void tty_binaryin(on) + int on; { #ifndef USE_TERMIO if (on) @@ -501,14 +724,16 @@ tty_binaryin(on) termbuf.lflags &= ~LPASS8; #else if (on) { - termbuf.c_lflag &= ~ISTRIP; + termbuf.c_iflag &= ~ISTRIP; } else { - termbuf.c_lflag |= ISTRIP; + termbuf.c_iflag |= ISTRIP; } #endif } + void tty_binaryout(on) + int on; { #ifndef USE_TERMIO if (on) @@ -528,6 +753,7 @@ tty_binaryout(on) #endif } + int tty_isbinaryin() { #ifndef USE_TERMIO @@ -537,6 +763,7 @@ tty_isbinaryin() #endif } + int tty_isbinaryout() { #ifndef USE_TERMIO @@ -547,6 +774,7 @@ tty_isbinaryout() } #ifdef LINEMODE + int tty_isediting() { #ifndef USE_TERMIO @@ -556,6 +784,7 @@ tty_isediting() #endif } + int tty_istrapsig() { #ifndef USE_TERMIO @@ -565,8 +794,9 @@ tty_istrapsig() #endif } + void tty_setedit(on) -int on; + int on; { #ifndef USE_TERMIO if (on) @@ -581,8 +811,9 @@ int on; #endif } + void tty_setsig(on) -int on; + int on; { #ifndef USE_TERMIO if (on) @@ -596,6 +827,7 @@ int on; } #endif /* LINEMODE */ + int tty_issofttab() { #ifndef USE_TERMIO @@ -610,8 +842,9 @@ tty_issofttab() #endif } + void tty_setsofttab(on) -int on; + int on; { #ifndef USE_TERMIO if (on) @@ -639,10 +872,11 @@ int on; #endif } + int tty_islitecho() { #ifndef USE_TERMIO - return (!(termbuf.sg.sg_flags & CTLECH)); + return (!(termbuf.lflags & LCTLECH)); #else # ifdef ECHOCTL return (!(termbuf.c_lflag & ECHOCTL)); @@ -656,14 +890,15 @@ tty_islitecho() #endif } + void tty_setlitecho(on) -int on; + int on; { #ifndef USE_TERMIO if (on) - termbuf.sg.sg_flags &= ~CTLECH; + termbuf.lflags &= ~LCTLECH; else - termbuf.sg.sg_flags |= CTLECH; + termbuf.lflags |= LCTLECH; #else # ifdef ECHOCTL if (on) @@ -678,6 +913,16 @@ int on; termbuf.c_lflag |= TCTLECH; # endif #endif +} + + int +tty_iscrnl() +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & CRMOD); +#else + return (termbuf.c_iflag & ICRNL); +#endif } /* @@ -695,43 +940,30 @@ struct termspeeds { { 38400, B9600 }, { -1, B9600 } }; + void tty_tspeed(val) + int val; { register struct termspeeds *tp; for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) ; -#ifndef USE_TERMIO - termbuf.sg.sg_ospeed = tp->value; -#else -# ifdef CBAUD - termbuf.c_cflag &= ~CBAUD; - termbuf.c_cflag |= tp->value; -# else - termbuf.c_ospeed = tp->value; -# endif -#endif + cfsetospeed(&termbuf, tp->value); } + void tty_rspeed(val) + int val; { register struct termspeeds *tp; for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) ; -#ifndef USE_TERMIO - termbuf.sg.sg_ispeed = tp->value; -#else -# ifdef CBAUD - termbuf.c_cflag &= ~CBAUD; - termbuf.c_cflag |= tp->value; -# else - termbuf.c_ispeed = tp->value; -# endif -#endif + cfsetispeed(&termbuf, tp->value); } #if defined(CRAY2) && defined(UNICOS5) + int tty_isnewmap() { return((termbuf.c_oflag & OPOST) && (termbuf.c_oflag & ONLCR) && @@ -739,21 +971,31 @@ tty_isnewmap() } #endif -#ifdef CRAY +#ifdef PARENT_DOES_UTMP # ifndef NEWINIT extern struct utmp wtmp; extern char wtmpf[]; # else /* NEWINIT */ int gotalarm; -/* ARGSUSED */ -void + + /* ARGSUSED */ + void nologinproc(sig) -int sig; + int sig; { gotalarm++; } # endif /* NEWINIT */ -#endif /* CRAY */ +#endif /* PARENT_DOES_UTMP */ + +#ifndef NEWINIT +# ifdef PARENT_DOES_UTMP +extern void utmp_sig_init P((void)); +extern void utmp_sig_reset P((void)); +extern void utmp_sig_wait P((void)); +extern void utmp_sig_notify P((int)); +# endif /* PARENT_DOES_UTMP */ +#endif /* * getptyslave() @@ -762,96 +1004,329 @@ int sig; * that is necessary. The return value is a file descriptor * for the slave side. */ + int getptyslave() { register int t = -1; -#ifndef CRAY +#if !defined(CRAY) || !defined(NEWINIT) +# ifdef LINEMODE + int waslm; +# endif +# ifdef TIOCGWINSZ + struct winsize ws; + extern int def_row, def_col; +# endif + extern int def_tspeed, def_rspeed; + /* + * Opening the slave side may cause initilization of the + * kernel tty structure. We need remember the state of + * if linemode was turned on + * terminal window size + * terminal speed + * so that we can re-set them if we need to. + */ +# ifdef LINEMODE + waslm = tty_linemode(); +# endif + + /* - * Disassociate self from control terminal and open ttyp side. - * Set important flags on ttyp and ptyp. + * Make sure that we don't have a controlling tty, and + * that we are the session (process group) leader. */ +# ifdef TIOCNOTTY t = open(_PATH_TTY, O_RDWR); if (t >= 0) { (void) ioctl(t, TIOCNOTTY, (char *)0); (void) close(t); } +# endif - t = open(line, O_RDWR); - if (t < 0) - fatalperror(net, line); - if (fchmod(t, 0)) - fatalperror(net, line); -#if BSD <= 43 - (void) signal(SIGHUP, SIG_IGN); - vhangup(); - (void) signal(SIGHUP, SIG_DFL); - t = open(line, O_RDWR); + +# ifdef PARENT_DOES_UTMP + /* + * Wait for our parent to get the utmp stuff to get done. + */ + utmp_sig_wait(); +# endif + + t = cleanopen(line); if (t < 0) fatalperror(net, line); + +#ifdef STREAMSPTY +#ifdef USE_TERMIO + ttyfd = t; +#endif + if (ioctl(t, I_PUSH, "ptem") < 0) + fatal(net, "I_PUSH ptem"); + if (ioctl(t, I_PUSH, "ldterm") < 0) + fatal(net, "I_PUSH ldterm"); + if (ioctl(t, I_PUSH, "ttcompat") < 0) + fatal(net, "I_PUSH ttcompat"); + if (ioctl(pty, I_PUSH, "pckt") < 0) + fatal(net, "I_PUSH pckt"); #endif + /* + * set up the tty modes as we like them to be. + */ init_termbuf(); -#ifndef USE_TERMIO +# ifdef TIOCGWINSZ + if (def_row || def_col) { + bzero((char *)&ws, sizeof(ws)); + ws.ws_col = def_col; + ws.ws_row = def_row; + (void)ioctl(t, TIOCSWINSZ, (char *)&ws); + } +# endif + + /* + * Settings for sgtty based systems + */ +# ifndef USE_TERMIO termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS; - termbuf.sg.sg_ospeed = termbuf.sg.sg_ispeed = B9600; -#else +# endif /* USE_TERMIO */ + + /* + * Settings for UNICOS (and HPUX) + */ +# if defined(CRAY) || defined(__hpux) + termbuf.c_oflag = OPOST|ONLCR|TAB3; + termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON; + termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; + termbuf.c_cflag = EXTB|HUPCL|CS8; +# endif + + /* + * Settings for all other termios/termio based + * systems, other than 4.4BSD. In 4.4BSD the + * kernel does the initial terminal setup. + */ +# if defined(USE_TERMIO) && !(defined(CRAY) || defined(__hpux)) && (BSD <= 43) +# ifndef OXTABS +# define OXTABS 0 +# endif termbuf.c_lflag |= ECHO; -#ifndef OXTABS -#define OXTABS 0 -#endif termbuf.c_oflag |= ONLCR|OXTABS; termbuf.c_iflag |= ICRNL; termbuf.c_iflag &= ~IXOFF; -# ifdef CBAUD - termbuf.c_cflag &= ~CBAUD; - termbuf.c_cflag |= B9600; -# else /* CBAUD */ - termbuf.c_ospeed = termbuf.c_ispeed = B9600; -# endif /* CBAUD */ -#endif +# endif /* defined(USE_TERMIO) && !defined(CRAY) && (BSD <= 43) */ + tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600); + tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600); +# ifdef LINEMODE + if (waslm) + tty_setlinemode(1); +# endif /* LINEMODE */ + + /* + * Set the tty modes, and make this our controlling tty. + */ set_termbuf(); -#else /* CRAY */ + if (login_tty(t) == -1) + fatalperror(net, "login_tty"); +#endif /* !defined(CRAY) || !defined(NEWINIT) */ + if (net > 2) + (void) close(net); + if (pty > 2) + (void) close(pty); +} + +#if !defined(CRAY) || !defined(NEWINIT) +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif +/* + * Open the specified slave side of the pty, + * making sure that we have a clean tty. + */ + int +cleanopen(line) + char *line; +{ + register int t; +#if defined(_SC_CRAY_SECURE_SYS) + struct secstat secbuf; +#endif /* _SC_CRAY_SECURE_SYS */ + +#ifndef STREAMSPTY + /* + * Make sure that other people can't open the + * slave side of the connection. + */ (void) chown(line, 0, 0); (void) chmod(line, 0600); -#endif /* CRAY */ +#endif + +# if !defined(CRAY) && (BSD > 43) + (void) revoke(line); +# endif +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (secstat(line, &secbuf) < 0) + return(-1); + if (setulvl(secbuf.st_slevel) < 0) + return(-1); + if (setucmp(secbuf.st_compart) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + t = open(line, O_RDWR|O_NOCTTY); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (setulvl(sysv.sy_minlvl) < 0) + return(-1); + if (setucmp(0) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + if (t < 0) + return(-1); + + /* + * Hangup anybody else using this ttyp, then reopen it for + * ourselves. + */ +# if !(defined(CRAY) || defined(__hpux)) && (BSD <= 43) && !defined(STREAMSPTY) + (void) signal(SIGHUP, SIG_IGN); + vhangup(); + (void) signal(SIGHUP, SIG_DFL); + t = open(line, O_RDWR|O_NOCTTY); + if (t < 0) + return(-1); +# endif +# if defined(CRAY) && defined(TCVHUP) + { + register int i; + (void) signal(SIGHUP, SIG_IGN); + (void) ioctl(t, TCVHUP, (char *)0); + (void) signal(SIGHUP, SIG_DFL); + setpgrp(); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (secstat(line, &secbuf) < 0) + return(-1); + if (setulvl(secbuf.st_slevel) < 0) + return(-1); + if (setucmp(secbuf.st_compart) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + i = open(line, O_RDWR); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (setulvl(sysv.sy_minlvl) < 0) + return(-1); + if (setucmp(0) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + if (i < 0) + return(-1); + (void) close(t); + t = i; + } +# endif /* defined(CRAY) && defined(TCVHUP) */ return(t); } +#endif /* !defined(CRAY) || !defined(NEWINIT) */ + +#if BSD <= 43 + int +login_tty(t) + int t; +{ + if (setsid() < 0) + fatalperror(net, "setsid()"); +# ifdef TIOCSCTTY + if (ioctl(t, TIOCSCTTY, (char *)0) < 0) + fatalperror(net, "ioctl(sctty)"); +# if defined(CRAY) + /* + * Close the hard fd to /dev/ttypXXX, and re-open through + * the indirect /dev/tty interface. + */ + close(t); + if ((t = open("/dev/tty", O_RDWR)) < 0) + fatalperror(net, "open(/dev/tty)"); +# endif +# else + close(open(line, O_RDWR)); +# endif + if (t != 0) + (void) dup2(t, 0); + if (t != 1) + (void) dup2(t, 1); + if (t != 2) + (void) dup2(t, 2); + if (t > 2) + close(t); + return(0); +} +#endif /* BSD <= 43 */ #ifdef NEWINIT char *gen_id = "fe"; #endif /* - * startslave(t, host) + * startslave(host) * - * Given a file descriptor (t) for a tty, and a hostname, do whatever + * Given a hostname, do whatever * is necessary to startup the login process on the slave side of the pty. */ /* ARGSUSED */ -startslave(t, host) -int t; -char *host; + void +startslave(host, autologin, autoname) + char *host; + int autologin; + char *autoname; { register int i; long time(); + char name[256]; +#ifdef NEWINIT + extern char *ptyip; + struct init_request request; + void nologinproc(); + register int n; +#endif /* NEWINIT */ + +#if defined(AUTHENTICATION) + if (!autoname || !autoname[0]) + autologin = 0; + + if (autologin < auth_level) { + fatal(net, "Authorization failed"); + exit(1); + } +#endif #ifndef NEWINIT -# ifdef CRAY +# ifdef PARENT_DOES_UTMP utmp_sig_init(); -# endif /* CRAY */ +# endif /* PARENT_DOES_UTMP */ if ((i = fork()) < 0) fatalperror(net, "fork"); if (i) { -# ifdef CRAY +# ifdef PARENT_DOES_UTMP /* * Cray parent will create utmp entry for child and send * signal to child to tell when done. Child waits for signal * before doing anything important. */ register int pid = i; + void sigjob P((int)); setpgrp(); utmp_sig_reset(); /* reset handler to default */ @@ -864,27 +1339,29 @@ char *host; SCPYN(wtmp.ut_user, "LOGIN"); SCPYN(wtmp.ut_host, host); SCPYN(wtmp.ut_line, line + sizeof("/dev/") - 1); +#ifndef __hpux SCPYN(wtmp.ut_id, wtmp.ut_line+3); +#else + SCPYN(wtmp.ut_id, wtmp.ut_line+7); +#endif pututline(&wtmp); endutent(); if ((i = open(wtmpf, O_WRONLY|O_APPEND)) >= 0) { (void) write(i, (char *)&wtmp, sizeof(struct utmp)); (void) close(i); } +#ifdef CRAY + (void) signal(WJSIGNAL, sigjob); +#endif utmp_sig_notify(pid); -# endif /* CRAY */ - (void) close(t); +# endif /* PARENT_DOES_UTMP */ } else { - start_login(t, host); + getptyslave(); + start_login(host, autologin, autoname); /*NOTREACHED*/ } #else /* NEWINIT */ - extern char *ptyip; - struct init_request request; - void nologinproc(); - register int n; - /* * Init will start up login process if we ask nicely. We only wait * for it to start up and begin normal telnet operation. @@ -899,7 +1376,7 @@ char *host; SCPYN(request.gen_id, gen_id); SCPYN(request.tty_id, &line[8]); SCPYN(request.host, host); - SCPYN(request.term_type, terminaltype); + SCPYN(request.term_type, terminaltype ? terminaltype : "network"); #if !defined(UNICOS5) request.signal = SIGCLD; request.pid = getpid(); @@ -942,6 +1419,7 @@ char *host; char *envinit[3]; extern char **environ; + void init_env() { extern char *getenv(); @@ -950,7 +1428,7 @@ init_env() envp = envinit; if (*envp = getenv("TZ")) *envp++ -= 3; -#ifdef CRAY +#if defined(CRAY) || defined(__hpux) else *envp++ = "TZ=GMT0"; #endif @@ -958,104 +1436,80 @@ init_env() environ = envinit; } -#ifdef CRAY -/* - * These are environment variable that we - * don't put on the argument line. - */ -char *invalid[] = { - "USER=", /* Set up by login */ - "HOME=", /* Set up by login */ - "LOGNAME=", /* Set up by login */ - "TMPDIR=", /* Set up by login */ - "SHELL=", /* Set up by login */ - "PATH=", /* Set up by login */ - "MAIL=", /* Set up by login */ - "TZ=", /* Login gets it from the environment */ - "TERM=", /* Login gets it from the environment */ - 0 -}; -#endif - #ifndef NEWINIT /* - * start_login(t, host) + * start_login(host) * * Assuming that we are now running as a child processes, this * function will turn us into the login process. */ -start_login(t, host) -int t; -char *host; + void +start_login(host, autologin, name) + char *host; + int autologin; + char *name; { register char *cp; register char **argv; char **addarg(); -#ifdef CRAY - register char **cpp, **cpp2; - utmp_sig_wait(); -# ifndef TCVHUP - setpgrp(); -# endif - t = open(line, 2); /* open ttyp */ - if (t < 0) - fatalperror(net, line); -# ifdef TCVHUP - /* - * Hangup anybody else using this ttyp, then reopen it for - * ourselves. - */ - (void) chown(line, 0, 0); - (void) chmod(line, 0600); - (void) signal(SIGHUP, SIG_IGN); - (void) ioctl(t, TCVHUP, (char *)0); - (void) signal(SIGHUP, SIG_DFL); - setpgrp(); - i = open(line, 2); - if (i < 0) - fatalperror(net, line); - (void) close(t); - t = i; -# endif /* TCVHUP */ - /* - * set ttyp modes as we like them to be - */ - init_termbuf(); - termbuf.c_oflag = OPOST|ONLCR|TAB3; - termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON; - termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; - termbuf.c_cflag = EXTB|HUPCL|CS8; - set_termbuf(); -#endif /* CRAY */ +#ifdef UTMPX + register int pid = getpid(); + struct utmpx utmpx; +#endif +#ifdef __svr4__ + char *term; + char termbuf[64]; +#endif +#ifdef UTMPX /* - * set up standard paths before forking to login + * Create utmp entry for child */ -#if BSD > 43 - if (login_tty(t) == -1) - fatalperror(net, "login_tty"); -#else - (void) dup2(t, 0); - (void) dup2(t, 1); - (void) dup2(t, 2); - (void) close(t); + + bzero(&utmpx, sizeof(utmpx)); + SCPYN(utmpx.ut_user, ".telnet"); + SCPYN(utmpx.ut_line, line + sizeof("/dev/") - 1); + utmpx.ut_pid = pid; + utmpx.ut_id[0] = 't'; + utmpx.ut_id[1] = 'n'; + utmpx.ut_id[2] = SC_WILDC; + utmpx.ut_id[3] = SC_WILDC; + utmpx.ut_type = LOGIN_PROCESS; + (void) time(&utmpx.ut_tv.tv_sec); + if (makeutx(&utmpx) == NULL) + fatal(net, "makeutx failed"); #endif - if (net > 2) - (void) close(net); - if (pty > 2) - (void) close(pty); + /* * -h : pass on name of host. * WARNING: -h is accepted by login if and only if * getuid() == 0. * -p : don't clobber the environment (so terminal type stays set). + * + * -f : force this login, he has already been authenticated */ argv = addarg(0, "login"); +#if !defined(NO_LOGIN_H) argv = addarg(argv, "-h"); argv = addarg(argv, host); -#if !defined(CRAY) && !defined(NO_LOGIN_P) +#endif +#ifdef __svr4__ + /* + * SVR4 version of -h takes TERM= as second arg, or - + */ + term = getenv("TERM"); + if (term == NULL || term[0] == 0) { + term = "-"; + } else { + strcpy(termbuf, "TERM="); + strncat(termbuf, term, sizeof(termbuf) - 6); + term = termbuf; + } + argv = addarg(argv, term); +#endif +#if !defined(NO_LOGIN_P) argv = addarg(argv, "-p"); #endif #ifdef BFTPDAEMON @@ -1067,21 +1521,45 @@ char *host; argv = addarg(argv, "-e"); argv = addarg(argv, BFTPPATH); } else +#endif +#if defined (SecurID) + /* + * don't worry about the -f that might get sent. + * A -s is supposed to override it anyhow. + */ + if (require_SecurID) + argv = addarg(argv, "-s"); +#endif +#if defined (AUTHENTICATION) + if (auth_level >= 0 && autologin == AUTH_VALID) { +# if !defined(NO_LOGIN_F) + argv = addarg(argv, "-f"); +# endif + argv = addarg(argv, name); + } else #endif if (getenv("USER")) { argv = addarg(argv, getenv("USER")); - } -#ifdef CRAY - for (cpp = environ; *cpp; cpp++) { - for (cpp2 = invalid; *cpp2; cpp2++) - if (strncmp(*cpp2, *cpp, strlen(*cpp2)) == 0) - break; - if (*cpp2) - continue; - argv = addarg(argv, *cpp); - } +#if (defined(CRAY) || defined(__hpux)) && defined(NO_LOGIN_P) + { + register char **cpp; + for (cpp = environ; *cpp; cpp++) + argv = addarg(argv, *cpp); + } #endif - + /* + * Assume that login will set the USER variable + * correctly. For SysV systems, this means that + * USER will no longer be set, just LOGNAME by + * login. (The problem is that if the auto-login + * fails, and the user then specifies a different + * account name, he can get logged in with both + * LOGNAME and USER in his environment, but the + * USER value will be wrong. + */ + unsetenv("USER"); + } + closelog(); execv(_PATH_LOGIN, argv); syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN); @@ -1089,13 +1567,12 @@ char *host; /*NOTREACHED*/ } -char ** + char ** addarg(argv, val) -register char **argv; -register char *val; + register char **argv; + register char *val; { register char **cpp; - char *malloc(); if (argv == NULL) { /* @@ -1122,7 +1599,7 @@ register char *val; *cpp = 0; return(argv); } -#endif NEWINIT +#endif /* NEWINIT */ /* * cleanup() @@ -1130,11 +1607,13 @@ register char *val; * This is the routine to call when we are all through, to * clean up anything that needs to be cleaned up. */ -cleanup() + /* ARGSUSED */ + void +cleanup(sig) + int sig; { - -#ifndef CRAY -# if BSD > 43 +#ifndef PARENT_DOES_UTMP +# if (BSD > 43) || defined(convex) char *p; p = line + sizeof("/dev/") - 1; @@ -1145,24 +1624,70 @@ cleanup() *p = 'p'; (void)chmod(line, 0666); (void)chown(line, 0, 0); + (void) shutdown(net, 2); + exit(1); # else + void rmut(); + rmut(); vhangup(); /* XXX */ -# endif (void) shutdown(net, 2); -#else /* CRAY */ -# ifndef NEWINIT - rmut(line); + exit(1); +# endif +#else /* PARENT_DOES_UTMP */ +# ifdef NEWINIT (void) shutdown(net, 2); - kill(0, SIGHUP); + exit(1); # else /* NEWINIT */ +# ifdef CRAY + static int incleanup = 0; + register int t; + + /* + * 1: Pick up the zombie, if we are being called + * as the signal handler. + * 2: If we are a nested cleanup(), return. + * 3: Try to clean up TMPDIR. + * 4: Fill in utmp with shutdown of process. + * 5: Close down the network and pty connections. + * 6: Finish up the TMPDIR cleanup, if needed. + */ + if (sig == SIGCHLD) + while (waitpid(-1, 0, WNOHANG) > 0) + ; /* VOID */ + t = sigblock(sigmask(SIGCHLD)); + if (incleanup) { + sigsetmask(t); + return; + } + incleanup = 1; + sigsetmask(t); + if (secflag) { + /* + * We need to set ourselves back to a null + * label to clean up. + */ + + setulvl(sysv.sy_minlvl); + setucmp((long)0); + } + + t = cleantmp(&wtmp); + setutent(); /* just to make sure */ +# endif /* CRAY */ + rmut(line); + close(pty); (void) shutdown(net, 2); -# endif /* NEWINT */ -#endif /* CRAY */ +# ifdef CRAY + if (t == 0) + cleantmp(&wtmp); +# endif /* CRAY */ exit(1); +# endif /* NEWINT */ +#endif /* PARENT_DOES_UTMP */ } -#if defined(CRAY) && !defined(NEWINIT) +#if defined(PARENT_DOES_UTMP) && !defined(NEWINIT) /* * _utmp_sig_rcv * utmp_sig_init @@ -1176,14 +1701,15 @@ cleanup() static int caught=0; /* NZ when signal intercepted */ static void (*func)(); /* address of previous handler */ -void + void _utmp_sig_rcv(sig) -int sig; + int sig; { caught = 1; (void) signal(SIGUSR1, func); } + void utmp_sig_init() { /* @@ -1193,11 +1719,18 @@ utmp_sig_init() fatalperror(net, "telnetd/signal"); } + void utmp_sig_reset() { (void) signal(SIGUSR1, func); /* reset handler to default */ } +# ifdef __hpux +# define sigoff() /* do nothing */ +# define sigon() /* do nothing */ +# endif + + void utmp_sig_wait() { /* @@ -1211,11 +1744,132 @@ utmp_sig_wait() sigon(); /* turn on signals again */ } + void utmp_sig_notify(pid) { kill(pid, SIGUSR1); } -#endif /* defined(CRAY) && !defined(NEWINIT) */ + +# ifdef CRAY +static int gotsigjob = 0; + + /*ARGSUSED*/ + void +sigjob(sig) + int sig; +{ + register int jid; + register struct jobtemp *jp; + + while ((jid = waitjob(NULL)) != -1) { + if (jid == 0) { + return; + } + gotsigjob++; + jobend(jid, NULL, NULL); + } +} + +/* + * Clean up the TMPDIR that login created. + * The first time this is called we pick up the info + * from the utmp. If the job has already gone away, + * then we'll clean up and be done. If not, then + * when this is called the second time it will wait + * for the signal that the job is done. + */ + int +cleantmp(wtp) + register struct utmp *wtp; +{ + struct utmp *utp; + static int first = 1; + register int mask, omask, ret; + extern struct utmp *getutid P((struct utmp *)); + + mask = sigmask(WJSIGNAL); + + if (first == 0) { + omask = sigblock(mask); + while (gotsigjob == 0) + sigpause(omask); + return(1); + } + first = 0; + setutent(); /* just to make sure */ + + utp = getutid(wtp); + if (utp == 0) { + syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR"); + return(-1); + } + /* + * Nothing to clean up if the user shell was never started. + */ + if (utp->ut_type != USER_PROCESS || utp->ut_jid == 0) + return(1); + + /* + * Block the WJSIGNAL while we are in jobend(). + */ + omask = sigblock(mask); + ret = jobend(utp->ut_jid, utp->ut_tpath, utp->ut_user); + sigsetmask(omask); + return(ret); +} + + int +jobend(jid, path, user) + register int jid; + register char *path; + register char *user; +{ + static int saved_jid = 0; + static char saved_path[sizeof(wtmp.ut_tpath)+1]; + static char saved_user[sizeof(wtmp.ut_user)+1]; + + if (path) { + strncpy(saved_path, path, sizeof(wtmp.ut_tpath)); + strncpy(saved_user, user, sizeof(wtmp.ut_user)); + saved_path[sizeof(saved_path)] = '\0'; + saved_user[sizeof(saved_user)] = '\0'; + } + if (saved_jid == 0) { + saved_jid = jid; + return(0); + } + cleantmpdir(jid, saved_path, saved_user); + return(1); +} + +/* + * Fork a child process to clean up the TMPDIR + */ +cleantmpdir(jid, tpath, user) + register int jid; + register char *tpath; + register char *user; +{ + switch(fork()) { + case -1: + syslog(LOG_ERR, "TMPDIR cleanup(%s): fork() failed: %m\n", + tpath); + break; + case 0: + execl(CLEANTMPCMD, CLEANTMPCMD, user, tpath, 0); + syslog(LOG_ERR, "TMPDIR cleanup(%s): execl(%s) failed: %m\n", + tpath, CLEANTMPCMD); + exit(1); + default: + /* + * Forget about child. We will exit, and + * /etc/init will pick it up. + */ + break; + } +} +# endif /* CRAY */ +#endif /* defined(PARENT_DOES_UTMP) && !defined(NEWINIT) */ /* * rmut() @@ -1224,7 +1878,37 @@ utmp_sig_notify(pid) * remove the utmp entry for this person. */ -#if !defined(CRAY) && BSD <= 43 +#ifdef UTMPX +rmut() +{ + register f; + int found = 0; + struct utmp *u, *utmp; + int nutmp; + struct stat statbf; + + struct utmpx *utxp, utmpx; + + /* + * This updates the utmpx and utmp entries and make a wtmp/x entry + */ + + SCPYN(utmpx.ut_line, line + sizeof("/dev/") - 1); + utxp = getutxline(&utmpx); + if (utxp) { + utxp->ut_type = DEAD_PROCESS; + utxp->ut_exit.e_termination = 0; + utxp->ut_exit.e_exit = 0; + (void) time(&utmpx.ut_tv.tv_sec); + utmpx.ut_tv.tv_usec = 0; + modutx(utxp); + } + endutxent(); +} /* end of rmut */ +#endif + +#if !defined(UTMPX) && !(defined(CRAY) || defined(__hpux)) && BSD <= 43 + void rmut() { register f; @@ -1232,9 +1916,6 @@ rmut() struct utmp *u, *utmp; int nutmp; struct stat statbf; - char *malloc(); - long time(); - off_t lseek(); f = open(utmpf, O_RDWR); if (f >= 0) { @@ -1278,3 +1959,42 @@ rmut() (void) chown(line, 0, 0); } /* end of rmut */ #endif /* CRAY */ + +#ifdef __hpux +rmut (line) +char *line; +{ + struct utmp utmp; + struct utmp *utptr; + int fd; /* for /etc/wtmp */ + + utmp.ut_type = USER_PROCESS; + (void) strncpy(utmp.ut_id, line+12, sizeof(utmp.ut_id)); + (void) setutent(); + utptr = getutid(&utmp); + /* write it out only if it exists */ + if (utptr) { + utptr->ut_type = DEAD_PROCESS; + utptr->ut_time = time((long *) 0); + (void) pututline(utptr); + /* set wtmp entry if wtmp file exists */ + if ((fd = open(wtmpf, O_WRONLY | O_APPEND)) >= 0) { + (void) write(fd, utptr, sizeof(utmp)); + (void) close(fd); + } + } + (void) endutent(); + + (void) chmod(line, 0666); + (void) chown(line, 0, 0); + line[14] = line[13]; + line[13] = line[12]; + line[8] = 'm'; + line[9] = '/'; + line[10] = 'p'; + line[11] = 't'; + line[12] = 'y'; + (void) chmod(line, 0666); + (void) chown(line, 0, 0); +} +#endif