Blame login/openpty.c

Packit Service 82fcde
/* Copyright (C) 1998-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Zack Weinberg <zack@rabi.phys.columbia.edu>, 1998.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <fcntl.h>
Packit Service 82fcde
#include <limits.h>
Packit Service 82fcde
#include <pty.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <termios.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <sys/types.h>
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Return the result of ptsname_r in the buffer pointed to by PTS,
Packit Service 82fcde
   which should be of length BUF_LEN.  If it is too long to fit in
Packit Service 82fcde
   this buffer, a sufficiently long buffer is allocated using malloc,
Packit Service 82fcde
   and returned in PTS.  0 is returned upon success, -1 otherwise.  */
Packit Service 82fcde
static int
Packit Service 82fcde
pts_name (int fd, char **pts, size_t buf_len)
Packit Service 82fcde
{
Packit Service 82fcde
  int rv;
Packit Service 82fcde
  char *buf = *pts;
Packit Service 82fcde
Packit Service 82fcde
  for (;;)
Packit Service 82fcde
    {
Packit Service 82fcde
      char *new_buf;
Packit Service 82fcde
Packit Service 82fcde
      if (buf_len)
Packit Service 82fcde
	{
Packit Service 82fcde
	  rv = ptsname_r (fd, buf, buf_len);
Packit Service 82fcde
Packit Service 82fcde
	  if (rv != 0 || memchr (buf, '\0', buf_len))
Packit Service 82fcde
	    /* We either got an error, or we succeeded and the
Packit Service 82fcde
	       returned name fit in the buffer.  */
Packit Service 82fcde
	    break;
Packit Service 82fcde
Packit Service 82fcde
	  /* Try again with a longer buffer.  */
Packit Service 82fcde
	  buf_len += buf_len;	/* Double it */
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	/* No initial buffer; start out by mallocing one.  */
Packit Service 82fcde
	buf_len = 128;		/* First time guess.  */
Packit Service 82fcde
Packit Service 82fcde
      if (buf != *pts)
Packit Service 82fcde
	/* We've already malloced another buffer at least once.  */
Packit Service 82fcde
	new_buf = realloc (buf, buf_len);
Packit Service 82fcde
      else
Packit Service 82fcde
	new_buf = malloc (buf_len);
Packit Service 82fcde
      if (! new_buf)
Packit Service 82fcde
	{
Packit Service 82fcde
	  rv = -1;
Packit Service 82fcde
	  __set_errno (ENOMEM);
Packit Service 82fcde
	  break;
Packit Service 82fcde
	}
Packit Service 82fcde
      buf = new_buf;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (rv == 0)
Packit Service 82fcde
    *pts = buf;		/* Return buffer to the user.  */
Packit Service 82fcde
  else if (buf != *pts)
Packit Service 82fcde
    free (buf);		/* Free what we malloced when returning an error.  */
Packit Service 82fcde
Packit Service 82fcde
  return rv;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Create pseudo tty master slave pair and set terminal attributes
Packit Service 82fcde
   according to TERMP and WINP.  Return handles for both ends in
Packit Service 82fcde
   AMASTER and ASLAVE, and return the name of the slave end in NAME.  */
Packit Service 82fcde
int
Packit Service 82fcde
openpty (int *amaster, int *aslave, char *name,
Packit Service 82fcde
	 const struct termios *termp, const struct winsize *winp)
Packit Service 82fcde
{
Packit Service 82fcde
#ifdef PATH_MAX
Packit Service 82fcde
  char _buf[PATH_MAX];
Packit Service 82fcde
#else
Packit Service 82fcde
  char _buf[512];
Packit Service 82fcde
#endif
Packit Service 82fcde
  char *buf = _buf;
Packit Service 82fcde
  int master, ret = -1, slave = -1;
Packit Service 82fcde
Packit Service 82fcde
  *buf = '\0';
Packit Service 82fcde
Packit Service 82fcde
  master = getpt ();
Packit Service 82fcde
  if (master == -1)
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  if (grantpt (master))
Packit Service 82fcde
    goto on_error;
Packit Service 82fcde
Packit Service 82fcde
  if (unlockpt (master))
Packit Service 82fcde
    goto on_error;
Packit Service 82fcde
Packit Service 82fcde
#ifdef TIOCGPTPEER
Packit Service 82fcde
  /* Try to allocate slave fd solely based on master fd first. */
Packit Service 82fcde
  slave = ioctl (master, TIOCGPTPEER, O_RDWR | O_NOCTTY);
Packit Service 82fcde
#endif
Packit Service 82fcde
  if (slave == -1)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Fallback to path-based slave fd allocation in case kernel doesn't
Packit Service 82fcde
       * support TIOCGPTPEER.
Packit Service 82fcde
       */
Packit Service 82fcde
      if (pts_name (master, &buf, sizeof (_buf)))
Packit Service 82fcde
        goto on_error;
Packit Service 82fcde
Packit Service 82fcde
      slave = open (buf, O_RDWR | O_NOCTTY);
Packit Service 82fcde
      if (slave == -1)
Packit Service 82fcde
        goto on_error;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* XXX Should we ignore errors here?  */
Packit Service 82fcde
  if (termp)
Packit Service 82fcde
    tcsetattr (slave, TCSAFLUSH, termp);
Packit Service 82fcde
#ifdef TIOCSWINSZ
Packit Service 82fcde
  if (winp)
Packit Service 82fcde
    ioctl (slave, TIOCSWINSZ, winp);
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
  *amaster = master;
Packit Service 82fcde
  *aslave = slave;
Packit Service 82fcde
  if (name != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (*buf == '\0')
Packit Service 82fcde
        if (pts_name (master, &buf, sizeof (_buf)))
Packit Service 82fcde
          goto on_error;
Packit Service 82fcde
Packit Service 82fcde
      strcpy (name, buf);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  ret = 0;
Packit Service 82fcde
Packit Service 82fcde
 on_error:
Packit Service 82fcde
  if (ret == -1) {
Packit Service 82fcde
    close (master);
Packit Service 82fcde
Packit Service 82fcde
    if (slave != -1)
Packit Service 82fcde
      close (slave);
Packit Service 82fcde
  }
Packit Service 82fcde
Packit Service 82fcde
  if (buf != _buf)
Packit Service 82fcde
    free (buf);
Packit Service 82fcde
Packit Service 82fcde
  return ret;
Packit Service 82fcde
}
Packit Service 82fcde
libutil_hidden_def (openpty)