view lib/pty.c @ 210:82ebd1b05345

2003-7-6 Brian Masney <masneyb@gftp.org> * lib/pty.c lib/gftp.h - added gftp_exec_with_new_pty() and gftp_exec_without_new_pty() * lib/sshv2.c - use the 2 new functions above * lib/pty.c lib/gftp.h - split open_ptys() to _gftp_ptym_open() and _gftp_ptys_open() * lib/sslcommon.c - don't do thread setup if we are compiling against glib 1.2. I do not want to link against the pthread library because that would make the text port dependant on pthreads being installed on the box
author masneyb
date Sun, 06 Jul 2003 13:52:43 +0000
parents 8d933999bba6
children b8d14c2c3097
line wrap: on
line source

/*****************************************************************************/
/*  pty.c - general purpose routines                                         */
/*  Copyright (C) 1998-2003 Brian Masney <masneyb@gftp.org>                  */
/*                                                                           */
/*  This program 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 of the License, or        */
/*  (at your option) any later version.                                      */
/*                                                                           */
/*  This program is distributed in the hope that it will be useful,          */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/*  MERCHANTABILITY or FITNESS FOR A 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 this program; if not, write to the Free Software              */
/*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA      */
/*****************************************************************************/

static const char cvsid[] = "$Id$";

#include "gftp.h"

#ifdef __sgi

char *
gftp_get_pty_impl (void)
{
  return ("sgi");
}


static int
_gftp_ptym_open (char *pts_name, size_t len, int *fds)
{
  char *new_pts_name;
  int fdm;

  if ((new_pts_name = _getpty (&fdm, O_RDWR, 0600, 0)) == NULL)
    return (GFTP_ERETRYABLE);

  strncpy (pts_name, new_pts_name, len);

  return (fdm);
}


static int
_gftp_ptys_open (int fdm, int fds, char *pts_name)
{
  int new_fds;

  if ((new_fds = open (pts_name, O_RDWR)) < 0)
    {
      close (fdm);
      return (-1);
    }

  return (new_fds);
}

#elif HAVE_GRANTPT

char *
gftp_get_pty_impl (void)
{
  return ("unix98");
}


static int
_gftp_ptym_open (char *pts_name, size_t len, int *fds)
{
  char *new_pts_name;
  int fdm;

  if ((fdm = open ("/dev/ptmx", O_RDWR)) < 0)
    return (GFTP_ERETRYABLE);

  if (grantpt (fdm) < 0)
    {
      close (fdm);
      return (GFTP_ERETRYABLE);
    }

  if (unlockpt (fdm) < 0)
    {
      close (fdm);
      return (GFTP_ERETRYABLE);
    }

  if ((new_pts_name = ptsname (fdm)) == NULL)
    {
      close (fdm);
      return (GFTP_ERETRYABLE);
    }

  strncpy (pts_name, new_pts_name, len);

  return (fdm);
}


static int
_gftp_ptys_open (int fdm, int fds, char *pts_name)
{
  int new_fds;

  if ((new_fds = open (pts_name, O_RDWR)) < 0)
    {
      close (fdm);
      return (-1);
    }

#ifdef SYSV
  /* I intentionally ignore these errors */
  ioctl (new_fds, I_PUSH, "ptem");
  ioctl (new_fds, I_PUSH, "ldterm");
  ioctl (new_fds, I_PUSH, "ttcompat");
#endif

  return (new_fds);
}

#elif HAVE_OPENPTY

char *
gftp_get_pty_impl (void)
{
  return ("openpty");
}


static int
_gftp_ptym_open (char *pts_name, size_t len, int *fds)
{
  int fdm;

  if (openpty (&fdm, fds, pts_name, NULL, NULL) < 0)
    return (GFTP_ERETRYABLE);

  ioctl (*fds, TIOCSCTTY, NULL);

  return (fdm);
}


static int
_gftp_ptys_open (int fdm, int fds, char *pts_name)
{
  if (login_tty (fds) < 0)
    return (GFTP_EFATAL);

  return (fds);
}

#else /* !HAVE_OPENPTY */

/* Fall back to *BSD... */

char *
gftp_get_pty_impl (void)
{
  return ("bsd");
}


static int
_gftp_ptym_open (char *pts_name, size_t len, int *fds)
{
  char *pos1, *pos2;
  int fdm;

  g_return_val_if_fail (len >= 10, GFTP_EFATAL);

  strncpy (pts_name, "/dev/ptyXY", len);
  for (pos1 = "pqrstuvwxyzPQRST"; *pos1 != '\0'; pos1++) 
    {
      pts_name[8] = *pos1;
      for (pos2 = "0123456789abcdef"; *pos2 != '\0'; pos2++)
        {
          pts_name[9] = *pos2;
          if ((fdm = open (pts_name, O_RDWR)) < 0)
            continue;

          pts_name[5] = 't';
          chmod (pts_name, S_IRUSR | S_IWUSR);
          chown (pts_name, getuid (), -1);

          return (fdm);
        }
    }

  return (GFTP_ERETRYABLE);
}


