/*
 * Copyright (c) 1983, 1988 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
#include <afsconfig.h>
#include <afs/param.h>

RCSID("$Header: /cvs/openafs/src/rlogind/rlogind.c,v 1.6 2003/07/15 23:16:06 shadow Exp $");

#ifdef MSG
#include "rlogind_msg.h"
#define MF_LIBC "libc.cat"
#define MS_LIBC 1
#define MSGSTR(n,s) NLgetamsg(MF_RLOGIND, MS_RLOGIND, n, s)
#else*/
#define MSGSTR(n,s) s
/*#endif*/


/*
 * remote login server:
 *	\0
 *	remuser\0
 *	locuser\0
 *	terminal_type/speed\0
 *	data
 *
 * Automatic login protocol is done here, using login -f upon success,
 * unless OLD_LOGIN is defined (then done in login, ala 4.2/4.3BSD).
 */

#include <afs/param.h>
#ifdef	AFS_SUN5_ENV
#define BSD_COMP
#include <sys/ioctl.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>
#ifdef	AFS_SUN5_ENV
#include <fcntl.h>
#endif

#include <netinet/in.h>

#include <errno.h>
#include <pwd.h>
#include <signal.h>
#ifdef _AIX
#include <utmp.h>
/* POSIX TERMIOS */
#include <termios.h>
#include <sys/ioctl.h>
#ifndef	AFS_AIX41_ENV
#include <sys/tty.h>
#endif
#include <sys/acl.h>
#include <sys/access.h>
#else /* _AIX */
#include <sgtty.h>
#endif /* _AIX */
#include <stdio.h>
#include <netdb.h>
#ifdef	AFS_AIX32_ENV
#include <sys/lockf.h>
#endif
#include <syslog.h>
#include <strings.h>

#ifdef	AFS_OSF_ENV
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <sys/signal.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#define TTYDEFCHARS
#include <sys/termios.h>
#define _PATH_LOGIN        "/usr/bin/login"
#endif

#if defined(AFS_HPUX_ENV)
#include <fcntl.h>
#include <sys/ptyio.h>
#endif /* defined(AFS_HPUX_ENV) */

#ifdef _AIX
#define DEF_UTMP	"Active tty entry not found in utmp file.\n"

#define NOLOGIN         "/etc/nologin"
#define BSHELL          "/bin/sh"

/*#define KAUTH*/

#ifdef KAUTH
int pass_tokens;
#include <afs/auth.h>
#include <afs/cellconfig.h>
#endif /* KAUTH */

struct utmp utmp;
#define UT_NAMESIZE     sizeof(utmp.ut_user)
#define	UTSIZ		(sizeof (struct utmp))

static void loginlog();
#endif /* _AIX */

#ifndef TIOCPKT_WINDOW
#define TIOCPKT_WINDOW 0x80
#endif

char *env[2];
#define	NMAX 30
char lusername[NMAX + 1], rusername[NMAX + 1];
static char term[64] = "TERM=";
#define	ENVSIZE	(sizeof("TERM=")-1)	/* skip null for concatenation */
int keepalive = 1;
int tracing = 0;
#ifdef	AFS_OSF_ENV
int check_all = 0;
#endif

#define	SUPERUSER(pwd)	((pwd)->pw_uid == 0)

extern int errno;
int reapchild();
struct passwd *getpwnam(), *pwd;
#ifdef	AFS_HPUX_ENV
#include <unistd.h>
struct s_passwd *s_pwd, *getspwnam();
struct stat s_pfile;
#endif
#if defined(AFS_AIX32_ENV) && (defined(NLS) || defined(KJI))
#include <locale.h>
#endif


#include "AFS_component_version_number.c"

main(argc, argv)
     int argc;
     char **argv;
{
    extern int opterr, optind, _check_rhosts_file;
    int ch;
    int on = 1, fromlen;
    struct sockaddr_in from;
#ifdef	AFS_AIX32_ENV
    struct sigaction sa;
    void trace_handler(int);

#if defined(NLS) || defined(KJI)
    setlocale(LC_ALL, "");
#endif
#endif
    openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);

#ifdef	KAUTH
    pass_tokens = 0;
#endif
    opterr = 0;
    while ((ch = getopt(argc, argv, "ln")) != EOF)
	switch (ch) {
#ifdef	AFS_OSF_ENV
	case 'a':
	    check_all = 1;
	    break;
#endif
	case 'l':
	    _check_rhosts_file = 0;
	    break;
	case 'n':
	    keepalive = 0;
	    break;
#ifdef 	AFS_AIX32_ENV
#ifdef KAUTH
	case 'v':
	    pass_tokens = 1;
	    break;
#endif /* KAUTH */
	case 's':
	    tracing = 1;
	    break;
#endif
	case '?':
	default:
#ifdef	AFS_AIX32_ENV
#ifdef KAUTH
	    syslog(LOG_ERR, "usage: rlogind [-v] [-l] [-n] [-s]");
#else /* KAUTH */
	    syslog(LOG_ERR, "usage: rlogind [-l] [-n] [-s]");
#endif /* KAUTH */
#else
#ifdef	AFS_OSF_ENV
	    syslog(LOG_ERR, "usage: rlogind [-l] [-n] [-a] [-s]");
#else
	    syslog(LOG_ERR, "usage: rlogind [-l] [-n]");
#endif
#endif
	    break;
	}
    argc -= optind;
    argv += optind;

    fromlen = sizeof(from);
    if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
	syslog(LOG_ERR, "Couldn't get peer name of remote host: %m");
	fatalperror("Can't get peer name of host");
    }
    if (keepalive
	&& setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
		      sizeof(on)) < 0)
	syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
#ifdef	AFS_AIX32_ENV
    if (tracing
	&& setsockopt(0, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)) < 0)
	syslog(LOG_WARNING, MSGSTR(SETDEBUG, "setsockopt (SO_DEBUG): %m"));
     /*MSG*/
	/* set-up signal handler routines for SRC TRACE ON/OFF support */
	memset((char *)&sa, 0, sizeof(sa));
    sa.sa_mask.losigs = sigmask(SIGUSR2);
    sa.sa_handler = trace_handler;
    sa.sa_flags = SA_RESTART;
    sigaction(SIGUSR1, &sa, NULL);
    sa.sa_mask.losigs = sigmask(SIGUSR1);
    sa.sa_handler = trace_handler;
    sa.sa_flags = SA_RESTART;
    sigaction(SIGUSR2, &sa, NULL);
#endif
#ifdef	AFS_OSF_ENV
    on = IPTOS_LOWDELAY;
    if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
	syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
#endif
    doit(0, &from);
}

#ifdef	AFS_AIX32_ENV
/*
 * trace_handler - SRC TRACE ON/OFF signal handler
 */
void
trace_handler(int sig)
{
    int onoff;

    onoff = (sig == SIGUSR1) ? 1 : 0;
    if (setsockopt(0, SOL_SOCKET, SO_DEBUG, &onoff, sizeof(onoff)) < 0)
	syslog(LOG_WARNING, MSGSTR(SETDEBUG, "setsockopt (SO_DEBUG): %m"));
 /*MSG*/}
#endif

int child;
void cleanup();
int netf;
#ifdef	AFS_OSF_ENV
char line[MAXPATHLEN];
#else
char *line;
#endif
#ifdef _AIX
char tbuf[MAXPATHLEN + 2];
struct hostent *hp;
struct hostent hostent;
#endif /* _AIX */

extern char *inet_ntoa();
#ifdef	AFS_HPUX_ENV
int netp;
char master[MAXPATHLEN], slave[MAXPATHLEN], tname[MAXPATHLEN];
#endif

#if	defined(TIOCSWINSZ) || defined(AFS_OSF_ENV)
struct winsize win = { 0, 0, 0, 0 };
#else /* ~TIOCWINSIZ */
/*
 * Window/terminal size structure.
 * This information is stored by the kernel
 * in order to provide a consistent interface,
 * but is not used by the kernel.
 *
 * Type must be "unsigned short" so that types.h not required.
 */
struct winsize {
    unsigned short ws_row;	/* rows, in characters */
    unsigned short ws_col;	/* columns, in characters */
    unsigned short ws_xpixel;	/* horizontal size, pixels */
    unsigned short ws_ypixel;	/* vertical size, pixels */
};
#endif /* ~TIOCWINSIZ */

doit(f, fromp)
     int f;
     struct sockaddr_in *fromp;
{
    int i, p, t, pid, on = 1;
#ifdef	AFS_OSF_ENV
    int master;
#endif
#ifdef	AFS_HPUX_ENV
    int t_;
    char *tn;
#endif
    char pline[16];

#ifndef OLD_LOGIN
    int authenticated = 0, hostok = 0;
    char remotehost[2 * MAXHOSTNAMELEN + 1];
#endif
#ifndef _AIX
    register struct hostent *hp;
    struct hostent hostent;
#endif
    char c, *cp;

#if defined(AFS_HPUX_ENV)
    int ptyStatusFlags;
#endif /* defined(AFS_HPUX_ENV) */

    alarm(60);
    read(f, &c, 1);
    if (c != 0)
	exit(1);

