#define I_IOCTL
#define I_SOCKET
#define I_ERRNO
#define I_STRING
#define I_SYS
#include "includes.h"

#include "debug.h"
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <pwd.h>

#ifdef USE_TIOCNOTTY
#include <sys/ioctl.h>
#endif

#ifdef SYSV
#include <stropts.h>
#if defined(SVR4) || defined(SCO)
int grantpt(int);
char *ptsname(int);
int unlockpt(int);
#endif
#endif

int pty_pid;

/*
 * This area heavily modified to use Dynix/PTX pty's
 * basically just dropped in a replacement open_pty command
 * Barry Boone (boone@a.cs.okstate.edu), June 4, 1994
 * Note that *prog is ignored... the shell is determined by SHELL if present,
 * or uses /bin/sh
 */

#ifdef DYNIXPTX

char **masterline;
char **slaveline;

int open_pty(char *prog)
{
  int masterfd = -1,
  slavefd,
  i,
   pid;
  int tempfd;
  char *a;

  masterfd = getpseudotty(&slaveline, &masterline);

  fflush (stdout);
  if (pid = fork ())
      {
         if (pid < 0) return -3;
         i = fcntl(masterfd,F_GETFL);
         fcntl(masterfd,F_SETFL,i | O_NONBLOCK);
         return(masterfd);
      }

    /*
     ** do a term setup here.
     */
    for (slavefd = 3; slavefd < 256; slavefd++)  /* changed */
       fcntl(slavefd,F_SETFD,1);

    setpgrp();
    tempfd = open((char *)slaveline,O_RDWR);
    if (tempfd < 0) return -1;

    if (dup2(tempfd,0) != 0 || dup2(tempfd,1) != 1 || dup2(tempfd,2) != 2)
      {
        close(tempfd);

        return -1;
      }

    close(tempfd);

terminal_restore(0);

a = getenv("SHELL");
if (a == NULL || access(a,1))
  a = "/bin/sh";
    /* these printfs are dangerous on NeXT,
       if the strings are too long, the printfs block! */
    printf ("Remote: term %s\r\n", VERSION);
    printf ("tty %s. Exec %s\r\n", slaveline, a[0]);

    execl(a, a, NULL);
    pty_pid = pid;

    return masterfd;
}
#else /* DYNIXPTX */


/* Moved these strings here for compilers which can't handle 
   aggregate auto initialization.  Makes open_pty() non-reentrant.
   HSW 93-01-29
   */
#ifdef __hpux
static  char masterline[]="/dev/ptym/ptyXY";
static  char slaveline[]="/dev/pty/ttyXY";
static  char *first ="pqrstuv";
#define NBANK 15
#else				/*  not hpux, and not sgi. */
static  char masterline[]="/dev/ptyXY";
static  char *first ="pqrstuvwxyz";
static  char slaveline[]="/dev/ttyXY";
#define NBANK 16
#endif

