]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/tty_tty.c
xnu-6153.11.26.tar.gz
[apple/xnu.git] / bsd / kern / tty_tty.c
index d6ad89213d71a3b5562852534e6710472e82507c..8cbdfaf62214221ba73496efc41fd84cf5a39420 100644 (file)
@@ -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
  * 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,
  * 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.
 #include <sys/tty.h>
 #include <sys/vnode_internal.h>
 #include <sys/file_internal.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;
-
-#endif /* !NeXT */
+#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, 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_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;
 }
 
-/*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;
+}