    alarm(0);
    fromp->sin_port = ntohs((u_short) fromp->sin_port);
    hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(struct in_addr),
		       fromp->sin_family);
    if (hp == 0) {
	/*
	 * Only the name is used below.
	 */
	hp = &hostent;
	hp->h_name = inet_ntoa(fromp->sin_addr);
#ifndef OLD_LOGIN
	hostok++;
#endif
    }
#ifndef OLD_LOGIN
#ifdef	AFS_HPUX_ENV
    else if (errno != ECONNREFUSED) {
#else
#ifdef	AFS_OSF_ENV
    else if (check_all || local_domain(hp->h_name)) {
#else
    else if (local_domain(hp->h_name)) {
#endif
#endif
	/*
	 * If name returned by gethostbyaddr is in our domain,
	 * attempt to verify that we haven't been fooled by someone
	 * in a remote net; look up the name and check that this
	 * address corresponds to the name.
	 */
	strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1);
	remotehost[sizeof(remotehost) - 1] = 0;
	hp = gethostbyname(remotehost);
	if (hp)
#if defined(BSD_42)
	    if (!memcmp
		(hp->h_addr, (caddr_t) & fromp->sin_addr,
		 sizeof(fromp->sin_addr))) {
#else /* BSD_42 */
	    for (; hp->h_addr_list[0]; hp->h_addr_list++)
		if (!memcmp
		    (hp->h_addr_list[0], (caddr_t) & fromp->sin_addr,
		     sizeof(fromp->sin_addr))) {
#endif /* BSD_42 */
		    hostok++;
#if !defined(BSD_42)
		    break;
#endif
		}
    } else
	hostok++;
#endif

    if (fromp->sin_family != AF_INET || fromp->sin_port >= IPPORT_RESERVED
	|| fromp->sin_port < IPPORT_RESERVED / 2) {
	syslog(LOG_NOTICE, "Connection from %s on illegal port",
	       inet_ntoa(fromp->sin_addr));
	fatal(f, "Permission denied");
    }
#ifdef IP_OPTIONS
    {
	u_char optbuf[BUFSIZ / 3], *cp;
	char lbuf[BUFSIZ], *lp;
	int optsize = sizeof(optbuf), ipproto;
	struct protoent *ip;

	if ((ip = getprotobyname("ip")) != NULL)
	    ipproto = ip->p_proto;
	else
	    ipproto = IPPROTO_IP;
	if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0
	    && optsize != 0) {
	    lp = lbuf;
	    for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
		sprintf(lp, " %2.2x", *cp);
	    syslog(LOG_NOTICE,
		   "Connection received using IP options (ignored):%s", lbuf);
	    if (setsockopt(0, ipproto, IP_OPTIONS, (char *)NULL, optsize) !=
		0) {
		syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
		exit(1);
	    }
	}
    }
#endif
    write(f, "", 1);
#ifndef OLD_LOGIN
    if (do_rlogin(hp->h_name) == 0) {
	if (hostok)
	    authenticated++;
	else
	    write(f, "rlogind: Host address mismatch.\r\n",
		  sizeof("rlogind: Host address mismatch.\r\n") - 1);
    }
#endif
#ifdef KAUTH
    if (pass_tokens) {
	if (intokens(0)) {
	    fprintf(stderr, "%s: invalid remote authentication\n", "login");
	    exit(1);
	}
    }
#endif /* KAUTH */

#ifdef	AFS_OSF_ENV
    netf = f;

    pid = forkpty(&master, line, NULL, &win);
    if (pid < 0) {
	if (errno == ENOENT)
	    fatal(f, "Out of ptys", 0);
	else
	    fatal(f, "Forkpty", 1);
    }
    if (pid == 0) {
	if (f > 2)		/* f should always be 0, but... */
	    (void)close(f);
	setup_term(0);
	if (authenticated) {
	    execl(_PATH_LOGIN, "login", "-p", "-h", hp->h_name, "-f",
		  lusername, NULL);
	} else {
	    char *sp = lusername;
	    while (*sp == ' ')
		sp++;
	    if (!strncmp(sp, "-f", 2)) {
		syslog(LOG_ERR, "Can't use '-f' in username");
		exit(1);
	    }
	    execl(_PATH_LOGIN, "login", "-p", "-h", hp->h_name, lusername,
		  NULL);
	}
	fatalperror(2, _PATH_LOGIN);
     /*NOTREACHED*/}
    ioctl(f, FIONBIO, &on);
    ioctl(master, FIONBIO, &on);
    ioctl(master, TIOCPKT, &on);
    signal(SIGCHLD, cleanup);
    protocol(f, master);
    signal(SIGCHLD, SIG_IGN);
    cleanup();
#else
#if	defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV) && defined(notdef)
    /*
     * Find an unused pty.  A security hole exists if we
     * allow any other process to hold the slave side of the
     * pty open while we are setting up.  The other process
     * could read from the slave side at the time that in.rlogind
     * is forwarding the "rlogin authentication" string from the 
     * rlogin client through the pty to the login program.  If the
     * other process grabs this string, the user at the rlogin
     * client program can forge an authentication string.
     * This problem would not exist if the pty subsystem
     * would fail the open of the master side when there are
     * existing open instances of the slave side.
     * Until the pty driver is changed to work this way, we use
     * a side effect of one particular ioctl to test whether
     * any process has the slave open.
     */

    for (cp = "pqrstuvwxyzPQRST"; *cp; cp++) {
	struct stat stb;
	int pgrp;

	/* make sure this "bank" of ptys exists */
	line = "/dev/ptyXX";
	line[strlen("/dev/pty")] = *cp;
	line[strlen("/dev/ptyp")] = '0';
	if (stat(line, &stb) < 0)
	    break;
	for (i = 0; i < 16; i++) {
	    line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
	    line[strlen("/dev/")] = 'p';

	    /*
	     * Test to see if this pty is unused.  The open
	     * on the master side of the pty will fail if
	     * any other process has it open.
	     */
	    if ((p = open(line, O_RDWR | O_NOCTTY)) == -1)
		continue;

	    /* 
	     * Lock the slave side so that no one else can 
	     * open it after this.
	     */
	    line[strlen("/dev/")] = 't';
	    if (chmod(line, 0600) == -1) {
		(void)close(p);
		continue;
	    }

	    /*
	     * XXX - Use a side effect of TIOCGPGRP on ptys
	     * to test whether any process is holding the
	     * slave side open.  May not work as we expect 
	     * in anything other than SunOS 4.1.
	     */
	    if ((ioctl(p, TIOCGPGRP, &pgrp) == -1) && (errno == EIO))
		goto gotpty;
	    else {
		(void)chmod(line, 0666);
		(void)close(p);
	    }
	}
    }
    fatal(f, "Out of ptys");
   /*NOTREACHED*/ gotpty:
    (void)ioctl(p, TIOCSWINSZ, &win);
    netf = f;
    line[strlen("/dev/")] = 't';
    t = open(line, O_RDWR | O_NOCTTY);
    if (t < 0)
	fatalperror(f, line);
    {
	/* These should be converted to termios ioctls. */
	struct sgttyb b;
	int zero = 0;

	if (gtty(t, &b) == -1)
	    perror("gtty");
	b.sg_flags = RAW | ANYP;
	/* XXX - ispeed and ospeed must be non-zero */
	b.sg_ispeed = B38400;
	b.sg_ospeed = B38400;
	if (stty(t, &b) == -1)
	    perror("stty");
	/*
	 * Turn off PASS8 mode, since "login" no longer does so.
	 */
	if (ioctl(t, TIOCLSET, &zero) == -1)
	    perror("ioctl TIOCLSET");
    }
#else
#ifdef _AIX
    (void)setsid();
    if ((p = open("/dev/ptc", O_RDWR)) >= 0) {
	line = ttyname(p);
    } else {
	fatal(f, MSGSTR(NOPTY, "Out of ptys"));
     /*MSG*/}

#else
#ifdef	AFS_HPUX_ENV
    if (getpty(&p, master, slave) != 0)
	fatal(f, "Unable to allocate pty on remote host");
    /* Global copies for use by SIGCHLD handler */
    netf = f;
    netp = p;

    /* Slave becomes controlling terminal for this session. */
    t_ = open(slave, O_RDWR);
    if (t_ < 0)
	fatalperror(f, slave);
    /*  Remove any reference to the current control terminal */
    if (vhangup() < 0)
	fatalperror(f, "rlogind: vhangup()");
    /* reopen the slave pseudo-terminal */
    t = open(slave, O_RDWR);
    if (t < 0)
	fatalperror(f, slave);
    close(t_);
    /* Get the line name for utmp accounting. */
    tn = ttyname(t);
    if (tn != NULL)
	strcpy(tname, tn);
    else
	strcpy(tname, slave);
#ifdef TIOCSWINSZ
    (void)ioctl(p, TIOCSWINSZ, &win);
#endif /* TIOCSWINSZ */
#ifndef OLD_LOGIN
    setup_term(t);
#endif /* ~OLD_LOGIN */

    /* Lock the pty so that the parent can then synchronize setting up
     * the child's process group and controlling terminal with the
     * login process.  The login process won't be able to read from
     * the pty until the parent unlocks it.
     */
    lockf(p, F_LOCK, 0);
#else
    for (c = 'p'; c <= 'z'; c++) {
	struct stat stb;
	line = "/dev/ptyXX";
	line[strlen("/dev/pty")] = c;
	line[strlen("/dev/ptyp")] = '0';
	if (stat(line, &stb) < 0)
	    break;
	for (i = 0; i < 16; i++) {
	    line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
	    p = open(line, O_RDWR);
	    if (p > 0)
		goto gotpty;
	}
    }
    fatal(f, "Out of ptys");
   /*NOTREACHED*/ gotpty:
#endif
#endif /* _AIX */

#ifdef AFS_SUN_ENV

    /* defect #2976 */

    strcpy(pline, line);
    pline[strlen("/dev/")] = 't';
    chmod(pline, 0620);

    if (close(p) < 0)
	goto L1;
    if ((p = open(line, O_RDWR)) < 0)
	fatal(f, "Bad open");

  L1:
#endif

#ifndef	AFS_HPUX_ENV
#ifdef	AFS_AIX32_ENV
#ifdef	TIOCSWINSZ
    (void)ioctl(p, TIOCSWINSZ, &win);
#else
#ifdef	TXSETWINSZ
    (void)ioctl(p, TXSETWINSZ, &win);
#endif /* TXSETWINSZ */
#endif /* TIOCSWINSZ */
#endif
#ifdef	TIOCSWINSZ
    (void)ioctl(p, TIOCSWINSZ, &win);
#endif
    netf = f;
#ifndef _AIX
    line[strlen("/dev/")] = 't';
    t = open(line, O_RDWR);
    if (t < 0)
	fatalperror(f, line);
#if !defined(ultrix)
    if (fchmod(t, 0))
	fatalperror(f, line);
    (void)signal(SIGHUP, SIG_IGN);
#if ! defined(AFS_HPUX_ENV) && ! defined(AFS_OSF_ENV)
    vhangup();
#endif /* !defined(AFS_HPUX_ENV) */
    (void)signal(SIGHUP, SIG_DFL);
#endif /* ultrix */
    t = open(line, O_RDWR);
    if (t < 0)
	fatalperror(f, line);
    setup_term(t);
#endif /* _AIX */
#endif /* !AFS_SUN_ENV && !AFS_SUN5_ENV */
#ifdef DEBUG
    {
	int tt = open("/dev/tty", O_RDWR);
	if (tt > 0) {
	    (void)ioctl(tt, TIOCNOTTY, 0);
	    (void)close(tt);
	}
    }
#endif
#endif /* !AFS_HPUX_ENV */
    pid = fork();
    if (pid < 0) {
	fatalperror(f, "");
    }
    if (pid == 0) {
#if	defined(AFS_SUN_ENV) && !defined(AFS_SUN5_ENV)  && defined(notdef)
	int tt;

	/* The child process needs to be the session leader
	 * and have the pty as its controlling tty
	 */
	setpgrp(0, 0);
	tt = open(line, O_RDWR);
	if (tt < 0)
	    fatalperror(f, line);
	close(f), close(p), close(t);
	if (tt != 0)
	    dup2(tt, 0);
	if (tt != 1)
	    dup2(tt, 1);
	if (tt != 2)
	    dup2(tt, 2);
	if (tt > 2)
	    close(tt);
#ifdef	notdef
	close(f), close(p);
	dup2(t, 0), dup2(t, 1), dup2(t, 2);
	close(t);
#endif
#else
#ifdef _AIX
	(void)setsid();
	t = open(line, O_RDWR);
	if (t < 0)
	    fatalperror(f, line);
	if (acl_fset(t, NO_ACC, NO_ACC, NO_ACC))
	    fatalperror(f, line);
	(void)signal(SIGHUP, SIG_IGN);
	/* frevoke(t); */

	{
	    struct sigaction sa;
	    memset((char *)&sa, 0, sizeof(sa));
	    sa.sa_handler = SIG_DFL;
	    sigaction(SIGQUIT, &sa, NULL);
	    sa.sa_handler = SIG_DFL;
	    sigaction(SIGHUP, &sa, NULL);
	}

	t = open(line, O_RDWR);
	if (t < 0)
	    fatalperror(f, line);
	setup_term(t);
#endif /* _AIX */
#ifdef	AFS_OSF_ENV
	(void)setsid();
	(void)ioctl(t, TIOCSCTTY, 0);
#endif /* AFS_OSF_ENV */
#ifdef	AFS_HPUX_ENV
	/* Wait for the parent to setup our process group and controlling tty. */
	lockf(p, F_LOCK, 0);
	lockf(p, F_ULOCK, 0);
#endif
	close(f), close(p);
	dup2(t, 0), dup2(t, 1), dup2(t, 2);
	close(t);
#endif /* AFS_SUN_ENV */
#ifdef	AFS_AIX32_ENV
	/*
	 * Reset SIGUSR1 and SIGUSR2 to non-restartable so children
	 * of rlogind do not get clobbered by the way rlogind handles
	 * these signals.
	 */
	{
	    struct sigaction sa;

	    memset((char *)&sa, 0, sizeof(sa));
	    sa.sa_mask.losigs = sigmask(SIGUSR2);
	    sa.sa_handler = trace_handler;
	    sigaction(SIGUSR1, &sa, NULL);
	    sa.sa_mask.losigs = sigmask(SIGUSR1);
	    sa.sa_handler = trace_handler;
	    sigaction(SIGUSR2, &sa, NULL);
	}

	(void)loginlog(lusername, line, hp->h_name);
#endif
#ifdef OLD_LOGIN
	execl("/bin/login", "login", "-r", hp->h_name, 0);
#else /* OLD_LOGIN */
	if (authenticated) {
#ifdef AFS_AIX32_ENV
	    execl("/bin/login.afs", "login", /*"-v", */ "-p", "-h",
		  hp->h_name, "-f", "--", lusername, 0);
#else
	    execl("/bin/login", "login", "-p", "-h", hp->h_name, "-f",
		  lusername, 0);
#endif
	} else {
	    char *sp = lusername;
	    while (*sp == ' ')
		sp++;
	    if (!strncmp(sp, "-f", 2)) {
		syslog(LOG_ERR, "Can't use '-f' in username");
		exit(1);
	    }
#ifdef	AFS_AIX32_ENV
	    execl("/bin/login.afs", "login", /*"-v", */ "-p", "-h",
		  hp->h_name, "--", lusername, 0);
#else
	    execl("/bin/login", "login", "-p", "-h", hp->h_name, lusername,
		  0);
#endif
	}
#endif /* OLD_LOGIN */
	fatalperror(2, "/bin/login");
     /*NOTREACHED*/}
#ifdef	AFS_HPUX_ENV
    /* Set the child up as a process group leader.
     * It must not be part of the parent's process group,
     * because signals issued in the child's process group
     * must not affect the parent.
     */
    setpgid(pid, pid);

    /* Make the pty slave the controlling terminal for the child's process group. */
    tcsetpgrp(t, pid);
    /* Close our controlling terminal. */
    close(t);

    /* Let the child know that it is a process group leader and has a controlling tty. */
    lockf(p, F_ULOCK, 0);
    ioctl(f, FIONBIO, &on);
    ioctl(p, FIONBIO, &on);
    ioctl(p, TIOCPKT, &on);
    signal(SIGTSTP, SIG_IGN);
    signal(SIGCHLD, cleanup);
#ifdef AFS_HPUX102_ENV
    signal(SIGTERM, cleanup);
    signal(SIGPIPE, cleanup);
#else
    signal(SIGTERM, cleanup,
	   sigmask(SIGCLD) | sigmask(SIGTERM) | sigmask(SIGPIPE));
    signal(SIGPIPE, cleanup,
	   sigmask(SIGCLD) | sigmask(SIGTERM) | sigmask(SIGPIPE));
#endif
    setpgid(0, 0);
    protocol(f, p);
    signal(SIGCHLD, SIG_IGN);
    signal(SIGTERM, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    cleanup();
    return;
#endif
#ifndef	_AIX
    close(t);
#endif

    ioctl(f, FIONBIO, &on);

#if !defined(AFS_HPUX_ENV)
    ioctl(p, FIONBIO, &on);
#else /* !defined(AFS_HPUX_ENV) */
    /* HPUX does not support FIONBIO on ptys, so, we do it the hard way */
    ptyStatusFlags = fcntl(p, F_GETFL, 0);
    fcntl(p, F_SETFL, ptyStatusFlags | O_NDELAY);
#endif /* !defined(AFS_HPUX_ENV) */

#if !defined(AFS_SUN5_ENV)
    ioctl(p, TIOCPKT, &on);
    signal(SIGTSTP, SIG_IGN);
#if defined(AFS_HPUX_ENV)
    signal(SIGINT, SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
#endif /* defined(AFS_HPUX_ENV) */
#endif /* !defined(AFS_SUN5_ENV) */
    signal(SIGCHLD, cleanup);
#ifndef	_AIX
#ifdef AFS_HPUX102_ENV
    setpgrp();
#else
    setpgrp(0, 0);
#endif
#endif
#ifdef	AFS_AIX32_ENV
    protocol(f, p, pid);
    (void)signal(SIGCHLD, SIG_IGN);
    cleanup(pid);
#else
    protocol(f, p);
    signal(SIGCHLD, SIG_IGN);
    cleanup();
#endif
#endif /* AFS_OSF_ENV */
}

char magic[2] = { 0377, 0377 };
char oobdata[] = { TIOCPKT_WINDOW };

/*
 * Handle a "control" request (signaled by magic being present)
 * in the data stream.  For now, we are only willing to handle
 * window size changes.
 */
control(pty, cp, n)
     int pty;
     char *cp;
     int n;
{
    struct winsize w;

    if (n < 4 + sizeof(w) || cp[2] != 's' || cp[3] != 's')
	return (0);
    oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
    memcpy((char *)&w, cp + 4, sizeof(w));
    w.ws_row = ntohs(w.ws_row);
    w.ws_col = ntohs(w.ws_col);
    w.ws_xpixel = ntohs(w.ws_xpixel);
    w.ws_ypixel = ntohs(w.ws_ypixel);
#ifdef	AFS_AIX32_ENV
#ifdef	TIOCSWINSZ
    (void)ioctl(pty, TIOCSWINSZ, &w);
#else
    (void)ioctl(pty, TXSETWINSZ, &w);
#endif
#else
#ifdef	TIOCSWINSZ
    (void)ioctl(pty, TIOCSWINSZ, &w);
#endif
#endif
    return (4 + sizeof(w));
}

/*
 * rlogin "protocol" machine.
 */
#ifdef	AFS_AIX32_ENV
protocol(f, p, pid)
     int f, p;
     pid_t pid;
#else
protocol(f, p)
     int f, p;
#endif
{
    char pibuf[1024], fibuf[1024], *pbp, *fbp;
    register pcc = 0, fcc = 0;
    int cc, nfd, pmask, fmask;
    char cntl;

    /*
     * Must ignore SIGTTOU, otherwise we'll stop
     * when we try and set slave pty's window shape
     * (our controlling tty is the master pty).
     */
    (void)signal(SIGTTOU, SIG_IGN);
#ifdef	AFS_OSF_ENV
    /* delay TIOCPKT_WINDOW oobdata, for backward compatibility */
    sleep(1);
#endif
    send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
    if (f > p)
	nfd = f + 1;
    else
	nfd = p + 1;
    fmask = 1 << f;
    pmask = 1 << p;
    for (;;) {
	int ibits, obits, ebits;

	ibits = 0;
	obits = 0;
	if (fcc)
	    obits |= pmask;
	else
	    ibits |= fmask;
	if (pcc >= 0)
	    if (pcc)
		obits |= fmask;
	    else
		ibits |= pmask;
	ebits = pmask;

	if (select(nfd, &ibits, obits ? &obits : (int *)NULL, &ebits, 0) < 0) {
	    if (errno == EINTR)
		continue;
#ifdef	AFS_AIX32_ENV
	    cleanup(pid);
#endif
	    fatalperror(f, "select");
	}
	if (ibits == 0 && obits == 0 && ebits == 0) {
	    /* shouldn't happen... */
	    sleep(5);
	    continue;
	}
#if !defined(AFS_SUN5_ENV)
#define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
	if (ebits & pmask) {
	    cc = read(p, &cntl, 1);
	    if (cc == 1 && pkcontrol(cntl)) {
		cntl |= oobdata[0];
		send(f, &cntl, 1, MSG_OOB);
		if (cntl & TIOCPKT_FLUSHWRITE) {
		    pcc = 0;
		    ibits &= ~pmask;
		}
	    }
	}
#endif /* !defined(AFS_SUN5_ENV) */

	if (ibits & fmask) {
	    fcc = read(f, fibuf, sizeof(fibuf));
	    if (fcc < 0 && errno == EWOULDBLOCK)
		fcc = 0;
	    else {
		register char *cp;
		int left, n;

		if (fcc <= 0)
		    break;
		fbp = fibuf;

	      top:
		for (cp = fibuf; cp < fibuf + fcc - 1; cp++)
		    if (cp[0] == magic[0] && cp[1] == magic[1]) {
			left = fcc - (cp - fibuf);
			n = control(p, cp, left);
			if (n) {
			    left -= n;
			    if (left > 0)
				memcpy(cp, cp + n, left);
			    fcc -= n;
			    goto top;	/* n^2 */
			}
		    }
		obits |= pmask;	/* try write */
	    }
	}

	if ((obits & pmask) && fcc > 0) {
	    cc = write(p, fbp, fcc);
	    if (cc > 0) {
		fcc -= cc;
		fbp += cc;
	    }
	}

	if (ibits & pmask) {
	    pcc = read(p, pibuf, sizeof(pibuf));
	    pbp = pibuf;
	    if (pcc < 0 && errno == EWOULDBLOCK)
		pcc = 0;
	    else if (pcc <= 0)
		break;
	    else if (pibuf[0] == 0) {
		pbp++, pcc--;
		obits |= fmask;	/* try a write */
	    } else {
#if !defined(AFS_SUN5_ENV)
		if (pkcontrol(pibuf[0])) {
		    pibuf[0] |= oobdata[0];
		    send(f, &pibuf[0], 1, MSG_OOB);
		}
#endif /* !defined(AFS_SUN5_ENV) */
		pcc = 0;
	    }
	}

	if ((obits & fmask) && pcc > 0) {
	    cc = write(f, pbp, pcc);
	    if (cc < 0 && errno == EWOULDBLOCK) {
		/* also shouldn't happen */
		sleep(5);
		continue;
	    }
	    if (cc > 0) {
		pcc -= cc;
		pbp += cc;
	    }
	}
    }
}

#ifdef	AFS_AIX32_ENV
void
cleanup(pid_t pid)
#else
void
cleanup()
#endif
{
    char *p;
#ifdef	AFS_HPUX_ENV
    char buf[BUFSIZ];
    int cc;

    fcntl(netp, F_SETFL, O_NDELAY);
    if ((cc = read(netp, buf, sizeof buf)) > 0) {
	write(netf, buf, cc);
    }
    {
	chmod(master, 0666);
	chown(master, 0, 0);
	chmod(slave, 0666);
	chown(slave, 0, 0);
    }
#else
#ifdef	AFS_AIX32_ENV
    struct utmp cutmp;
    int found = 0, f, t;
    int rcode;
    off_t offset;

    p = line + sizeof("/dev/") - 1;
    if (logout(p))
	logwtmp(p, "", "");
#ifdef _AIX
    (void)acl_set(line, R_ACC | W_ACC | X_ACC, R_ACC | W_ACC | X_ACC,
		  R_ACC | W_ACC | X_ACC);
#else
    (void)chmod(line, 0666);
#endif /* _AIX */
    (void)chown(line, 0, 0);
    *p = 'p';
#ifdef _AIX
    (void)acl_set(line, R_ACC | W_ACC | X_ACC, R_ACC | W_ACC | X_ACC,
		  R_ACC | W_ACC | X_ACC);
#else
    (void)chmod(line, 0666);
#endif /* _AIX */
#else
    p = line + sizeof("/dev/") - 1;
    if (logout(p))
	logwtmp(p, "", "");
    (void)chmod(line, 0666);
    (void)chown(line, 0, 0);
    *p = 'p';
    (void)chmod(line, 0666);
#endif
    (void)chown(line, 0, 0);
#endif
    shutdown(netf, 2);
    exit(1);
}

fatal(f, msg)
     int f;
     char *msg;
{
    char buf[BUFSIZ];

    buf[0] = '\01';		/* error indicator */
    (void)sprintf(buf + 1, "rlogind: %s.\r\n", msg);
    (void)write(f, buf, strlen(buf));

    exit(1);
}

fatalperror(f, msg)
     int f;
     char *msg;
{
    char buf[BUFSIZ];
    extern int sys_nerr;
    extern char *sys_errlist[];

    if ((unsigned)errno < sys_nerr)
	(void)sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
    else
	(void)sprintf(buf, "%s: Error %d", msg, errno);

    fatal(f, buf);
}

#ifndef OLD_LOGIN
do_rlogin(host)
     char *host;
{

    int ru;
    getstr(rusername, sizeof(rusername), "remuser too long");
    getstr(lusername, sizeof(lusername), "locuser too long");
    getstr(term + ENVSIZE, sizeof(term) - ENVSIZE, "Terminal type too long");

    if (getuid())
	return (-1);
    pwd = getpwnam(lusername);
    if (pwd == NULL)
	return (-1);
#ifdef	AFS_HPUX_ENV
#define	SUPERUSER(pwd)	((pwd)->pw_uid == 0)
#define SECUREPASS		"/.secure/etc/passwd"
    /* If /.secure/etc/passwd file exists then make sure that user has
     * a valid entry in both password files.
     * Initialize s_pwd to point to pwd so check won't fail if the
     * secure password file doesn't exists.
     */
    s_pwd = (struct s_passwd *)pwd;
    if (stat(SECUREPASS, &s_pfile) == 0)
	s_pwd = getspwnam(lusername);
    if (s_pwd == NULL) {
	return (-1);
    }
#endif

#ifdef _AIX
    if (*pwd->pw_shell == '\0')
	pwd->pw_shell = BSHELL;
    /* check for disabled logins before */
    /* authenticating based on .rhosts  */
    if (pwd->pw_uid)
	if (checknologin() == 0)
	    return (-1);
#endif /* _AIX */
    ru = ruserok(host, SUPERUSER(pwd), rusername, lusername);
    return (ru);
}


getstr(buf, cnt, errmsg)
     char *buf;
     int cnt;
     char *errmsg;
{
    char c;

    do {
	if (read(0, &c, 1) != 1)
	    exit(1);
	if (--cnt < 0)
	    fatal(1, errmsg);
	*buf++ = c;
    } while (c != 0);
}

extern char **environ;

#ifdef _AIX
/* POSIX TERMIOS */
speed_t
speeds(speed)
     char *speed;
{
    if (strcmp(speed, "38400") == 0)
	return (B38400);
    if (strcmp(speed, "19200") == 0)
	return (B19200);
    if (strcmp(speed, "9600") == 0)
	return (B9600);
    if (strcmp(speed, "4800") == 0)
	return (B4800);
    if (strcmp(speed, "2400") == 0)
	return (B2400);
    if (strcmp(speed, "1800") == 0)
	return (B1800);
    if (strcmp(speed, "1200") == 0)
	return (B1200);
    if (strcmp(speed, "600") == 0)
	return (B600);
    if (strcmp(speed, "300") == 0)
	return (B300);
    if (strcmp(speed, "200") == 0)
	return (B200);
    if (strcmp(speed, "150") == 0)
	return (B150);
    if (strcmp(speed, "134") == 0)
	return (B134);
    if (strcmp(speed, "110") == 0)
	return (B110);
    if (strcmp(speed, "75") == 0)
	return (B75);
    if (strcmp(speed, "50") == 0)
	return (B50);
    if (strcmp(speed, "0") == 0)
	return (B0);
    return (B0);
}
#else
char *speeds[] = {
    "0", "50", "75", "110", "134", "150", "200", "300", "600",
    "1200", "1800", "2400", "4800", "9600", "19200", "38400",
#ifdef	AFS_HPUX_ENV
    "EXTA", "EXTB"
#endif
};

#define	NSPEEDS	(sizeof(speeds) / sizeof(speeds[0]))
#endif

setup_term(fd)
     int fd;
{
#ifndef	AFS_OSF_ENV
    register char *cp = strchr(term, '/'), **cpp;
#endif
#ifdef	AFS_AIX32_ENV
#ifdef _AIX
    /* POSIX TERMIOS */
    struct termios termios;
#else /* _AIX */
    struct sgttyb sgttyb;
#endif /* _AIX */
    char *speed;
#ifdef	AFS_HPUX_ENV
    struct termio tp;
#endif

#ifdef _AIX
    /* POSIX TERMIOS */
    tcgetattr(fd, &termios);
#else /* _AIX */
#ifdef	AFS_HPUX_ENV
    ioctl(fd, TCGETA, &tp);
#else
    (void)ioctl(fd, TIOCGETP, &sgttyb);
#endif
#endif /* _AIX */
    if (cp) {
	*cp++ = '\0';
	speed = cp;
	cp = strchr(speed, '/');
	if (cp)
	    *cp++ = '\0';
#ifdef _AIX
	/* POSIX TERMIOS */
	/* Setup PTY with some reasonable defaults */
	termios.c_cc[VINTR] = CINTR;
	termios.c_cc[VQUIT] = CQUIT;
	termios.c_cc[VERASE] = CERASE;
	termios.c_cc[VKILL] = CKILL;
	termios.c_cc[VEOF] = CEOF;
#if defined (NLS) || defined (KJI)
	/* For NLS environments, we need 8 bit data stream. */
	termios.c_iflag = IXON | BRKINT | IGNPAR | ICRNL;
	termios.c_cflag = PARENB | CS8 | HUPCL | CREAD;
#else
	termios.c_iflag = IXON | BRKINT | IGNPAR | ISTRIP | ICRNL;
	termios.c_cflag = PARENB | CS7 | HUPCL | CREAD;
#endif /* NLS */
	termios.c_oflag = OPOST | ONLCR | TAB3;
	termios.c_lflag =
	    ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL | IEXTEN;
	cfsetispeed(&termios, speeds(speed));
	cfsetospeed(&termios, speeds(speed));
#else /* _AIX */
	for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
	    if (strcmp(*cpp, speed) == 0) {
#ifdef	AFS_HPUX_ENV
		tp.c_cflag &= ~CBAUD;
		tp.c_cflag |= cpp - speeds;
#else
		sgttyb.sg_ispeed = sgttyb.sg_ospeed = cpp - speeds;
#endif
		break;
	    }
#endif /* _AIX */
    }
#ifdef _AIX
    /* POSIX TERMIOS */
    tcsetattr(fd, TCSANOW, &termios);
#else /* _AIX */
#ifdef	AFS_HPUX_ENV
    tp.c_iflag &= ~INPCK;
    tp.c_iflag |= ICRNL | IXON;
    tp.c_oflag |= OPOST | ONLCR | TAB3;
    tp.c_oflag &= ~ONLRET;
    tp.c_lflag |= (ECHO | ECHOE | ECHOK | ISIG | ICANON);
    tp.c_cflag &= ~PARENB;
    tp.c_cflag |= CS8;
    tp.c_cc[VMIN] = 1;
    tp.c_cc[VTIME] = 0;
    tp.c_cc[VEOF] = CEOF;
    ioctl(fd, TCSETAF, &tp);
#else
    sgttyb.sg_flags = ECHO | CRMOD | ANYP | XTABS;
    (void)ioctl(fd, TIOCSETP, &sgttyb);
#endif
#endif /* _AIX */

#else /* AFS_AIX32_ENV */

#ifdef	AFS_OSF_ENV
    register char *cp = strchr(term + ENVSIZE, '/');
    char *speed;
    struct termios tt;

    tcgetattr(fd, &tt);
    if (cp) {
	*cp++ = '\0';
	speed = cp;
	cp = strchr(speed, '/');
	if (cp)
	    *cp++ = '\0';
	cfsetspeed(&tt, atoi(speed));
    }

    tt.c_iflag = TTYDEF_IFLAG;
    tt.c_oflag = TTYDEF_OFLAG;
    tt.c_lflag = TTYDEF_LFLAG;
    memcpy(tt.c_cc, ttydefchars, sizeof(tt.c_cc));
    tcsetattr(fd, TCSAFLUSH, &tt);
#else
    struct sgttyb sgttyb;
    char *speed;

    (void)ioctl(fd, TIOCGETP, &sgttyb);
    if (cp) {
	*cp++ = '\0';
	speed = cp;
	cp = strchr(speed, '/');
	if (cp)
	    *cp++ = '\0';
	for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
	    if (strcmp(*cpp, speed) == 0) {
		sgttyb.sg_ispeed = sgttyb.sg_ospeed = cpp - speeds;
		break;
	    }
    }
    sgttyb.sg_flags = ECHO | CRMOD | ANYP | XTABS;
    (void)ioctl(fd, TIOCSETP, &sgttyb);
#endif
#endif /* AFS_AIX32_ENV */
    env[0] = term;
    env[1] = 0;
    environ = env;
}

/*
 * Check whether host h is in our local domain,
 * as determined by the part of the name following
 * the first '.' in its name and in ours.
 * If either name is unqualified (contains no '.'),
 * assume that the host is local, as it will be
 * interpreted as such.
 */
local_domain(h)
     char *h;
{
    char localhost[MAXHOSTNAMELEN];
    char *p1, *p2 = strchr(h, '.');
#ifdef	AFS_OSF_ENV
    char *topdomain();

    localhost[0] = 0;
#endif

    (void)gethostname(localhost, sizeof(localhost));
#ifdef	AFS_OSF_ENV
    p1 = topdomain(localhost);
    p2 = topdomain(h);
#else
    p1 = strchr(localhost, '.');
#endif
    if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
	return (1);
    return (0);
}
#endif /* OLD_LOGIN */


#ifdef	AFS_OSF_ENV
char *
topdomain(h)
     char *h;
{
    register char *p;
    char *maybe = NULL;
    int dots = 0;

    for (p = h + strlen(h); p >= h; p--) {
	if (*p == '.') {
	    if (++dots == 2)
		return (p);
	    maybe = p;
	}
    }
    return (maybe);
}
#endif


#ifdef _AIX
/*
 * NAME: getnonflatname()
 *                                                                    
 * FUNCTION: gets the name in /etc/passwd and replaces the
 *		flattened name that might have been passed in.
 *                                                                    
 * EXECUTION ENVIRONMENT: static
 *                                                                   
 * RETURNS: none.
 *
 */

void
getnonflatname(char *user)
{
    struct passwd *pw;		/* return from getpwent() */
    char currflat[UT_NAMESIZE + 1];	/* name we are looking for */
    char dataflat[UT_NAMESIZE + 1];	/* name from data base */
    register int siz;
    register int members = 0;

#ifndef _KJI
    setpwent();

    while ((pw = getpwent()) != NULL) {
	/* Map NLS characters in name to ascii */
	NLflatstr(user, currflat, UT_NAMESIZE + 1);
	NLflatstr(pw->pw_name, dataflat, UT_NAMESIZE + 1);
	if (strcmp(currflat, dataflat) == 0) {
	    strcpy(user, pw->pw_name);
	    break;
	}
    }
    endpwent();
#endif
}


checknologin()
{
    register int fd;

    if ((fd = open(NOLOGIN, O_RDONLY, 0)) >= 0) {
	return (0);
	close(fd);
    } else
	return (-1);
}


/*
 * NAME: loginlog
 *                                                                    
 * FUNCTION: log the login
 *                                                                    
 * EXECUTION ENVIRONMENT: static
 *
 *		record login in utmp and wtmp files
 *                                                                   
 * RETURNS: void
 */

static void
loginlog(user, tty, hostname)
     char *user;
     char *tty;
     char *hostname;
{
    char *home;			/* user's home directory */
    char *dev;			/* final portion of tty name [ with hft# ] */
    char hush[PATH_MAX];	/* .hushlogin file */
    int t;			/* index into the utmp file */
    int found = 0;		/* utmp entry was found */
    int f;			/* file descriptor */
    uid_t uid;			/* the user ID */
    off_t offset;		/* for keeping wtmp file un-corrupted */
    struct utmp utmp;		/* utmp structure being created */
    struct utmp outmp;		/* utmp entry from /etc/utmp file */
    struct utmp *up;		/* pointer to entry in utmp file */

    int rcode;

    /*
     * Initialize a utmp file entry to be filled in and written
     * to UTMP_FILE and WTMP_FILE.
     */

    memset(&utmp, 0, UTSIZ);
    up = &utmp;

    /*
     * The last portion of the device name is placed in the utmp
     * entry.  If the last portion is all digits it is assumed to
     * be a multiplexed device, such as an hft, and the pointer
     * is backed up to find the previous portion.
     */

    if (dev = strrchr(tty, '/')) {
	if (!*(dev + strspn(dev, "/0123456789")))
	    while (dev != tty && *--dev != '/');

	if (*dev == '/')
	    dev++;

	tty = dev;
    }

    if (tty)
	strncpy(up->ut_line, tty, sizeof up->ut_line);

    /*
     * The remainder of the utmp entry is filled in.  The process
     * id and tty line should already have been present, however,
     * these are set here for completeness.
     */

    strncpy(up->ut_user, user, sizeof up->ut_user);
    up->ut_pid = getpid();
    up->ut_type = LOGIN_PROCESS;
    up->ut_time = time((time_t *) 0);

    if (hostname)
	strncpy(up->ut_host, hostname, sizeof up->ut_host);

    /* Scan the utmp file. If an entry is found that        */
    /* is the same as this tty, use this slot to create the */
    /* new entry. This slot has to be an empty slot.        */
    /* Re-using the same slot used by the tty (with an      */
    /* empty type) will avoid printing of empty slots on    */
    /* "who -a" output.                                     */

    if ((f = open(UTMP_FILE, O_RDWR)) >= 0) {
	lseek(f, (off_t) 0, SEEK_SET);
	rcode = lockf(f, F_LOCK, 0);
	t = 0;
	while (read(f, (char *)&outmp, UTSIZ) == UTSIZ) {
	    if ((!strcmp(outmp.ut_line, tty)) && outmp.ut_type == EMPTY) {
		break;
	    } else
		t++;
	}
	lseek(f, (off_t) (t * UTSIZ), 0);
	write(f, (char *)up, UTSIZ);
	lseek(f, (off_t) 0, SEEK_SET);
	rcode = lockf(f, F_ULOCK, 0);
	close(f);
    }

    /*
     * The utmp entry is appended to the wtmp file to maintain a
     * log of all login activity.
     */
    if ((f = open(WTMP_FILE, O_WRONLY | O_APPEND)) >= 0) {
	offset = lseek(f, 0L, 2);
	if (offset % UTSIZ)
	    write(f, (char *)up, UTSIZ - (offset % UTSIZ));

	write(f, (char *)up, UTSIZ);
	close(f);
    }

}

#ifdef	KAUTH
#define MAXLOCALTOKENS 4
#define HEADER_SIZE 18

/*
 * intkens:
 *
 * This routine accepts a token on the specified file handle;
 * The input format for a token is:
 *
 *   Field #    Contents         description
 *    (1)       Version #        unsigned integer (< 2^32)
 *    (2)       Length           unsigned integer (< 2^32)
 *    (3)       startTime        unsigned afs_int32 (< 2^32)
 *    (4)       endTime          unsigned afs_int32 (< 2^32)
 *    (5)       sessionKey       char[8]
 *    (6)       kvno             short (< 2^16)
 *    (7)       ticketLen        unsigned integer (< 2^32)
 *    (8)       ticket           char[MAXKTCTICKETLEN]
 *    (9)       AFS name         char[MAXKTCNAMELEN]
 *    (10)      Cell Name        char[MAXKTCREALMLEN]
 *
 * Each field is comma separated except (5) and (6), and (8) and (9);  the
 * ticket is variable length.  The * converted token is placed into the
 * token structure pointed to by the variable "token".  The first and second
 * fields are padded with spaces on the right so that they can be fixed length.
 * This is required so that the length can be read in before reading in the
 * whole packet.
 */

extern int errno;

intokens(s)
     int s;
{
    char buf[512 * MAXLOCALTOKENS], *bp;
    int length, readed, count;
    unsigned index, version;
    struct ktc_token token;
    struct ktc_principal tclient, tserver;

    if (setpag()) {
	perror("setpag");
	exit(1);
    }

/*
**	Read in the first two fields.
*/

    readed = 0;
    errno = 0;
    while (readed < HEADER_SIZE) {
	count = read(s, (char *)((int)buf + readed), HEADER_SIZE - readed);
	if (count <= 0) {
	    perror("intokens read");
	    exit(1);
	}
	readed = readed + count;
    }

    count = readed;
    bp = buf;

    /* (1) Version # */
    for (index = 0; (index + bp - buf) < count && bp[index] != ','; index++);

    if ((index + bp - buf) == count) {
	fprintf(stderr, "overran buffer while searching for version #\n");
	exit(1);
    }

    if (bp[index] != ',') {
	fprintf(stderr, "Didn't stop on a comma, searching for version #\n");
	exit(1);
    }

    bp[index] = '\0';

    sscanf(bp, "%u", &version);

    if (version != 2) {
	fprintf(stderr, "intokens: incompatible version encountered: %d\n",
		version);
	exit(1);
    }

    bp = bp + index + 1;

    /* (2) Length # */
    for (index = 0; (index + bp - buf) < count && bp[index] != ','; index++);

    if ((index + bp - buf) == count) {
	fprintf(stderr, "overran buffer while searching for length #\n");
	exit(1);
    }

    if (bp[index] != ',') {
	fprintf(stderr, "Didn't stop on a comma, searching for length\n");
	exit(1);
    }

    bp[index] = '\0';

    sscanf(bp, "%u", &length);

    bp = bp + index + 1;

    errno = 0;
    while (readed < length) {
	count = read(s, (char *)((int)buf + readed), length - readed);
	if (count <= 0) {
	    perror("intokens read");
	    exit(1);
	}
	readed = readed + count;
    }

    count = readed;

/*
**	Terminate looping through the list of tokens when we hit a null byte.
*/
    while (*bp) {
	/* (3) startTime */

	for (index = 0; (index + bp - buf) < count && bp[index] != ',';
	     index++);

	if ((index + bp - buf) == count) {
	    fprintf(stderr,
		    "overran buffer while searching for startTime #\n");
	    exit(1);
	}

	if (bp[index] != ',') {
	    fprintf(stderr,
		    "Didn't stop on a comma, searching for startTime #\n");
	    exit(1);
	}

	bp[index] = '\0';

	sscanf(bp, "%u", &token.startTime);

	/* (4) endTime */

	bp = bp + index + 1;

	for (index = 0; (index + bp - buf) < count && bp[index] != ',';
	     index++);

	if ((index + bp - buf) == count) {
	    fprintf(stderr, "overran buffer while searching for endTime #\n");
	    exit(1);
	}

	if (bp[index] != ',') {
	    fprintf(stderr,
		    "Didn't stop on a comma, searching for endTime #\n");
	    exit(1);
	}

	bp[index] = '\0';

	sscanf(bp, "%u", &token.endTime);

	/* (5) sessionKey */

	bp = bp + index + 1;
	memcpy(token.sessionKey.data, bp, 8);

	/* (6) kvno */

	bp = bp + 8;
	for (index = 0; (index + bp - buf) < count && bp[index] != ',';
	     index++);

	if ((index + bp - buf) == count) {
	    fprintf(stderr, "overran buffer while searching for kvno\n");
	    exit(1);
	}

	if (bp[index] != ',') {
	    fprintf(stderr, "Didn't stop on a comma, searching for kvno\n");
	    exit(1);
	}

	bp[index] = '\0';

	token.kvno = atoi(bp);

	/* (7) ticketLen */

	bp = bp + index + 1;

	for (index = 0; (index + bp - buf) < count && bp[index] != ',';
	     index++);

	if ((index + bp - buf) == count) {
	    fprintf(stderr, "overran buffer while searching for ticketLen\n");
	    exit(1);
	}

	if (bp[index] != ',') {
	    fprintf(stderr,
		    "Didn't stop on a comma, searching for ticketLen\n");
	    exit(1);
	}

	bp[index] = '\0';

	sscanf(bp, "%u", &token.ticketLen);

	/* (8) ticketLen */

	bp = bp + index + 1;

	if ((index + bp - buf) + token.ticketLen > count) {
	    fprintf(stderr, "overran buffer while copying ticket\n");
	    exit(1);
	}

	memcpy(token.ticket, bp, token.ticketLen);

	bp = bp + token.ticketLen;

	/* (9) User name */

	for (index = 0; (index + bp - buf) < count && bp[index] != ',';
	     index++);

	if ((index + bp - buf) == count) {
	    fprintf(stderr, "overran buffer while searching for Cell Name\n");
	    exit(1);
	}

	bp[index] = '\0';
	tclient.instance[0] = '\0';
	strncpy(tclient.name, bp, MAXKTCNAMELEN);

	/* (10) Cell name */

	bp = bp + index + 1;

	for (index = 0; (index + bp - buf) < count && bp[index] != ',';
	     index++);

	if ((index + bp - buf) == count) {
	    fprintf(stderr, "overran buffer while searching for end\n");
	    exit(1);
	}

	bp[index] = '\0';
	strncpy(tclient.cell, bp, MAXKTCREALMLEN);
	bp = bp + index + 1;

	strcpy(tserver.name, "afs");
	tserver.instance[0] = '\0';
	strncpy(tserver.cell, tclient.cell, MAXKTCREALMLEN);

	if (ktc_SetToken(&tserver, &token, &tclient, 0)) {
	    fprintf(stderr, "intokens: ktc_SetToken failed for %s\n",
		    tclient.cell);
	}
    }
    return (0);
}
#endif /* KAUTH */
#endif /* _AIX */

#ifdef	AFS_HPUX_ENV
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdio.h>

# define	MAX_PTY_LEN	MAXPATHLEN+1
# define	CLONE_DRV	"/dev/ptym/clone"

/*
**	Ugh!  The following is gross, but there is no other simple way
**	to tell if a file is a master pty without looking at its major
**	device number.  Lets put it in ifdefs so it will fail to compile
**	on other architectures without modification.
*/
#ifdef __hp9000s300
#define PTY_MASTER_MAJOR_NUMBER 16
#endif
#ifdef __hp9000s800
#define PTY_MASTER_MAJOR_NUMBER 16
#endif

struct stat stb;
struct stat m_stbuf;		/* stat buffer for master pty */
struct stat s_stbuf;		/* stat buffer for slave pty */

/*
**	ptymdirs	--	directories to search for master ptys
**	ptysdirs	--	directories to search for slave ptys
**	ptymloc		--	full path to pty master
**	ptysloc		--	full path to pty slave
*/
char *ptymdirs[] = { "/dev/ptym/", "/dev/", (char *)0 };
char *ptysdirs[] = { "/dev/pty/", "/dev/", (char *)0 };
char ptymloc[MAX_PTY_LEN];
char ptysloc[MAX_PTY_LEN];

/*
**	oltrs	--	legal first char of pty name (letter) (old style)
**	onums	--	legal second char of pty name (number) for namespace 1
**              (old style)
**	ltrs	--	legal first char of pty name (letter) (new style)
**	nums	--	legal second and third char of pty name (number) (new style)
**	nums	--	legal second, third and fourth  char of pty name for names
**              space 3. 
*/
char oltrs[] = "onmlkjihgfecbazyxwvutsrqp";
char onums[] = "0123456789abcdef";
char ltrs[] = "pqrstuvwxyzabcefghijklmno";
char nums[] = "0123456789";

/*
**	getpty()	--	get a pty pair
**
**	Input	--	none
**	Output	--	zero if pty pair gotten; non-zero if not
**	[value parameter mfd]
**		  mfd	Master FD for the pty pair
**	[optional value parameters -- only set if != NULL]
**		mname	Master pty file name
**		sname	Slave pty file name
**
**	NOTE: This routine does not open the slave pty.  Therefore, if you
**	want to quickly find the slave pty to open, it is recommended that
**	you *not* pass sname as NULL.
*/

/*
**	Modified 3/28/89 by Peter Notess to search the new, expanded naming
**	convention.  The search is intended to proceed efficiently for both
**	large and small configurations, assuming that the ptys are created
**	in a pre-specified order.  This routine will do a last-ditch,
**	brute-force search of all files in the pty directories if the more
**	efficient search fails.  This is intended to make the search robust
**	in the case where the pty configuration is a non-standard one,
**	although it might take longer to find a pty in this case.  The
**	assumed order of creation is:
**		/dev/ptym/pty[p-z][0-f]
**		/dev/ptym/pty[a-ce-o][0-f]
**		/dev/ptym/pty[p-z][0-9][0-9]
**		/dev/ptym/pty[a-ce-o][0-9][0-9]
**	with /dev/ptym/pty[p-r][0-f] linked into /dev.  The search will
**	proceed in an order that leaves the ptys which also appear in /dev
**	searched last so that they remain available for other applications
**	as long as possible.
*/

/*
 * Modified by Glen A. Foster, September 23, 1986 to get around a problem
 * with 4.2BSD job control in the HP-UX (a.k.a. system V) kernel.  Before
 * this fix, getpty() used to find a master pty to use, then open the cor-
 * responding slave side, just to see if it was there (kind of a sanity
 * check), then close the slave side, fork(), and let the child re-open 
 * the slave side in order to get the proper controlling terminal.  This
 * was an excellent solution EXCEPT for the case when another process was
 * already associated with the same slave side before we (telnetd) were
 * exec()ed.  In that case, the controlling tty stuff gets all messed up,
 * and the solution is to NOT open the slave side in the parent (before the
 * fork()), but to let the child be the first to open it after its setpgrp()
 * call.  This works in all cases.  This stuff is black magic, really!
 *
 * This is necessary due to HP's implementation of 4.2BSD job control.
 */

/*
 * Modified by Byron Deadwiler, March 9, 1992 to add access to the clone
 * driver to get ptys faster.  Also added code to access ptys using
 * a new namespace (naming convention) that increases the number of
 * pty's that can be configured on a system.
 *
 *	Name-Space      It is a union of three name-spaces, the earlier two
 *			being supported for compatibility. The name comprises
 *			a generic name (/dev/pty/tty) followed by an alphabet
 *			followed by one to three numerals. The alphabet is one
 *			of the 25 in alpha[], which has 'p' thro' 'o' excluding
 *			'd'. The numeral is either hex or decimal.
 *	                ------------------------------------
 *	                | minor | name  |    Remarks       |
 *	                |----------------------------------|
 *                  |      0|  ttyp0|  Modulo 16 hex   |
 *	                |      :|      :|  representation. |
 *	                |     15|  ttypf|                  |
 *	                |     16|  ttyq0|                  |
 *	                |      :|      :|                  |
 *	                |    175|  ttyzf|                  |
 *	                |    176|  ttya0|                  |
 *	                |      :|      :|                  |
 *	                |    223|  ttycf|                  |
 *	                |    224|  ttye0|                  |
 *	                |      :|      :|                  |
 *	                |    399|  ttyof|  Total 400       |
 *	                |----------------------------------|
 *	                |    400| ttyp00|  Modulo hundred  |
 *	                |      :|      :|  decimal repr.   |
 *	                |    499| ttyp99|                  |
 *	                |    500| ttyq00|                  |
 *	                |      :|      :|                  |
 *	                |   1499| ttyz99|                  |
 *	                |   1500| ttya00|                  |
 *	                |      :|      :|                  |
 *	                |   1799| ttyc99|                  |
 *	                |   1800| ttye00|                  |
 *	                |      :|      :|                  |
 *	                |   2899| ttyo99|  Total 2500      |
 *	                |----------------------------------|
 *	                |   2900|ttyp000|  Modulo thousand |
 *	                |      :|      :|  decimal repr.   |
 *	                |   3899|ttyp999|                  |
 *	                |   4900|ttyq000|                  |
 *	                |      :|      :|                  |
 *	                |  12899|ttyz999|                  |
 *	                |  13900|ttya000|                  |
 *	                |      :|      :|                  |
 *	                |  16899|ttyc999|                  |
 *	                |  16900|ttye000|                  |
 *	                |      :|      :|                  |
 *	                |  27899|ttyo999|  Total 25000     |
 *	                |  27900|   	|     invalid      |
 *	                ------------------------------------
 */

/*
**	NOTE:	this routine should be put into a library somewhere, since
**	both rlogin and telnet need it!  also, other programs might want to
**	call it some day to get a pty pair ...
*/
getpty(mfd, mname, sname)
     int *mfd;
     char *mname, *sname;
{
    int loc, ltr, num, num2;
    register int mlen, slen;
    char *s, *s_path;

    if ((*mfd = open(CLONE_DRV, O_RDWR)) != -1) {
	s_path = ptsname(*mfd);
	strcpy(sname, s_path);
	(void)chmod(sname, 0622);
	/* get master path name */
	s = strrchr(sname, '/') + 2;	/* Must succeed since slave_pty */
	/* begins with /dev/            */
	sprintf(mname, "%s%s%s", ptymdirs[0], "p", s);
	return 0;
    }


    for (loc = 0; ptymdirs[loc] != NULL; loc++) {
	if (stat(ptymdirs[loc], &stb))	/* no directory ... */
	    continue;		/*  so try next one */

	/*      first, try the 3rd name space ptyp000-ptyo999  */
	/*      generate the master pty path    */
	if (namesp3(mfd, mname, sname, loc) == 0) {
	    return 0;
	}

	/*      second, try the 2nd name space ptyp00-ptyo99  */
	/*      generate the master pty path    */
	(void)strcpy(ptymloc, ptymdirs[loc]);
	(void)strcat(ptymloc, "ptyLNN");
	mlen = strlen(ptymloc);

	/*      generate the slave pty path     */
	(void)strcpy(ptysloc, ptysdirs[loc]);
	(void)strcat(ptysloc, "ttyLNN");
	slen = strlen(ptysloc);

	for (ltr = 0; ltrs[ltr] != '\0'; ltr++) {
	    ptymloc[mlen - 3] = ltrs[ltr];
	    ptymloc[mlen - 2] = '0';
	    ptymloc[mlen - 1] = '0';
	    if (stat(ptymloc, &stb))	/* no ptyL00 ... */
		break;		/* go try old style names */

	    for (num = 0; nums[num] != '\0'; num++)
		for (num2 = 0; nums[num2] != '\0'; num2++) {
		    ptymloc[mlen - 2] = nums[num];
		    ptymloc[mlen - 1] = nums[num2];
		    if ((*mfd = open(ptymloc, O_RDWR)) < 0)	/* no master        */
			continue;	/* try next num */

		    ptysloc[slen - 3] = ltrs[ltr];
		    ptysloc[slen - 2] = nums[num];
		    ptysloc[slen - 1] = nums[num2];

		    /*
		     **     NOTE:   changed to only stat the slave device; see
		     **     comments all over the place about job control ...
		     */
		    if (fstat(*mfd, &m_stbuf) < 0
			|| stat(ptysloc, &s_stbuf) < 0) {
			close(*mfd);
			continue;
		    }
		    /*
		     **     sanity check: are the minor numbers the same??
		     */
		    if (minor(m_stbuf.st_rdev) != minor(s_stbuf.st_rdev)) {
			close(*mfd);
			continue;	/* try next num */
		    }

		    /*      else we got both a master and a slave pty       */
		  got_one:(void)chmod(ptysloc, 0622);
		    /* not readable */
		    if (mname != NULL)
			(void)strcpy(mname, ptymloc);
		    if (sname != NULL)
			(void)strcpy(sname, ptysloc);
		    return 0;	/* return OK    */
		}
	}

	/*      now, check old-style names      */
	/*  the 1st name-space ptyp0-ptyof */
	/*      generate the master pty path    */
	(void)strcpy(ptymloc, ptymdirs[loc]);
	(void)strcat(ptymloc, "ptyLN");
	mlen = strlen(ptymloc);

	/*      generate the slave pty path     */
	(void)strcpy(ptysloc, ptysdirs[loc]);
	(void)strcat(ptysloc, "ttyLN");
	slen = strlen(ptysloc);

	for (ltr = 0; oltrs[ltr] != '\0'; ltr++) {
	    ptymloc[mlen - 2] = oltrs[ltr];
	    ptymloc[mlen - 1] = '0';
	    if (stat(ptymloc, &stb))	/* no ptyL0 ... */
		continue;	/* try next ltr */

	    for (num = 0; onums[num] != '\0'; num++) {
		ptymloc[mlen - 1] = onums[num];
		if ((*mfd = open(ptymloc, O_RDWR)) < 0)	/* no master        */
		    continue;	/* try next num */

		ptysloc[slen - 2] = oltrs[ltr];
		ptysloc[slen - 1] = onums[num];

		/*
		 **     NOTE:   changed to only stat the slave device; see
		 **     comments all over the place about job control ...
		 */
		if (fstat(*mfd, &m_stbuf) < 0 || stat(ptysloc, &s_stbuf) < 0) {
		    close(*mfd);
		    continue;
		}
		/*
		 **     sanity check: are the minor numbers the same??
		 */
		if (minor(m_stbuf.st_rdev) != minor(s_stbuf.st_rdev)) {
		    close(*mfd);
		    continue;	/* try next num */
		}

		/*      else we got both a master and a slave pty       */
		goto got_one;
	    }
	}
    }
    /* we failed in our search--we now try the slow brute-force method */
    for (loc = 0; ptymdirs[loc] != NULL; loc++) {
	DIR *dirp;
	struct dirent *dp;

	if ((dirp = opendir(ptymdirs[loc])) == NULL)	/* no directory ... */
	    continue;		/*  so try next one */

	(void)strcpy(ptymloc, ptymdirs[loc]);
	mlen = strlen(ptymloc);
	(void)strcpy(ptysloc, ptysdirs[loc]);
	slen = strlen(ptysloc);

	while ((dp = readdir(dirp)) != NULL) {
	    /* stat, open, go for it, else continue */
	    ptymloc[mlen] = '\0';
	    (void)strcat(ptymloc, dp->d_name);

	    if (stat(ptymloc, &m_stbuf)
		|| (m_stbuf.st_mode & S_IFMT) != S_IFCHR
		|| major(m_stbuf.st_rdev) != PTY_MASTER_MAJOR_NUMBER)
		continue;

	    if ((*mfd = open(ptymloc, O_RDWR)) < 0)	/* busy master      */
		continue;	/* try next entry */

	    ptysloc[slen] = '\0';	/* guess at corresponding slave name */
	    (void)strcat(ptysloc, dp->d_name);
	    if (ptysloc[slen] == 'p')
		ptysloc[slen] = 't';

	    if (stat(ptysloc, &s_stbuf) < 0
		|| minor(m_stbuf.st_rdev) != minor(s_stbuf.st_rdev)) {
		close(*mfd);
		continue;
	    }
	    goto got_one;
	}

	closedir(dirp);
    }

    /*  we were not able to get the master/slave pty pair       */
    return -1;
}

namesp3(mfd, mname, sname, loc)
     int *mfd, loc;
     char *mname, *sname;
{
    int ltr, num, num2, num3;
    register int mlen, slen;

    /*      first, try the new naming convention    */
    /*      generate the master pty path    */
    (void)strcpy(ptymloc, ptymdirs[loc]);
    (void)strcat(ptymloc, "ptyLNNN");
    mlen = strlen(ptymloc);

    /*      generate the slave pty path     */
    (void)strcpy(ptysloc, ptysdirs[loc]);
    (void)strcat(ptysloc, "ttyLNNN");
    slen = strlen(ptysloc);

    for (ltr = 0; ltrs[ltr] != '\0'; ltr++) {
	ptymloc[mlen - 4] = ltrs[ltr];
	ptymloc[mlen - 3] = '0';
	ptymloc[mlen - 2] = '0';
	ptymloc[mlen - 1] = '0';
	if (stat(ptymloc, &stb))	/* no ptyL00 ... */
	    break;		/* go try old style names */

	for (num = 0; nums[num] != '\0'; num++)
	    for (num2 = 0; nums[num2] != '\0'; num2++)
		for (num3 = 0; nums[num3] != '\0'; num3++) {
		    ptymloc[mlen - 3] = nums[num];
		    ptymloc[mlen - 2] = nums[num2];
		    ptymloc[mlen - 1] = nums[num3];
		    if ((*mfd = open(ptymloc, O_RDWR)) < 0)	/* no master        */
			continue;	/* try next num */

		    ptysloc[slen - 4] = ltrs[ltr];
		    ptysloc[slen - 3] = nums[num];
		    ptysloc[slen - 2] = nums[num2];
		    ptysloc[slen - 1] = nums[num3];

		    /*
		     **     NOTE:   changed to only stat the slave device; see
		     **     comments all over the place about job control ...
		     */
		    if (fstat(*mfd, &m_stbuf) < 0
			|| stat(ptysloc, &s_stbuf) < 0) {
			close(*mfd);
			continue;
		    }
		    /*
		     **     sanity check: are the minor numbers the same??
		     */
		    if (minor(m_stbuf.st_rdev) != minor(s_stbuf.st_rdev)) {
			close(*mfd);
			continue;	/* try next num */
		    }

		    /*      else we got both a master and a slave pty       */
		    (void)chmod(ptysloc, 0622);	/* not readable */
		    if (mname != NULL)
			(void)strcpy(mname, ptymloc);
		    if (sname != NULL)
			(void)strcpy(sname, ptysloc);
		    return 0;	/* return OK    */
		}
    }
    return 1;
}
#endif