int open_pty(char *prog) 
{
  int masterfd = -1,
  slavefd = -1,
  i,
  pid;
  int pip[2];
#if !defined(sgi) && !defined(SYSV) && !defined(_AIX)
#define	SFIRST	(sizeof (slaveline) - 3)
#define	SSECOND	(sizeof (slaveline) - 2)

				/* Ok. Stuff for not sgi. */
#define	MFIRST	(sizeof (masterline) - 3)
#define	MSECOND	(sizeof (masterline) - 2)
  char *ptr = first;
  int c;
#else /* sgi || SYSV */
#ifdef SYSV
				/* Stuff for SYSV */
  void (*savesig)(int);
  char *slaveline;
#else /* SYSV */
#ifdef _AIX
                              /* AIX Stuff */
  char *slaveline;
#else /* AIX */
				/* And then finally, sgi stuff. */
  char *linetmp;
  char slaveline[20];
#endif /* AIX */
#endif /* SYSV */
#endif /* sgi || SYSV */
  
#if !defined(sgi) && !defined(SYSV) && !defined(_AIX)
  /* get a pseudo tty */
  ptr = first;
  for (c = *ptr; (c = *ptr); ++ptr) {
    struct stat statbuff;
    
    masterline[MFIRST] = c;
    masterline[MSECOND] = '0';
    
    if (stat (masterline, &statbuff) < 0)
      continue; /* no pty's on this bank availiable */

    i = 0;
again:
    for (; i<NBANK; ++i) {
      masterline[MSECOND] = "0123456789abcdef"[i];
      if ((masterfd = open (masterline, O_RDWR, 0)) > 0)
	break;
    }
    if (i != NBANK) {
				/* Ok. now check to make sure we can */
				/* open the slave as well. */
      slaveline[SFIRST] = masterline[MFIRST];
      slaveline[SSECOND] = masterline[MSECOND];
      
      if ((slavefd = open (slaveline, O_RDWR, 0)) < 0) {
	close(masterfd);
	++i;
	goto again;
      }
      break;
    }
    
  }

  if (!c)
    return -1;
  
  DEBUG_FP(stderr, "%s: opened pty master=%s(%u) slave=%s(%u)\n",
              term_server, masterline, masterfd, slaveline, slavefd);
#ifdef SUIDROOT
  if (fchmod (slavefd, 0622))
    return -2;
#endif
  
  close(slavefd);

#else /* sgi || SYSV || _AIX */

#ifdef SYSV
  if ((masterfd = open("/dev/ptmx", O_RDWR, 0)) < 0)
    return -1;
  savesig = signal(SIGCHLD, SIG_DFL);
  if (grantpt(masterfd) < 0) {
    close(masterfd);
    sigset(SIGCHLD, savesig);
    return -1;
  }
  sigset(SIGCHLD, savesig);
  if (unlockpt(masterfd) < 0) {
    close(masterfd);
    return -1;
  }
  slaveline = ptsname(masterfd);
  if (slaveline == NULL) {
    close(masterfd);
    return -1;
  }
#else /* SYSV */

#ifdef _AIX
  if ( (masterfd=open("/dev/ptc",O_RDWR,0600)) < 0 )
      return -1; /* No pty's available */
  slaveline = ttyname(masterfd);
#else /* AIX */  
  linetmp = _getpty(&masterfd, O_RDWR, 0600, 0);
  
  if (linetmp == NULL)
    return -1; /* no pty's available */
  
  strcpy(slaveline, linetmp);
  
  /* Don't check slave - have to assume it's going to work */

#endif /* AIX */  
#endif /* SYSV */
#endif /* sgi */

  fflush (stdout);
  if (pipe(pip) <0)
    return -3; /* unable to pipe */
  if ((pid = fork ()) < 0)
    return -3; /* unable to fork */
  
  if (pid == 0) { /* child. */
/*    char  ** a, *b[2] = {0, }; Ultrix MIPS compiler chokes */
    char  ** a, *b[2];
    char argv0[1024];
    char *progname=NULL;
    int loginshell=0;

    b[0]=b[1]=NULL;

    close(0);
    close(1);
    close(2);

    lose_ctty();
    
    /* make it control tty */
    if ((slavefd = open (slaveline, O_RDWR, 0)) < 0) {
      exit(1);
    }
#ifdef USE_VHANGUP
    signal (SIGHUP, SIG_IGN);
    vhangup ();
    signal (SIGHUP, SIG_DFL);
    
    if ((slavefd = open (slaveline, O_RDWR, 0)) < 0) {
      exit(1);
    }
#endif

#ifdef SYSV
    if (ioctl(slavefd, I_PUSH, "ptem") < 0) {
      exit(1);
    }
    if (ioctl(slavefd, I_PUSH, "ldterm") < 0) {
      exit(1);
    }
#ifdef SVR4
    if (ioctl(slavefd, I_PUSH, "ttcompat") < 0) {
      exit(1);
    }
#endif /* SVR4 */
#endif /* SYSV */

    /*
     **	do a term setup here.
     */
    dup2 (slavefd, 0);
    dup2 (slavefd, 1);
    dup2 (slavefd, 2);  
    for (slavefd = 3; slavefd < 64; ++slavefd)
    {
      if (slavefd != pip[1])  close(slavefd);
    }

#if !(defined(SVR4)) && !(defined(__hpux))
    terminal_new(0); 
#endif

    /* find out what shell to run. */
    /* note: technically we should free a, but as we are 
     * in the child who will either exec or exit, we can 
     * neglect this.  croutons.
     */
    a = rebuild_arg(prog);
    if ( !a )  a = b; 
    if(rshtype == 1){
      if ( !a[0] )  { a[0] = getenv("SHELL"); loginshell=1; }
      if ( !a[0] )  { a[0] = "/bin/sh"; loginshell=1; }
      progname = a[0];
      if (loginshell) {
  	sprintf(argv0, "-%s", strrchr(a[0], '/') ?
		(char*)(strrchr(a[0], '/') + 1 ) : (char*) a[0]);
  	a[0] = argv0;
	}
    }else{
      if ( !a[0] )  a[0] = getenv("SHELL");
      if ( !a[0] )  a[0] = "/bin/sh";
    };

    /* these printfs are dangerous on NeXT,
       if the strings are too long, the printfs block! */
#if !defined(hcx)
    printf ("Remote: term %s\r\n", VERSION);
    printf ("tty %s. Exec %s\r\n", slaveline, a[0]);
#endif

    /* pipe will close upon successful exec */
    if (fcntl (pip[1], F_SETFD, 1) == -1)
    {
      /* close it now since fcntl failed */
      close (pip[1]);
    }

#ifdef SHAREDIR
    seteuid(getuid());
    setegid(getgid());
#endif
    if(rshtype == 1){
      execvp(progname, a);
    }else{
      execvp(a[0], a);
    };
    
   /* write something so parent notes exec failure */
    write(pip[1],"",1);
    close(pip[1]);

    /* for what it's worth */
    printf ("Exec failed %s\r\n", a[0]);
    exit(1);
  }

  /* parent */
  close(pip[1]);

  /* Wait for exec, read blocks until pipe is closed */
  if (read(pip[0], (char *) &i, 1) == 0)
    DEBUG_FP (stderr, "%s: exec apparently succeeded\n", term_server);
  else
    DEBUG_FP (stderr, "%s: exec apparently failed\n", term_server);
  close(pip[0]);

  set_nonblock(masterfd);
  /*	ioctl(masterfd, TIOCPKY, &one); BSD pty packet mode. */
  pty_pid = pid;
  return masterfd;
}