static int
_gftp_ptys_open (int fdm, int fds, char *pts_name)
{
  int new_fds;

  if ((new_fds = open (pts_name, O_RDWR)) < 0)
    {
      close (fdm);
      return (-1);
    }

#if defined(TIOCSCTTY) && !defined(CIBAUD)
  ioctl (new_fds, TIOCSCTTY, NULL);
#endif

  return (new_fds);
}

#endif /* __sgi */


static int
_gftp_tty_raw (int fd)
{
  struct termios buf;

  if (tcgetattr (fd, &buf) < 0)
    return (-1);

  buf.c_iflag |= IGNPAR;
  buf.c_iflag &= ~(ICRNL | ISTRIP | IXON | IGNCR | IXANY | IXOFF | INLCR);
  buf.c_lflag &= ~(ECHO | ICANON | ISIG | ECHOE | ECHOK | ECHONL);
#ifdef IEXTEN
  buf.c_lflag &= ~(IEXTEN);
#endif

  buf.c_oflag &= ~(OPOST);
  buf.c_cc[VMIN] = 1;
  buf.c_cc[VTIME] = 0;

  if (tcsetattr (fd, TCSADRAIN, &buf) < 0)
    return (-1);
  return (0);
}


static void
_gftp_close_all_fds (void)
{
  int i, maxfds;

#ifdef HAVE_GETDTABLESIZE
  maxfds = getdtablesize () - 1;
#elif defined (OPEN_MAX)
  maxfds = OPEN_MAX;
#else
  maxfds = -1;
#endif

  for (i=3; i<maxfds; i++)
    close (i);
}


pid_t
gftp_exec_without_new_pty (gftp_request * request, int *fdm, char **args)
{
  pid_t child;
  int s[2];

  if (socketpair (AF_LOCAL, SOCK_STREAM, 0, s) < 0)
    {
      request->logging_function (gftp_logging_error, request,
                                 _("Cannot create a socket pair: %s\n"),
                                 g_strerror (errno));
      return (-1);
    }

  if ((child = fork ()) == 0)
    {
      setsid ();

      close (s[0]);

      _gftp_tty_raw (s[1]);
      dup2 (s[1], 0);
      dup2 (s[1], 1);
      dup2 (s[1], 2);
      _gftp_close_all_fds ();

      execvp (args[0], args);

      printf (_("Error: Cannot execute ssh: %s\n"), g_strerror (errno));
      exit (1);
    }
  else if (child > 0)
    {
      close (s[1]);
      _gftp_tty_raw (s[0]);
      *fdm = s[0];
      return (child);
    }
  else
    {
      request->logging_function (gftp_logging_error, request->user_data,
                                 _("Cannot fork another process: %s\n"),
                                 g_strerror (errno));
      return (-1);
    }
}


pid_t
gftp_exec_with_new_pty (gftp_request * request, int *fdm, char **args)
{
  char pts_name[64];
  pid_t child;
  int fds;

  *pts_name = '\0';
  if ((*fdm = _gftp_ptym_open (pts_name, sizeof (pts_name), &fds)) < 0)
    {
      request->logging_function (gftp_logging_error, request->user_data,
                                _("Cannot open master pty %s: %s\n"), pts_name,
                                g_strerror (errno));
      return (-1);
    }

  if ((child = fork ()) == 0)
    {
      setsid ();

      if ((fds = _gftp_ptys_open (*fdm, fds, pts_name)) < 0)
        {
          printf ("Cannot open slave pts %s: %s\n", pts_name, 
                  g_strerror (errno));
          return (-1);
        }

      close (*fdm);

      _gftp_tty_raw (fds);
      dup2 (fds, 0);
      dup2 (fds, 1);
      dup2 (fds, 2);
      _gftp_close_all_fds ();

      execvp (args[0], args);

      printf (_("Error: Cannot execute ssh: %s\n"), g_strerror (errno));
      exit (1);
    }
  else if (child > 0)
    {
      _gftp_tty_raw (*fdm);
      return (child);
    }
  else
    {
      request->logging_function (gftp_logging_error, request->user_data,
                                 _("Cannot fork another process: %s\n"),
                                 g_strerror (errno));
      return (-1);
    }
}