]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/tty_tty.c
xnu-4903.270.47.tar.gz
[apple/xnu.git] / bsd / kern / tty_tty.c
index 29868fadc1a4f1ac9261a32afcb312c4c393e151..8cbdfaf62214221ba73496efc41fd84cf5a39420 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 1997-2006 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
  * 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,7 +22,7 @@
  * 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@
  */
 /*-
@@ -82,25 +82,83 @@ 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(__unused dev_t dev, int flag, __unused int mode, proc_t p)
+cttyopen(dev_t dev, int flag, __unused int mode, proc_t 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_thread = current_thread();
        context.vc_ucred = kauth_cred_proc_ref(p);
 
-       error = VNOP_OPEN(ttyvp, flag, &context);
+       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);
+
        vnode_put(ttyvp);
        kauth_cred_unref(&context.vc_ucred);
 
-       return (error);
+       return error;
 }
 
 int
@@ -110,8 +168,9 @@ cttyread(__unused dev_t dev, struct uio *uio, int flag)
        struct vfs_context context;
        int error;
 
-       if (ttyvp == NULL)
-               return (EIO);
+       if (ttyvp == NULL) {
+               return EIO;
+       }
 
        context.vc_thread = current_thread();
        context.vc_ucred = NOCRED;
@@ -119,7 +178,7 @@ cttyread(__unused dev_t dev, struct uio *uio, int flag)
        error = VNOP_READ(ttyvp, uio, flag, &context);
        vnode_put(ttyvp);
 
-       return (error);
+       return error;
 }
 
 int
@@ -129,8 +188,9 @@ cttywrite(__unused dev_t dev, struct uio *uio, int flag)
        struct vfs_context context;
        int error;
 
-       if (ttyvp == NULL)
-               return (EIO);
+       if (ttyvp == NULL) {
+               return EIO;
+       }
 
        context.vc_thread = current_thread();
        context.vc_ucred = NOCRED;
@@ -138,7 +198,7 @@ cttywrite(__unused dev_t dev, struct uio *uio, int flag)
        error = VNOP_WRITE(ttyvp, uio, flag, &context);
        vnode_put(ttyvp);
 
-       return (error);
+       return error;
 }
 
 int
@@ -149,9 +209,10 @@ cttyioctl(__unused dev_t dev, u_long cmd, caddr_t addr, int flag, proc_t p)
        struct session *sessp;
        int error = 0;
 
-       if (ttyvp == NULL)
-               return (EIO);
-       if (cmd == TIOCSCTTY)  { /* don't allow controlling tty to be set    */
+       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;
        }
@@ -159,13 +220,15 @@ cttyioctl(__unused dev_t dev, u_long cmd, caddr_t addr, int flag, proc_t p)
                sessp = proc_session(p);
                if (!SESS_LEADER(p, sessp)) {
                        OSBitAndAtomic(~((uint32_t)P_CONTROLT), &p->p_flag);
-                       if (sessp != SESSION_NULL)
+                       if (sessp != SESSION_NULL) {
                                session_rele(sessp);
+                       }
                        error = 0;
                        goto out;
                } else {
-                       if (sessp != SESSION_NULL)
+                       if (sessp != SESSION_NULL) {
                                session_rele(sessp);
+                       }
                        error = EINVAL;
                        goto out;
                }
@@ -176,7 +239,7 @@ cttyioctl(__unused dev_t dev, u_long cmd, caddr_t addr, int flag, proc_t p)
        error = VNOP_IOCTL(ttyvp, cmd, addr, flag, &context);
 out:
        vnode_put(ttyvp);
-       return (error);
+       return error;
 }
 
 int
@@ -189,11 +252,12 @@ cttyselect(__unused dev_t dev, int flag, void* wql, __unused proc_t p)
        context.vc_thread = current_thread();
        context.vc_ucred = NOCRED;
 
-       if (ttyvp == NULL)
-               return (1);     /* try operation to get EOF/failure */
-       error = 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);
+       return error;
 }
 
 /* This returns vnode with ioref */
@@ -208,16 +272,16 @@ cttyvp(proc_t p)
 
        session_lock(sessp);
        vp = (p->p_flag & P_CONTROLT ? sessp->s_ttyvp : NULLVP);
-       vid = sessp->s_ttyvid;  
+       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)
+               if (vnode_getwithvid(vp, vid) != 0) {
                        vp = NULLVP;
+               }
        }
-       return(vp);
+       return vp;
 }
-