From 35eb5cdcb3c8270a68cafad7ff368bfff605bcbe Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 6 Jul 2020 10:54:46 +0200 Subject: [PATCH] openpty: improve implementation and handling of platforms without it Signed-off-by: Christian Brauner --- configure.ac | 6 +- src/include/openpty.c | 172 ++++++++++++++++++++++++++++-------------- src/include/openpty.h | 28 +------ src/lxc/Makefile.am | 14 +++- src/lxc/conf.c | 2 +- src/lxc/terminal.c | 2 +- 6 files changed, 136 insertions(+), 88 deletions(-) diff --git a/configure.ac b/configure.ac index 61db4f2c1..f74d29a72 100644 --- a/configure.ac +++ b/configure.ac @@ -663,7 +663,7 @@ fi AC_CHECK_LIB(pthread, main) AC_CHECK_FUNCS(statvfs) AC_CHECK_LIB(util, openpty) -AC_CHECK_FUNCS([openpty hasmntopt setmntent endmntent utmpxname]) +AC_CHECK_FUNCS([hasmntopt setmntent endmntent utmpxname]) AC_CHECK_FUNCS([getgrgid_r], AM_CONDITIONAL(HAVE_GETGRGID_R, true) AC_DEFINE(HAVE_GETGRGID_R,1,[Have getgrgid_r]), @@ -684,6 +684,10 @@ AC_CHECK_FUNCS([keyctl], AM_CONDITIONAL(HAVE_KEYCTL, true) AC_DEFINE(HAVE_KEYCTL,1,[Have keyctl]), AM_CONDITIONAL(HAVE_KEYCTL, false)) +AC_CHECK_FUNCS([openpty], + AM_CONDITIONAL(HAVE_OPENPTY, true) + AC_DEFINE(HAVE_OPENPTY,1,[Have openpty]), + AM_CONDITIONAL(HAVE_OPENPTY, false)) AC_CHECK_FUNCS([prlimit], AM_CONDITIONAL(HAVE_PRLIMIT, true) AC_DEFINE(HAVE_PRLIMIT,1,[Have prlimit]), diff --git a/src/include/openpty.c b/src/include/openpty.c index 5a1bdbadb..d3cb0344b 100644 --- a/src/include/openpty.c +++ b/src/include/openpty.c @@ -1,76 +1,136 @@ - /* - * openpty: glibc implementation - * - * Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. - * - * Authors: - * Zack Weinberg , 1998. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - - * This library 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define _XOPEN_SOURCE /* See feature_test_macros(7) */ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#define _GNU_SOURCE #include #include #include #include #include +#include +#include #include #include -#include -#include -#define _PATH_DEVPTMX "/dev/ptmx" +#ifdef HAVE_PTY_H +#include +#endif -int openpty (int *aptx, int *apty, char *name, struct termios *termp, - struct winsize *winp) +static int pts_name(int fd, char **pts, size_t buf_len) { - char buf[PATH_MAX]; - int ptx, pty; + int rv; + char *buf = *pts; + + for (;;) { + char *new_buf; + + if (buf_len) { + rv = ptsname_r(fd, buf, buf_len); + + if (rv != 0 || memchr(buf, '\0', buf_len)) + /* We either got an error, or we succeeded and the + returned name fit in the buffer. */ + break; + + /* Try again with a longer buffer. */ + buf_len += buf_len; /* Double it */ + } else + /* No initial buffer; start out by mallocing one. */ + buf_len = 128; /* First time guess. */ + + if (buf != *pts) + /* We've already malloced another buffer at least once. */ + new_buf = realloc(buf, buf_len); + else + new_buf = malloc(buf_len); + if (!new_buf) { + rv = -1; + break; + } + buf = new_buf; + } + + if (rv == 0) + *pts = buf; /* Return buffer to the user. */ + else if (buf != *pts) + free(buf); /* Free what we malloced when returning an error. */ + + return rv; +} + +int __unlockpt(int fd) +{ +#ifdef TIOCSPTLCK + int unlock = 0; + + if (ioctl(fd, TIOCSPTLCK, &unlock)) { + if (errno != EINVAL) + return -1; + } +#endif + return 0; +} + +int openpty(int *ptx, int *pty, char *name, const struct termios *termp, + const struct winsize *winp) +{ + char _buf[PATH_MAX]; + char *buf = _buf; + int ptx_fd, ret = -1, pty_fd = -1; + + *buf = '\0'; + + ptx_fd = open("/dev/ptmx", O_RDWR | O_NOCTTY); + if (ptx_fd == -1) + return -1; + + if (__unlockpt(ptx_fd)) + goto on_error; + +#ifdef TIOCGPTPEER + /* Try to allocate pty_fd solely based on ptx_fd first. */ + pty_fd = ioctl(ptx_fd, TIOCGPTPEER, O_RDWR | O_NOCTTY); +#endif + if (pty_fd == -1) { + /* Fallback to path-based pty_fd allocation in case kernel doesn't + * support TIOCGPTPEER. + */ + if (pts_name(ptx_fd, &buf, sizeof(_buf))) + goto on_error; - ptx = open(_PATH_DEVPTMX, O_RDWR); - if (ptx == -1) - return -1; + pty_fd = open(buf, O_RDWR | O_NOCTTY); + if (pty_fd == -1) + goto on_error; + } - if (grantpt(ptx)) - goto fail; + if (termp) + tcsetattr(pty_fd, TCSAFLUSH, termp); +#ifdef TIOCSWINSZ + if (winp) + ioctl(pty_fd, TIOCSWINSZ, winp); +#endif - if (unlockpt(ptx)) - goto fail; + *ptx = ptx_fd; + *pty = pty_fd; + if (name != NULL) { + if (*buf == '\0') + if (pts_name(ptx_fd, &buf, sizeof(_buf))) + goto on_error; - if (ptyname_r(ptx, buf, sizeof buf)) - goto fail; + strcpy(name, buf); + } - pty = open(buf, O_RDWR | O_NOCTTY); - if (pty == -1) - goto fail; + ret = 0; - /* XXX Should we ignore errors here? */ - if (termp) - tcsetattr(pty, TCSAFLUSH, termp); - if (winp) - ioctl(pty, TIOCSWINSZ, winp); +on_error: + if (ret == -1) { + close(ptx_fd); - *aptx = ptx; - *apty = pty; - if (name != NULL) - strcpy(name, buf); + if (pty_fd != -1) + close(pty_fd); + } - return 0; + if (buf != _buf) + free(buf); -fail: - close(ptx); - return -1; + return ret; } diff --git a/src/include/openpty.h b/src/include/openpty.h index 3b67739ad..8d4699f9a 100644 --- a/src/include/openpty.h +++ b/src/include/openpty.h @@ -1,25 +1,4 @@ -/* - * openpty: glibc implementation - * - * Copyright (C) 1998, 1999, 2004 Free Software Foundation, Inc. - * - * Authors: - * Zack Weinberg , 1998. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - - * This library 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 - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ +/* SPDX-License-Identifier: LGPL-2.1+ */ #ifndef _OPENPTY_H #define _OPENPTY_H @@ -32,8 +11,7 @@ * attributes according to @__termp and @__winp and return handles for both * ends in @__aptx and @__apts. */ -extern int openpty (int *__aptx, int *__apts, char *__name, - const struct termios *__termp, - const struct winsize *__winp); +extern int openpty(int *ptx, int *pty, char *name, const struct termios *termp, + const struct winsize *winp); #endif diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index d1e23647e..473bca5c1 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -54,8 +54,11 @@ noinst_HEADERS = api_extensions.h \ if IS_BIONIC noinst_HEADERS += ../include/fexecve.h \ - ../include/lxcmntent.h \ - ../include/openpty.h + ../include/lxcmntent.h +endif + +if !HAVE_OPENPTY +noinst_HEADERS += ../include/openpty.h endif if !HAVE_PRLIMIT @@ -156,8 +159,11 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \ if IS_BIONIC liblxc_la_SOURCES += ../include/fexecve.c ../include/fexecve.h \ - ../include/lxcmntent.c ../include/lxcmntent.h \ - ../include/openpty.c ../include/openpty.h + ../include/lxcmntent.c ../include/lxcmntent.h +endif + +if !HAVE_OPENPTY +liblxc_la_SOURCES += ../include/openpty.c ../include/openpty.h endif if !HAVE_GETGRGID_R diff --git a/src/lxc/conf.c b/src/lxc/conf.c index e00900642..b26bbfa32 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -69,7 +69,7 @@ #include #endif -#if HAVE_PTY_H +#if HAVE_OPENPTY #include #else #include <../include/openpty.h> diff --git a/src/lxc/terminal.c b/src/lxc/terminal.c index d76a313f5..7d7021897 100644 --- a/src/lxc/terminal.c +++ b/src/lxc/terminal.c @@ -29,7 +29,7 @@ #include "terminal.h" #include "utils.h" -#if HAVE_PTY_H +#if HAVE_OPENPTY #include #else #include <../include/openpty.h> -- 2.47.2