From c578a286621a95cbcd28c62f7583d70c9bc908b5 Mon Sep 17 00:00:00 2001 From: Alex Hornung Date: Tue, 1 Sep 2009 14:00:15 +0100 Subject: [PATCH] pty - Introduce unix98 ptys * Add the clone base device, /dev/ptmx. * Add TIOCISPTMASTER ioctl, which returns successfully if the device is a unix98 pty master. * Add sysctl kern.pty_debug, default to 0, to control pty debug info printing. * Remove old unix98 pty code, which was inactive and inherited from an initial implementation during devfs development. * Add userland functions: - posix_openpt (is equivalent to opening /dev/ptmx) - ptsname (as described in standard but thread-safe) - unlockpt (no op) - grantpt (no op) * Implement proper permissions for unix98 pty slaves. Set them to real uid - group "tty" - 0620 for the slave device, as specified in standard (for grantpt). Master's permissions don't matter as the device cannot be opened again. Set the permissions of the unix98 ptys to be overridable by setattr() as the old ptys are. * Use the define UNIX98_PTYS to activate/deactivate the unix98 pty code in tty_pty.c. By default it is enabled. NOTE: due to the permission handling on cloning of the slave pty, grantpt is not needed and would only pose a security risk. --- include/stdlib.h | 8 +- lib/libc/gen/Makefile.inc | 2 +- lib/libc/gen/ptsname.c | 134 +++++++++++++++++++++++++++ sys/kern/tty_pty.c | 186 +++++++++++++++++++++++++++++--------- sys/sys/ttycom.h | 1 + 5 files changed, 283 insertions(+), 48 deletions(-) create mode 100644 lib/libc/gen/ptsname.c diff --git a/include/stdlib.h b/include/stdlib.h index 171498d001..7a137f09b4 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -188,7 +188,7 @@ double erand48(unsigned short[3]); /* char *fcvt(double, int, int * __restrict, int * __restrict); */ /* char *gcvt(double, int, int * __restrict, int * __restrict); */ int getsubopt(char **, char *const *, char **); -/* int grantpt(int); */ +int grantpt(int); char *initstate(unsigned long /* XSI requires u_int */, char *, long); long jrand48(unsigned short[3]); char *l64a(long); @@ -200,8 +200,8 @@ char *mktemp(char *); #endif long mrand48(void); long nrand48(unsigned short[3]); -/* int posix_openpt(int); */ -/* char *ptsname(int); */ +int posix_openpt(int); +char *ptsname(int); int putenv(char *); long random(void); char *realpath(const char *, char resolved_path[]); @@ -211,7 +211,7 @@ int setkey(const char *); char *setstate(/* const */ char *); void srand48(long); void srandom(unsigned long); -/* int unlockpt(int); */ +int unlockpt(int); #endif /* __XSI_VISIBLE */ #if __BSD_VISIBLE diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc index 4a7c246347..edbdabaa64 100644 --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -24,7 +24,7 @@ SRCS+= _pthread_stubs.c _rand48.c _spinlock_stub.c _thread_init.c \ lockf.c lrand48.c mrand48.c msgctl.c \ msgget.c msgrcv.c msgsnd.c nftw.c nice.c \ nlist.c nrand48.c ntp_gettime.c opendir.c \ - pause.c pmadvise.c popen.c posix_spawn.c posixshm.c \ + pause.c pmadvise.c popen.c posix_spawn.c posixshm.c ptsname.c \ psignal.c pw_scan.c pwcache.c \ raise.c readdir.c readpassphrase.c rewinddir.c \ scandir.c seed48.c seekdir.c semconfig.c semctl.c semget.c semop.c \ diff --git a/lib/libc/gen/ptsname.c b/lib/libc/gen/ptsname.c new file mode 100644 index 0000000000..446cdc852b --- /dev/null +++ b/lib/libc/gen/ptsname.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2009 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "namespace.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "reentrant.h" +#include "un-namespace.h" + +#include "libc_private.h" + +#define TTYNAME_DEVFS_COMPAT 1 + +static char ptsname_buf[sizeof(_PATH_DEV) + NAME_MAX]; + +static once_t ptsname_init_once = ONCE_INITIALIZER; +static thread_key_t ptsname_key; +static int ptsname_keycreated = 0; + +static int +__isptmaster(int fd) +{ + int error; + + error = _ioctl(fd, TIOCISPTMASTER); + if ((error) && (errno != EBADF)) + errno = EINVAL; + + return error; +} + +static void +ptsname_keycreate(void) +{ + ptsname_keycreated = (thr_keycreate(&ptsname_key, free) == 0); +} + +char * +ptsname(int fd) +{ + int error; + size_t used; + char *buf; + + error = __isptmaster(fd); + if (error) + return (error); + + if (thr_main() != 0) + buf = ptsname_buf; + else { + if (thr_once(&ptsname_init_once, ptsname_keycreate) != 0 || + !ptsname_keycreated) + return (NULL); + if ((buf = thr_getspecific(ptsname_key)) == NULL) { + if ((buf = malloc(sizeof ptsname_buf)) == NULL) + return (NULL); + if (thr_setspecific(ptsname_key, buf) != 0) { + free(buf); + return (NULL); + } + } + } + + strcpy(buf, "/dev/"); + used = strlen(buf); + + if (((error = fdevname_r(fd, buf+used, sizeof(ptsname_buf)-used))) != 0) { + errno = error; + return (NULL); + } + + buf[used+2] = 's'; + + return (buf); +} + +int +posix_openpt(int oflag) +{ + return open("/dev/ptmx", oflag); +} + +int +unlockpt(int fd) +{ + return __isptmaster(fd); +} + +int +grantpt(int fd) +{ + return __isptmaster(fd); +} \ No newline at end of file diff --git a/sys/kern/tty_pty.c b/sys/kern/tty_pty.c index 9be0253aaa..40f3030540 100644 --- a/sys/kern/tty_pty.c +++ b/sys/kern/tty_pty.c @@ -60,9 +60,10 @@ #include #include #include +#include +#include -DEVFS_DECLARE_CLONE_BITMAP(pty); -DEVFS_DECLARE_CLONE_BITMAP(pts); +#define UNIX98_PTYS 1 MALLOC_DEFINE(M_PTY, "ptys", "pty data structures"); @@ -81,8 +82,13 @@ static d_close_t ptcclose; static d_read_t ptcread; static d_write_t ptcwrite; static d_poll_t ptcpoll; -#if 0 + +#ifdef UNIX98_PTYS +DEVFS_DECLARE_CLONE_BITMAP(pty); + static d_clone_t ptyclone; + +static int pty_debug_level = 0; #endif #define CDEV_MAJOR_S 5 @@ -115,13 +121,13 @@ static struct dev_ops ptc_ops = { struct pt_ioctl { int pt_flags; + int pt_flags2; struct selinfo pt_selr, pt_selw; u_char pt_send; u_char pt_ucntl; struct tty pt_tty; cdev_t devs, devc; struct prison *pt_prison; - short ref_count; }; #define PF_PKT 0x08 /* packet mode */ @@ -130,6 +136,23 @@ struct pt_ioctl { #define PF_NOSTOP 0x40 #define PF_UCNTL 0x80 /* user control mode */ +#define PF_UNIX98 0x01 +#define PF_SOPEN 0x02 +#define PF_MOPEN 0x04 + +static int +ptydebug(int level, char *fmt, ...) +{ + __va_list ap; + + __va_start(ap, fmt); + if (level <= pty_debug_level) + kvprintf(fmt, ap); + __va_end(ap); + + return 0; +} + /* * This function creates and initializes a pts/ptc pair * @@ -164,29 +187,42 @@ ptyinit(int n) ttyregister(&pt->pt_tty); } -#if 0 +#ifdef UNIX98_PTYS static int ptyclone(struct dev_clone_args *ap) { int unit; struct pt_ioctl *pt; - unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(pty), 256); + /* + * Limit the number of unix98 pty (slave) devices to 1000, as + * the utmp(5) format only allows for 8 bytes for the tty, + * "pts/XXX". + * If this limit is reached, we don't clone and return error + * to devfs. + */ + unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(pty), 1000); - if (unit < 0) + if (unit < 0) { + ap->a_dev = NULL; return 1; + } pt = kmalloc(sizeof(*pt), M_PTY, M_WAITOK | M_ZERO); - pt->ref_count++; - pt->devc = ap->a_dev = make_only_dev(&ptc_ops, unit, ap->a_cred->cr_ruid, 0, 0600, "ptm/%d", unit); - pt->devs = make_dev(&pts_ops, unit, ap->a_cred->cr_ruid, 0, 0600, "pts/%d", unit); - //reference_dev(pt->devc); - //reference_dev(pt->devs); + pt->devc = ap->a_dev = make_only_dev(&ptc_ops, unit, ap->a_cred->cr_ruid, + 0, 0600, "ptm/%d", unit); + pt->devs = make_dev(&pts_ops, unit, ap->a_cred->cr_ruid, GID_TTY, 0620, + "pts/%d", unit); + + pt->devs->si_flags |= SI_OVERRIDE; /* uid, gid, perms from dev */ + pt->devc->si_flags |= SI_OVERRIDE; /* uid, gid, perms from dev */ pt->devs->si_drv1 = pt->devc->si_drv1 = pt; pt->devs->si_tty = pt->devc->si_tty = &pt->pt_tty; pt->pt_tty.t_dev = pt->devs; + pt->pt_flags2 |= PF_UNIX98; + ttyregister(&pt->pt_tty); return 0; @@ -233,13 +269,17 @@ ptsopen(struct dev_open_args *ap) if (error == 0) ptcwakeup(tp, FREAD|FWRITE); -#if 0 - /* unix98 pty stuff */ - if ((!error) && (!memcmp(dev->si_name, "pts/", 4))) { - ((struct pt_ioctl *)dev->si_drv1)->ref_count++; - //reference_dev(dev); - //reference_dev(((struct pt_ioctl *)dev->si_drv1)->devc); - //devfs_clone_bitmap_set(&DEVFS_CLONE_BITMAP(pts), dev->si_uminor-300); +#ifdef UNIX98_PTYS + /* + * Unix98 pty stuff. + * On open of the slave, we set the corresponding flag in the common + * struct. + */ + ptydebug(1, "ptsopen=%s | unix98? %s\n", dev->si_name, + (pti->pt_flags2 & PF_UNIX98)?"yes":"no"); + + if ((!error) && (pti->pt_flags2 & PF_UNIX98)) { + pti->pt_flags2 |= PF_SOPEN; } #endif @@ -251,21 +291,36 @@ ptsclose(struct dev_close_args *ap) { cdev_t dev = ap->a_head.a_dev; struct tty *tp; + struct pt_ioctl *pti = dev->si_drv1; int err; -#if 0 - /* unix98 pty stuff */ - if (!memcmp(dev->si_name, "pts/", 4)) { - if (--((struct pt_ioctl *)dev->si_drv1)->ref_count == 0) { - kfree(dev->si_drv1, M_PTY); + + tp = dev->si_tty; + err = (*linesw[tp->t_line].l_close)(tp, ap->a_fflag); + ptsstop(tp, FREAD|FWRITE); + (void) ttyclose(tp); + +#ifdef UNIX98_PTYS + /* + * Unix98 pty stuff. + * On close of the slave, we unset the corresponding flag, and if the master + * isn't open anymore, we destroy the slave and unset the unit. + */ + ptydebug(1, "ptsclose=%s | unix98? %s\n", dev->si_name, + (pti->pt_flags2 & PF_UNIX98)?"yes":"no"); + + if (pti->pt_flags2 & PF_UNIX98) { + pti->pt_flags2 &= ~PF_SOPEN; + KKASSERT((pti->pt_flags2 & PF_SOPEN) == 0); + ptydebug(1, "master open? %s\n", + (pti->pt_flags2 & PF_MOPEN)?"yes":"no"); + + if (!(pti->pt_flags2 & PF_SOPEN) && !(pti->pt_flags2 & PF_MOPEN)) { devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(pty), dev->si_uminor); destroy_dev(dev); } } #endif - tp = dev->si_tty; - err = (*linesw[tp->t_line].l_close)(tp, ap->a_fflag); - ptsstop(tp, FREAD|FWRITE); - (void) ttyclose(tp); + return (err); } @@ -402,6 +457,20 @@ ptcopen(struct dev_open_args *ap) pti->devc->si_gid = 0; pti->devc->si_perms = 0600; +#ifdef UNIX98_PTYS + /* + * Unix98 pty stuff. + * On open of the master, we set the corresponding flag in the common + * struct. + */ + ptydebug(1, "ptcopen=%s (master) | unix98? %s\n", dev->si_name, + (pti->pt_flags2 & PF_UNIX98)?"yes":"no"); + + if (pti->pt_flags2 & PF_UNIX98) { + pti->pt_flags2 |= PF_MOPEN; + } +#endif + return (0); } @@ -410,11 +479,20 @@ ptcclose(struct dev_close_args *ap) { cdev_t dev = ap->a_head.a_dev; struct tty *tp; - struct pt_ioctl *pti; + struct pt_ioctl *pti = dev->si_drv1; tp = dev->si_tty; (void)(*linesw[tp->t_line].l_modem)(tp, 0); +#ifdef UNIX98_PTYS + /* + * Unix98 pty stuff. + * On close of the master, we unset the corresponding flag in the common + * struct asap. + */ + pti->pt_flags2 &= ~PF_MOPEN; +#endif + /* * XXX MDMBUF makes no sense for ptys but would inhibit the above * l_modem(). CLOCAL makes sense but isn't supported. Special @@ -439,17 +517,27 @@ ptcclose(struct dev_close_args *ap) pti->devc->si_gid = 0; pti->devc->si_perms = 0666; -#if 0 - if (!memcmp(dev->si_name, "ptm/", 4)) { - ((struct pt_ioctl *)dev->si_drv1)->devc = NULL; - if (--((struct pt_ioctl *)dev->si_drv1)->ref_count == 0) { - kfree(dev->si_drv1, M_PTY); +#ifdef UNIX98_PTYS + /* + * Unix98 pty stuff. + * On close of the master, we destroy the master and, if no slaves are open, + * we destroy the slave device and unset the unit. + */ + ptydebug(1, "ptcclose=%s (master) | unix98? %s\n", dev->si_name, + (pti->pt_flags2 & PF_UNIX98)?"yes":"no"); + if (pti->pt_flags2 & PF_UNIX98) { + KKASSERT((pti->pt_flags2 & PF_MOPEN) == 0); + destroy_dev(dev); + pti->devc = NULL; + + if (!(pti->pt_flags2 & PF_SOPEN)) { + ptydebug(1, "ptcclose: slaves are not open\n"); + destroy_dev(pti->devs); devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(pty), dev->si_uminor); } - //release_dev(dev); - //release_dev(((struct pt_ioctl *)dev->si_drv1)->devs); } #endif + return (0); } @@ -740,6 +828,12 @@ ptyioctl(struct dev_ioctl_args *ap) pti->pt_flags &= ~PF_REMOTE; ttyflush(tp, FREAD|FWRITE); return (0); + + case TIOCISPTMASTER: + if ((pti->pt_flags2 & PF_UNIX98) && (pti->devc == dev)) + return (0); + else + return (EINVAL); } /* @@ -864,19 +958,25 @@ ptyioctl(struct dev_ioctl_args *ap) static void ptc_drvinit (void *unused); +#ifdef UNIX98_PTYS +SYSCTL_INT(_kern, OID_AUTO, pty_debug, CTLFLAG_RW, &pty_debug_level, + 0, "Change pty debug level"); +#endif + static void ptc_drvinit(void *unused) { int i; - devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(pty)); - devfs_clone_bitmap_init(&DEVFS_CLONE_BITMAP(pts)); - -#if 0 - /* Unix98 pty stuff, leave out for now */ - make_dev(&ptc_ops, 0, 0, 0, 0666, "ptmx"); - devfs_clone_handler_add("ptmx", ptyclone); +#ifdef UNIX98_PTYS + /* + * Unix98 pty stuff. + * Create the clonable base device. + */ + make_autoclone_dev(&ptc_ops, &DEVFS_CLONE_BITMAP(pty), ptyclone, + 0, 0, 0666, "ptmx"); #endif + for (i = 0; i < 256; i++) { ptyinit(i); } diff --git a/sys/sys/ttycom.h b/sys/sys/ttycom.h index 466e209fca..c3913a36a8 100644 --- a/sys/sys/ttycom.h +++ b/sys/sys/ttycom.h @@ -132,6 +132,7 @@ struct winsize { * of last DCd rise */ #define TIOCSDRAINWAIT _IOW('t', 87, int) /* set ttywait timeout */ #define TIOCGDRAINWAIT _IOR('t', 86, int) /* get ttywait timeout */ +#define TIOCISPTMASTER _IO('t', 85) /* is pty master */ #define TTYDISC 0 /* termios tty line discipline */ #define SLIPDISC 4 /* serial IP discipline */ -- 2.41.0