#endif /* DYNIXPTX */

static char *argv[4] = {"/bin/sh", "-c", NULL, NULL};

/* non-pty exec */
/* usage is more like rsh, shell can handle compound command */
int open_socket(char *prog) 
{
  int i;
  int soc[2];
  int pid;
  int pip[2];

#ifdef NO_UNIX_DOMAIN
  if (s_pipe(soc) < 0)
	return -4;	/* no stream pipe */
#else
  if (socketpair(AF_UNIX, SOCK_STREAM, 0, soc) < 0)  
    return -4;  /* no socket */
#endif

  /* soc[0] is term's end */
  set_nonblock(soc[0]);

  pipe(pip);
  if ((pid = fork ()) < 0)
    return -3; /* unable to fork */
  
  if (pid == 0) /* child. */
  { 
    close (0);
    close (1);
    close (2);

    lose_ctty();

    /* soc[1] is child's end */
    dup2 (soc[1], 0);
    dup2 (soc[1], 1);
    dup2 (soc[1], 2);
    for (i = 3; i < 64;++i)
      if (i != pip[1])
	close(i);
    
    setbuf(stdout, NULL);
    
    /* unpack args */
    for (i=0; prog[i] != '\0'; ++i)
      if (prog[i] == '\377')
	prog[i] = ' ';

    argv[2] = prog;

    /* pipe will close upon successful exec */
    if (fcntl (pip[1], F_SETFD, 1) == -1)
      close (pip[1]);

    seteuid(getuid());
    setegid(getgid());
    execvp(argv[0], argv);

    write(pip[1],"",1);
    close(pip[1]);
    
    printf ("Exec failed %s\r\n", argv[0]);
    exit(1);
  }

  /* parent */
  close(pip[1]);
  close(soc[1]);

  /* Wait for exec, read blocks until pipe is closed */
  if (read(pip[0], (char *) &i, 1) == 0)
    DEBUG_FP (stderr, "%s: exec apparently succeeded\n", term_server);
  else
    DEBUG_FP (stderr, "%s: exec apparently failed\n", term_server);
  close(pip[0]);

  pty_pid = pid;
  return soc[0];
}
