/* Copyright (C) 1998 Free Software Foundation, Inc.
|
/* Copyright (C) 1998 Free Software Foundation, Inc.
|
This file is part of the GNU C Library.
|
This file is part of the GNU C Library.
|
Contributed by Zack Weinberg <zack@rabi.phys.columbia.edu>, 1998.
|
Contributed by Zack Weinberg <zack@rabi.phys.columbia.edu>, 1998.
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
The GNU C Library is free software; you can redistribute it and/or
|
modify it under the terms of the GNU Library General Public License as
|
modify it under the terms of the GNU Library General Public License as
|
published by the Free Software Foundation; either version 2 of the
|
published by the Free Software Foundation; either version 2 of the
|
License, or (at your option) any later version.
|
License, or (at your option) any later version.
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
The GNU C Library is distributed in the hope that it will be useful,
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
Library General Public License for more details.
|
Library General Public License for more details.
|
|
|
You should have received a copy of the GNU Library General Public
|
You should have received a copy of the GNU Library General Public
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
Boston, MA 02111-1307, USA. */
|
Boston, MA 02111-1307, USA. */
|
|
|
#include <assert.h>
|
#include <assert.h>
|
#include <errno.h>
|
#include <errno.h>
|
#include <grp.h>
|
#include <grp.h>
|
#include <limits.h>
|
#include <limits.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <string.h>
|
#include <string.h>
|
#include <sys/resource.h>
|
#include <sys/resource.h>
|
#include <sys/stat.h>
|
#include <sys/stat.h>
|
#include <sys/types.h>
|
#include <sys/types.h>
|
#include <sys/wait.h>
|
#include <sys/wait.h>
|
#include <unistd.h>
|
#include <unistd.h>
|
#include "pty-private.h"
|
#include "pty-private.h"
|
|
|
|
|
/* uClinux-2.0 has vfork, but Linux 2.0 doesn't */
|
/* uClinux-2.0 has vfork, but Linux 2.0 doesn't */
|
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
#if ! defined __NR_vfork
|
#if ! defined __NR_vfork
|
#define vfork fork
|
#define vfork fork
|
#endif
|
#endif
|
|
|
extern int ptsname_r (int fd, char *buf, size_t buflen);
|
extern int ptsname_r (int fd, char *buf, size_t buflen);
|
|
|
/* Return the result of ptsname_r in the buffer pointed to by PTS,
|
/* Return the result of ptsname_r in the buffer pointed to by PTS,
|
which should be of length BUF_LEN. If it is too long to fit in
|
which should be of length BUF_LEN. If it is too long to fit in
|
this buffer, a sufficiently long buffer is allocated using malloc,
|
this buffer, a sufficiently long buffer is allocated using malloc,
|
and returned in PTS. 0 is returned upon success, -1 otherwise. */
|
and returned in PTS. 0 is returned upon success, -1 otherwise. */
|
static int
|
static int
|
pts_name (int fd, char **pts, size_t buf_len)
|
pts_name (int fd, char **pts, size_t buf_len)
|
{
|
{
|
int rv;
|
int rv;
|
char *buf = *pts;
|
char *buf = *pts;
|
|
|
for (;;)
|
for (;;)
|
{
|
{
|
char *new_buf;
|
char *new_buf;
|
|
|
if (buf_len)
|
if (buf_len)
|
{
|
{
|
rv = ptsname_r (fd, buf, buf_len);
|
rv = ptsname_r (fd, buf, buf_len);
|
|
|
if (rv != 0 || memchr (buf, '\0', buf_len))
|
if (rv != 0 || memchr (buf, '\0', buf_len))
|
/* We either got an error, or we succeeded and the
|
/* We either got an error, or we succeeded and the
|
returned name fit in the buffer. */
|
returned name fit in the buffer. */
|
break;
|
break;
|
|
|
/* Try again with a longer buffer. */
|
/* Try again with a longer buffer. */
|
buf_len += buf_len; /* Double it */
|
buf_len += buf_len; /* Double it */
|
}
|
}
|
else
|
else
|
/* No initial buffer; start out by mallocing one. */
|
/* No initial buffer; start out by mallocing one. */
|
buf_len = 128; /* First time guess. */
|
buf_len = 128; /* First time guess. */
|
|
|
if (buf != *pts)
|
if (buf != *pts)
|
/* We've already malloced another buffer at least once. */
|
/* We've already malloced another buffer at least once. */
|
new_buf = realloc (buf, buf_len);
|
new_buf = realloc (buf, buf_len);
|
else
|
else
|
new_buf = malloc (buf_len);
|
new_buf = malloc (buf_len);
|
if (! new_buf)
|
if (! new_buf)
|
{
|
{
|
rv = -1;
|
rv = -1;
|
errno = ENOMEM;
|
errno = ENOMEM;
|
break;
|
break;
|
}
|
}
|
buf = new_buf;
|
buf = new_buf;
|
}
|
}
|
|
|
if (rv == 0)
|
if (rv == 0)
|
*pts = buf; /* Return buffer to the user. */
|
*pts = buf; /* Return buffer to the user. */
|
else if (buf != *pts)
|
else if (buf != *pts)
|
free (buf); /* Free what we malloced when returning an error. */
|
free (buf); /* Free what we malloced when returning an error. */
|
|
|
return rv;
|
return rv;
|
}
|
}
|
|
|
/* Change the ownership and access permission of the slave pseudo
|
/* Change the ownership and access permission of the slave pseudo
|
terminal associated with the master pseudo terminal specified
|
terminal associated with the master pseudo terminal specified
|
by FD. */
|
by FD. */
|
int
|
int
|
grantpt (int fd)
|
grantpt (int fd)
|
{
|
{
|
int retval = -1;
|
int retval = -1;
|
#ifdef PATH_MAX
|
#ifdef PATH_MAX
|
char _buf[PATH_MAX];
|
char _buf[PATH_MAX];
|
#else
|
#else
|
char _buf[512];
|
char _buf[512];
|
#endif
|
#endif
|
char *buf = _buf;
|
char *buf = _buf;
|
struct stat st;
|
struct stat st;
|
uid_t uid;
|
uid_t uid;
|
gid_t gid;
|
gid_t gid;
|
pid_t pid;
|
pid_t pid;
|
|
|
if (pts_name (fd, &buf, sizeof (_buf)))
|
if (pts_name (fd, &buf, sizeof (_buf)))
|
return -1;
|
return -1;
|
|
|
if (stat(buf, &st) < 0)
|
if (stat(buf, &st) < 0)
|
goto cleanup;
|
goto cleanup;
|
|
|
/* Make sure that we own the device. */
|
/* Make sure that we own the device. */
|
uid = getuid ();
|
uid = getuid ();
|
if (st.st_uid != uid)
|
if (st.st_uid != uid)
|
{
|
{
|
if (chown (buf, uid, st.st_gid) < 0)
|
if (chown (buf, uid, st.st_gid) < 0)
|
goto helper;
|
goto helper;
|
}
|
}
|
|
|
gid = getgid ();
|
gid = getgid ();
|
|
|
/* Make sure the group of the device is that special group. */
|
/* Make sure the group of the device is that special group. */
|
if (st.st_gid != gid)
|
if (st.st_gid != gid)
|
{
|
{
|
if (chown (buf, uid, gid) < 0)
|
if (chown (buf, uid, gid) < 0)
|
goto helper;
|
goto helper;
|
}
|
}
|
|
|
/* Make sure the permission mode is set to readable and writable by
|
/* Make sure the permission mode is set to readable and writable by
|
the owner, and writable by the group. */
|
the owner, and writable by the group. */
|
if ((st.st_mode & ACCESSPERMS) != (S_IRUSR|S_IWUSR|S_IWGRP))
|
if ((st.st_mode & ACCESSPERMS) != (S_IRUSR|S_IWUSR|S_IWGRP))
|
{
|
{
|
if (chmod (buf, S_IRUSR|S_IWUSR|S_IWGRP) < 0)
|
if (chmod (buf, S_IRUSR|S_IWUSR|S_IWGRP) < 0)
|
goto helper;
|
goto helper;
|
}
|
}
|
|
|
retval = 0;
|
retval = 0;
|
goto cleanup;
|
goto cleanup;
|
|
|
/* We have to use the helper program. */
|
/* We have to use the helper program. */
|
helper:
|
helper:
|
|
|
pid = vfork ();
|
pid = vfork ();
|
if (pid == -1)
|
if (pid == -1)
|
goto cleanup;
|
goto cleanup;
|
else if (pid == 0)
|
else if (pid == 0)
|
{
|
{
|
/* Disable core dumps. */
|
/* Disable core dumps. */
|
struct rlimit rl = { 0, 0 };
|
struct rlimit rl = { 0, 0 };
|
setrlimit (RLIMIT_CORE, &rl);
|
setrlimit (RLIMIT_CORE, &rl);
|
|
|
/* We pase the master pseudo terminal as file descriptor PTY_FILENO. */
|
/* We pase the master pseudo terminal as file descriptor PTY_FILENO. */
|
if (fd != PTY_FILENO)
|
if (fd != PTY_FILENO)
|
if (dup2 (fd, PTY_FILENO) < 0)
|
if (dup2 (fd, PTY_FILENO) < 0)
|
_exit (FAIL_EBADF);
|
_exit (FAIL_EBADF);
|
|
|
execle (_PATH_PT_CHOWN, _PATH_PT_CHOWN, NULL, NULL);
|
execle (_PATH_PT_CHOWN, _PATH_PT_CHOWN, NULL, NULL);
|
_exit (FAIL_EXEC);
|
_exit (FAIL_EXEC);
|
}
|
}
|
else
|
else
|
{
|
{
|
int w;
|
int w;
|
|
|
if (waitpid (pid, &w, 0) == -1)
|
if (waitpid (pid, &w, 0) == -1)
|
goto cleanup;
|
goto cleanup;
|
if (!WIFEXITED (w))
|
if (!WIFEXITED (w))
|
errno = ENOEXEC;
|
errno = ENOEXEC;
|
else
|
else
|
switch (WEXITSTATUS(w))
|
switch (WEXITSTATUS(w))
|
{
|
{
|
case 0:
|
case 0:
|
retval = 0;
|
retval = 0;
|
break;
|
break;
|
case FAIL_EBADF:
|
case FAIL_EBADF:
|
errno = EBADF;
|
errno = EBADF;
|
break;
|
break;
|
case FAIL_EINVAL:
|
case FAIL_EINVAL:
|
errno = EINVAL;
|
errno = EINVAL;
|
break;
|
break;
|
case FAIL_EACCES:
|
case FAIL_EACCES:
|
errno = EACCES;
|
errno = EACCES;
|
break;
|
break;
|
case FAIL_EXEC:
|
case FAIL_EXEC:
|
errno = ENOEXEC;
|
errno = ENOEXEC;
|
break;
|
break;
|
|
|
default:
|
default:
|
assert(! "getpt: internal error: invalid exit code from pt_chown");
|
assert(! "getpt: internal error: invalid exit code from pt_chown");
|
}
|
}
|
}
|
}
|
|
|
cleanup:
|
cleanup:
|
if (buf != _buf)
|
if (buf != _buf)
|
free (buf);
|
free (buf);
|
|
|
return retval;
|
return retval;
|
}
|
}
|
|
|