/* Copyright (C) 1998,2001 Free Software Foundation, Inc.

   This file is part of GNU Inetutils.

   GNU Inetutils is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   GNU Inetutils is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Inetutils; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA. */

#include "telnetd.h"
#include <sys/wait.h>
#include <fcntl.h>

#ifdef AUTHENTICATION
# include <libtelnet/auth.h>
#endif


extern char line[256];

void
setup_utmp (char *line)
{
  char *ut_id = utmp_ptsid (line, "tn");
  utmp_init (line + sizeof ("/dev/") - 1, ".telnet", ut_id);
}

int
startslave (char *host, int autologin, char *autoname)
{
  pid_t pid;
  static int master;
  int slave_pty;
  char *slave;
  char bbuf[1024];

  
#ifdef AUTHENTICATION
  if (!autoname || !autoname[0])
    autologin = 0;

  if (autologin < auth_level)
    {
      fatal (net, "Authorization failed");
      exit (1);
    }
#endif

#define SOFLGS O_RDWR | O_NOCTTY

  slave_pty = open("/dev/ptc",SOFLGS);

  if((slave_pty == 0) || (slave_pty < 0)) {
	sprintf(bbuf,"openpty: %d",errno);
	fatal(net, bbuf);
  }

  if(grantpt(slave_pty) < 0) {

	sprintf(bbuf,"grant: %d",errno);

	fatal (net, bbuf);
	pid = -1;

  } else {

	if(unlockpt(slave_pty) < 0) {

	 	fatal (net, "unlock pty");
		pid = -1;

  	} else {

		slave = ptsname(slave_pty);
		strncpy(line,slave,256);

#define MASTER_OUTSIDE_FORK 1

#ifdef MASTER_OUTSIDE_FORK

		master = open(slave, SOFLGS);

        	if(master < 0) {
	  		fatal (net, "open slave");
			debug_output_data("fatal: failed to open %s\n",slave);
			pid = -1;
   		} else {	
#endif


			pid = fork();

			if(pid) { 
#ifdef MASTER_OUTSIDE_FORK
				//ioctl(master, TIOCUCNTL, 1);

				ioctl(master, I_PUSH, "ldterm");
#endif
				debug_output_data("fork() called - child is pid %d master is %d slave is %s slave_pty is %d\n",pid,master,slave,slave_pty);
			} else {
#ifndef MASTER_OUTSIDE_FORK
				master = open(slave, SOFLGS);
				if(master < 0) {
					fatal(net, "open master");
					pid = -1;
				} 
#endif
				if(login_tty(master))
				{
					fatal(net, "login_tty");
					pid = -1;
				}
				debug_output_data("login_tty called for master\n");
				

			}
#ifdef MASTER_OUTSIDE_FORK
		}
#endif
   	}
  }
	
  if (pid < 0)
    {
      if (errno == ENOENT)
	{
	  syslog (LOG_ERR, "Out of ptys (sheer)");
	  fatal (net, "Out of ptys (Sheer) ");
	}
      else
	{
	  syslog (LOG_ERR, "forkpty: %m");
	  fatal (net, "Forkpty");
	}
    }

  if (pid == 0)
      {
	/* Child */
	debug_output_data("CHILD: Net: %d master: %d\n",net,master);

	if (net > 2)	
	  close (net);

#ifdef UTMPX
	setup_utmp (line);
#endif
	start_login (host, autologin, line);
      }

  /* Master */
  return master;
//	return slave_pty;
}
  
extern char **environ;
/*
 * scrub_env()
 *
 * Remove a few things from the environment that
 * don't need to be there.
 *
 * Security fix included in telnet-95.10.23.NE of David Borman <deb@cray.com>.
 */
static void
scrub_env ()
{
  register char **cpp, **cpp2;

  for (cpp2 = cpp = environ; *cpp; cpp++)
    {
      if (strncmp (*cpp, "LD_", 3) 
	  && strncmp (*cpp, "_RLD_", 5) 
	  && strncmp (*cpp, "LIBPATH=", 8) 
	  && strncmp (*cpp, "IFS=", 4))
	*cpp2++ = *cpp;
    }
  *cpp2 = 0;
}

void
start_login (char *host, int autologin, char *name)
{
  char *cmd;
  int argc;
  char **argv;
  
  scrub_env ();

  debug_output_data("start_login %s\n", name);

  /* Set the environment variable "LINEMODE" to indicate our linemode */
  if (lmodetype == REAL_LINEMODE)
    setenv ("LINEMODE", "real", 1);
  else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK)
    setenv ("LINEMODE", "kludge", 1);

  cmd = expand_line (login_invocation);
  if (!cmd)
    fatal (net, "can't expand login command line");
  argcv_get (cmd, "", &argc, &argv);

  debug_output_data("execute command: %s\n", cmd);

  execv (argv[0], argv);
  syslog (LOG_ERR, "%s: %m\n", cmd);
  fatalperror (net, cmd);
}

void
cleanup (int sig)
{
  char *p;

  if (sig)
    {
      int status;
      pid_t pid = waitpid((pid_t)-1, &status, WNOHANG);
      syslog (LOG_INFO, "child process %ld exited: %d",
	      (long) pid, WEXITSTATUS(status));
    }
  
  p = line + sizeof (PATH_DEV) - 1;
  utmp_logout (p);
  chmod (line, 0644);
  chown (line, 0, 0);
  shutdown (net, 2);
  exit (1);
}

  
