X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/8f6c56a50524aa785f7e596d52dddfb331e18961..cb3231590a3c94ab4375e2228bd5e86b0cf1ad7e:/bsd/kern/tty_tty.c diff --git a/bsd/kern/tty_tty.c b/bsd/kern/tty_tty.c index fac3c37cd..8cbdfaf62 100644 --- a/bsd/kern/tty_tty.c +++ b/bsd/kern/tty_tty.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1997-2013 Apple Computer, Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. - * + * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,10 +22,9 @@ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ -/* Copyright (c) 1997 Apple Computer, Inc. All Rights Reserved */ /*- * Copyright (c) 1982, 1986, 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -72,173 +71,217 @@ #include #include #include -#ifndef NeXT -#include -#ifdef DEVFS -#include -#endif /*DEVFS*/ - -static d_open_t cttyopen; -static d_read_t cttyread; -static d_write_t cttywrite; -static d_ioctl_t cttyioctl; -static d_select_t cttyselect; - -#endif /* !NeXT */ +#include /* Forward declarations for cdevsw[] entry */ /* XXX we should consider making these static */ -int cttyopen(dev_t dev, int flag, int mode, struct proc *p); +int cttyopen(dev_t dev, int flag, int mode, proc_t p); int cttyread(dev_t dev, struct uio *uio, int flag); int cttywrite(dev_t dev, struct uio *uio, int flag); -int cttyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p); -int cttyselect(dev_t dev, int flag, void* wql, struct proc *p); - -#ifndef NeXT +int cttyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, proc_t p); +int cttyselect(dev_t dev, int flag, void* wql, proc_t p); +static vnode_t cttyvp(proc_t p); -#define CDEV_MAJOR 1 -/* Don't make static, fdesc_vnops uses this. */ -struct cdevsw ctty_cdevsw = - { cttyopen, nullclose, cttyread, cttywrite, /*1*/ - cttyioctl, nullstop, nullreset, nodevtotty,/* tty */ - cttyselect, nommap, NULL, "ctty", NULL, -1 }; - -#endif /* !NeXT */ - -#define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL) - -/*ARGSUSED*/ int -cttyopen(__unused dev_t dev, int flag, __unused int mode, struct proc *p) +cttyopen(dev_t dev, int flag, __unused int mode, proc_t p) { - struct vnode *ttyvp = cttyvp(p); + vnode_t ttyvp = cttyvp(p); struct vfs_context context; - int error; + int error = 0; + int cttyflag, doclose = 0; + struct session *sessp; - if (ttyvp == NULL) - return (ENXIO); + if (ttyvp == NULL) { + return ENXIO; + } - context.vc_proc = p; - context.vc_ucred = p->p_ucred; - error = VNOP_OPEN(ttyvp, flag, &context); + context.vc_thread = current_thread(); + context.vc_ucred = kauth_cred_proc_ref(p); + + sessp = proc_session(p); + session_lock(sessp); + cttyflag = sessp->s_flags & S_CTTYREF; + session_unlock(sessp); + + /* + * A little hack--this device, used by many processes, + * happens to do an open on another device, which can + * cause unhappiness if the second-level open blocks indefinitely + * (as could be the case if the master side has hung up). Since + * we know that this driver doesn't care about the serializing + * opens and closes, we can drop the lock. To avoid opencount leak, + * open the vnode only for the first time. + */ + if (cttyflag == 0) { + devsw_unlock(dev, S_IFCHR); + error = VNOP_OPEN(ttyvp, flag, &context); + devsw_lock(dev, S_IFCHR); + + if (error) { + goto out; + } + + /* + * If S_CTTYREF is set, some other thread did an open + * and was able to set the flag, now perform a close, else + * set the flag. + */ + session_lock(sessp); + if (cttyflag == (sessp->s_flags & S_CTTYREF)) { + sessp->s_flags |= S_CTTYREF; + } else { + doclose = 1; + } + session_unlock(sessp); + + /* + * We have to take a reference here to make sure a close + * gets called during revoke. Note that once a controlling + * tty gets opened by this driver, the only way close will + * get called is when the session leader , whose controlling + * tty is ttyvp, exits and vnode is revoked. We cannot + * redirect close from this driver because underlying controlling + * terminal might change and close may get redirected to a + * wrong vnode causing panic. + */ + if (doclose) { + devsw_unlock(dev, S_IFCHR); + VNOP_CLOSE(ttyvp, flag, &context); + devsw_lock(dev, S_IFCHR); + } else { + error = vnode_ref(ttyvp); + } + } +out: + session_rele(sessp); - return (error); + vnode_put(ttyvp); + kauth_cred_unref(&context.vc_ucred); + + return error; } -/*ARGSUSED*/ int cttyread(__unused dev_t dev, struct uio *uio, int flag) { - struct proc *p = current_proc(); - register struct vnode *ttyvp = cttyvp(p); + vnode_t ttyvp = cttyvp(current_proc()); struct vfs_context context; int error; - if (ttyvp == NULL) - return (EIO); + if (ttyvp == NULL) { + return EIO; + } - context.vc_proc = p; + context.vc_thread = current_thread(); context.vc_ucred = NOCRED; error = VNOP_READ(ttyvp, uio, flag, &context); + vnode_put(ttyvp); - return (error); + return error; } -/*ARGSUSED*/ int cttywrite(__unused dev_t dev, struct uio *uio, int flag) { - struct proc *p = current_proc(); - register struct vnode *ttyvp = cttyvp(p); + vnode_t ttyvp = cttyvp(current_proc()); struct vfs_context context; int error; - if (ttyvp == NULL) - return (EIO); + if (ttyvp == NULL) { + return EIO; + } - context.vc_proc = p; + context.vc_thread = current_thread(); context.vc_ucred = NOCRED; error = VNOP_WRITE(ttyvp, uio, flag, &context); + vnode_put(ttyvp); - return (error); + return error; } -/*ARGSUSED*/ -#ifndef NeXT -static int -cttyioctl(dev, cmd, addr, flag, p) - dev_t dev; - int cmd; - caddr_t addr; - int flag; - struct proc *p; -#else int -cttyioctl(__unused dev_t dev, u_long cmd, caddr_t addr, int flag, - struct proc *p) -#endif /* !NeXT */ +cttyioctl(__unused dev_t dev, u_long cmd, caddr_t addr, int flag, proc_t p) { - struct vnode *ttyvp = cttyvp(p); + vnode_t ttyvp = cttyvp(current_proc()); struct vfs_context context; + struct session *sessp; + int error = 0; - if (ttyvp == NULL) - return (EIO); - if (cmd == TIOCSCTTY) /* don't allow controlling tty to be set */ - return EINVAL; /* to controlling tty -- infinite recursion */ + if (ttyvp == NULL) { + return EIO; + } + if (cmd == TIOCSCTTY) { /* don't allow controlling tty to be set */ + error = EINVAL; /* to controlling tty -- infinite recursion */ + goto out; + } if (cmd == TIOCNOTTY) { - if (!SESS_LEADER(p)) { - p->p_flag &= ~P_CONTROLT; - return (0); - } else - return (EINVAL); + sessp = proc_session(p); + if (!SESS_LEADER(p, sessp)) { + OSBitAndAtomic(~((uint32_t)P_CONTROLT), &p->p_flag); + if (sessp != SESSION_NULL) { + session_rele(sessp); + } + error = 0; + goto out; + } else { + if (sessp != SESSION_NULL) { + session_rele(sessp); + } + error = EINVAL; + goto out; + } } - context.vc_proc = p; + context.vc_thread = current_thread(); context.vc_ucred = NOCRED; - return (VNOP_IOCTL(ttyvp, cmd, addr, flag, &context)); + error = VNOP_IOCTL(ttyvp, cmd, addr, flag, &context); +out: + vnode_put(ttyvp); + return error; } -/*ARGSUSED*/ int -cttyselect(__unused dev_t dev, int flag, void* wql, struct proc *p) +cttyselect(__unused dev_t dev, int flag, void* wql, __unused proc_t p) { - struct vnode *ttyvp = cttyvp(p); + vnode_t ttyvp = cttyvp(current_proc()); struct vfs_context context; + int error; - context.vc_proc = p; + context.vc_thread = current_thread(); context.vc_ucred = NOCRED; - if (ttyvp == NULL) - return (1); /* try operation to get EOF/failure */ - return (VNOP_SELECT(ttyvp, flag, FREAD|FWRITE, wql, &context)); + if (ttyvp == NULL) { + return 1; /* try operation to get EOF/failure */ + } + error = VNOP_SELECT(ttyvp, flag, FREAD | FWRITE, wql, &context); + vnode_put(ttyvp); + return error; } -#ifndef NeXT -static ctty_devsw_installed = 0; -#ifdef DEVFS -static void *ctty_devfs_token; -#endif - -static void -ctty_drvinit(void *unused) +/* This returns vnode with ioref */ +static vnode_t +cttyvp(proc_t p) { - dev_t dev; - - if( ! ctty_devsw_installed ) { - dev = makedev(CDEV_MAJOR,0); - cdevsw_add(&dev,&ctty_cdevsw,NULL); - ctty_devsw_installed = 1; -#ifdef DEVFS - ctty_devfs_token = - devfs_add_devswf(&ctty_cdevsw, 0, DV_CHR, 0, 0, - 0666, "tty"); -#endif - } -} + vnode_t vp; + int vid; + struct session *sessp; -SYSINIT(cttydev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ctty_drvinit,NULL) + sessp = proc_session(p); + session_lock(sessp); + vp = (p->p_flag & P_CONTROLT ? sessp->s_ttyvp : NULLVP); + vid = sessp->s_ttyvid; + session_unlock(sessp); -#endif /* !NeXT */ + session_rele(sessp); + + if (vp != NULLVP) { + /* cannot get an IO reference, return NULLVP */ + if (vnode_getwithvid(vp, vid) != 0) { + vp = NULLVP; + } + } + return vp; +}