]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/tty_tty.c
xnu-3247.10.11.tar.gz
[apple/xnu.git] / bsd / kern / tty_tty.c
index 608db3a28c0797a562ebec9880ffdcfe69461b5a..84960728d5a22739e99d7d089113688bc955556f 100644 (file)
@@ -1,16 +1,19 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1997-2013 Apple Computer, Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * Copyright (c) 1999-2003 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
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * 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
@@ -20,9 +23,8 @@
  * Please see the License for the specific language governing rights and
  * limitations under the License.
  * 
- * @APPLE_LICENSE_HEADER_END@
+ * @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.
 #include <sys/systm.h>
 #include <sys/conf.h>
 #include <sys/ioctl.h>
-#include <sys/proc.h>
+#include <sys/proc_internal.h>
 #include <sys/tty.h>
-#include <sys/vnode.h>
-#include <sys/file.h>
-#ifndef NeXT
-#include <sys/kernel.h>
-#ifdef DEVFS
-#include <sys/devfsext.h>
-#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;
-
-#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*/
+#include <sys/vnode_internal.h>
+#include <sys/file_internal.h>
+#include <sys/kauth.h>
+
+/* Forward declarations for cdevsw[] entry */
+/* XXX we should consider making these static */
+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, proc_t p);
+int cttyselect(dev_t dev, int flag, void* wql, proc_t p);
+static vnode_t cttyvp(proc_t p);
+
 int
-cttyopen(dev, flag, mode, p)
-       dev_t dev;
-       int flag, mode;
-       struct proc *p;
+cttyopen(dev_t dev, int flag, __unused int mode, proc_t p)
 {
-       struct vnode *ttyvp = cttyvp(p);
-       int error;
+       vnode_t ttyvp = cttyvp(p);
+       struct vfs_context context;
+       int error = 0;
+       int cttyflag, doclose = 0;
+       struct session *sessp;
 
        if (ttyvp == NULL)
                return (ENXIO);
-#ifndef NeXT
-       VOP_LOCK(ttyvp);
-#else
-       /*
-        * This is the only place that NeXT Guarding has been used for
-        * VOP_.*LOCK style calls.  Note all of the other diffs should
-        * use the three paramater lock/unlock.
-        */
-       vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY, p);
-#endif
 
-#ifdef PARANOID
+       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);
+
        /*
-        * Since group is tty and mode is 620 on most terminal lines
-        * and since sessions protect terminals from processes outside
-        * your session, this check is probably no longer necessary.
-        * Since it inhibits setuid root programs that later switch
-        * to another user from accessing /dev/tty, we have decided
-        * to delete this test. (mckusick 5/93)
+        * 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. 
         */
-       error = VOP_ACCESS(ttyvp,
-         (flag&FREAD ? VREAD : 0) | (flag&FWRITE ? VWRITE : 0), p->p_ucred, p);
-       if (!error)
-#endif /* PARANOID */
-               error = VOP_OPEN(ttyvp, flag, NOCRED, p);
-       VOP_UNLOCK(ttyvp, 0, p);
+       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);
+
+       vnode_put(ttyvp);
+       kauth_cred_unref(&context.vc_ucred);
+
        return (error);
 }
 
-/*ARGSUSED*/
 int
-cttyread(dev, uio, flag)
-       dev_t dev;
-       struct uio *uio;
-       int flag;
+cttyread(__unused dev_t dev, struct uio *uio, int flag)
 {
-       struct proc *p = uio->uio_procp;
-       register struct vnode *ttyvp = cttyvp(uio->uio_procp);
+       vnode_t ttyvp = cttyvp(current_proc());
+       struct vfs_context context;
        int error;
 
        if (ttyvp == NULL)
                return (EIO);
-       vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY, p);
-       error = VOP_READ(ttyvp, uio, flag, NOCRED);
-       VOP_UNLOCK(ttyvp, 0, p);
+
+       context.vc_thread = current_thread();
+       context.vc_ucred = NOCRED;
+
+       error = VNOP_READ(ttyvp, uio, flag, &context);
+       vnode_put(ttyvp);
+
        return (error);
 }
 
-/*ARGSUSED*/
 int
-cttywrite(dev, uio, flag)
-       dev_t dev;
-       struct uio *uio;
-       int flag;
+cttywrite(__unused dev_t dev, struct uio *uio, int flag)
 {
-       struct proc *p = uio->uio_procp;
-       register struct vnode *ttyvp = cttyvp(uio->uio_procp);
+       vnode_t ttyvp = cttyvp(current_proc());
+       struct vfs_context context;
        int error;
 
        if (ttyvp == NULL)
                return (EIO);
-       vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY, p);
-       error = VOP_WRITE(ttyvp, uio, flag, NOCRED);
-       VOP_UNLOCK(ttyvp, 0, p);
+
+       context.vc_thread = current_thread();
+       context.vc_ucred = NOCRED;
+
+       error = VNOP_WRITE(ttyvp, uio, flag, &context);
+       vnode_put(ttyvp);
+
        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(dev, cmd, addr, flag, p)
-       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 (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;
+               }
        }
-       return (VOP_IOCTL(ttyvp, cmd, addr, flag, NOCRED, p));
+       context.vc_thread = current_thread();
+       context.vc_ucred = NOCRED;
+
+       error = VNOP_IOCTL(ttyvp, cmd, addr, flag, &context);
+out:
+       vnode_put(ttyvp);
+       return (error);
 }
 
-/*ARGSUSED*/
 int
-cttyselect(dev, flag, wql, p)
-       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_thread = current_thread();
+       context.vc_ucred = NOCRED;
 
        if (ttyvp == NULL)
                return (1);     /* try operation to get EOF/failure */
-       return (VOP_SELECT(ttyvp, flag, FREAD|FWRITE, NOCRED, wql, p));
+       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);
+
+       session_rele(sessp);
+
+       if (vp != NULLVP) {
+               /* cannot get an IO reference, return NULLVP */
+               if (vnode_getwithvid(vp, vid) != 0)
+                       vp = NULLVP;
+       }
+       return(vp);
+}
 
-#endif /* !NeXT */